From 17cd581968f75b29e0bcc7a78362801c6156d115 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 16 Aug 2023 12:58:20 +0200 Subject: [PATCH 01/95] Clean up test resource loading --- .../ext/flex/FlexIntegrationTest.java | 29 +--- .../opentripplanner/ext/flex/FlexTest.java | 10 +- .../opentripplanner/ConstantsForTests.java | 12 +- .../graph_builder/module/FakeGraph.java | 16 +- .../module/osm/OpenStreetMapParserTest.java | 5 +- .../module/osm/OsmModuleTest.java | 23 +-- .../module/osm/PlatformLinkerTest.java | 11 +- .../module/osm/TestIntermediatePlaces.java | 2 +- .../module/osm/TriangleInequalityTest.java | 10 +- .../module/osm/UnconnectedAreasTest.java | 18 ++- .../module/osm/UnroutableTest.java | 8 +- .../module/osm/WalkableAreaBuilderTest.java | 3 +- .../graph/DefaultRoutingServiceTest.java | 2 +- .../stoptimes/AlternativeLegsTest.java | 6 +- .../test/support/ResourceLoader.java | 23 +++ src/test/resources/generateGtfs.py | 149 ------------------ src/test/resources/generated.gtfs.zip | Bin 46498 -> 0 bytes .../addPerpendicularRoutes.gtfs.zip | Bin .../{ => gtfs}/testagency/agency.txt | 0 .../{ => gtfs}/testagency/calendar.txt | 0 .../{ => gtfs}/testagency/frequencies.txt | 0 .../{ => gtfs}/testagency/pathways.txt | 0 .../{ => gtfs}/testagency/routes.txt | 0 .../{ => gtfs}/testagency/shapes.txt | 0 .../{ => gtfs}/testagency/stop_times.txt | 0 .../resources/{ => gtfs}/testagency/stops.txt | 0 .../{ => gtfs}/testagency/transfers.txt | 0 .../resources/{ => gtfs}/testagency/trips.txt | 0 .../graph_builder/module => }/osm/B+R.osm.pbf | Bin .../module => }/osm/NYC_small.osm.pbf | Bin .../graph_builder/module => }/osm/P+R.osm.pbf | Bin .../osm/bridge_construction.osm.pbf | Bin .../module => }/osm/coincident_pr.osm.pbf | Bin .../osm/coincident_pr_dupl.osm.pbf | Bin .../osm/coincident_pr_overlap.osm.pbf | Bin .../osm/coincident_pr_reverse.osm.pbf | Bin .../module => osm}/columbus.osm.pbf | Bin .../module => }/osm/hackett_pr.osm.pbf | Bin src/test/resources/{ => osm}/isoiiluoto.pbf | Bin .../osm/lund-station-sweden.osm.pbf | Bin .../graph_builder/module => }/osm/map.osm.pbf | Bin .../{ => osm}/oslo-east-filtered.osm.pbf | Bin .../osm/otp-multipolygon-test.osm.pbf | Bin .../module => }/osm/skoyen.osm.pbf | Bin .../module => }/osm/stopareas.pbf | Bin .../module => }/osm/usf_area.osm.pbf | Bin .../raptor/speedtest/norway/README.md | 12 -- src/test/resources/testagency.zip | Bin 3453 -> 0 bytes 48 files changed, 73 insertions(+), 266 deletions(-) create mode 100644 src/test/java/org/opentripplanner/test/support/ResourceLoader.java delete mode 100755 src/test/resources/generateGtfs.py delete mode 100644 src/test/resources/generated.gtfs.zip rename src/test/resources/{org/opentripplanner/graph_builder/module => gtfs}/addPerpendicularRoutes.gtfs.zip (100%) rename src/test/resources/{ => gtfs}/testagency/agency.txt (100%) rename src/test/resources/{ => gtfs}/testagency/calendar.txt (100%) rename src/test/resources/{ => gtfs}/testagency/frequencies.txt (100%) rename src/test/resources/{ => gtfs}/testagency/pathways.txt (100%) rename src/test/resources/{ => gtfs}/testagency/routes.txt (100%) rename src/test/resources/{ => gtfs}/testagency/shapes.txt (100%) rename src/test/resources/{ => gtfs}/testagency/stop_times.txt (100%) rename src/test/resources/{ => gtfs}/testagency/stops.txt (100%) rename src/test/resources/{ => gtfs}/testagency/transfers.txt (100%) rename src/test/resources/{ => gtfs}/testagency/trips.txt (100%) rename src/test/resources/{org/opentripplanner/graph_builder/module => }/osm/B+R.osm.pbf (100%) rename src/test/resources/{org/opentripplanner/graph_builder/module => }/osm/NYC_small.osm.pbf (100%) rename src/test/resources/{org/opentripplanner/graph_builder/module => }/osm/P+R.osm.pbf (100%) rename src/test/resources/{org/opentripplanner/graph_builder/module => }/osm/bridge_construction.osm.pbf (100%) rename src/test/resources/{org/opentripplanner/graph_builder/module => }/osm/coincident_pr.osm.pbf (100%) rename src/test/resources/{org/opentripplanner/graph_builder/module => }/osm/coincident_pr_dupl.osm.pbf (100%) rename src/test/resources/{org/opentripplanner/graph_builder/module => }/osm/coincident_pr_overlap.osm.pbf (100%) rename src/test/resources/{org/opentripplanner/graph_builder/module => }/osm/coincident_pr_reverse.osm.pbf (100%) rename src/test/resources/{org/opentripplanner/graph_builder/module => osm}/columbus.osm.pbf (100%) rename src/test/resources/{org/opentripplanner/graph_builder/module => }/osm/hackett_pr.osm.pbf (100%) rename src/test/resources/{ => osm}/isoiiluoto.pbf (100%) rename src/test/resources/{org/opentripplanner/graph_builder/module => }/osm/lund-station-sweden.osm.pbf (100%) rename src/test/resources/{org/opentripplanner/graph_builder/module => }/osm/map.osm.pbf (100%) rename src/test/resources/{ => osm}/oslo-east-filtered.osm.pbf (100%) rename src/test/resources/{org/opentripplanner/graph_builder/module => }/osm/otp-multipolygon-test.osm.pbf (100%) rename src/test/resources/{org/opentripplanner/graph_builder/module => }/osm/skoyen.osm.pbf (100%) rename src/test/resources/{org/opentripplanner/graph_builder/module => }/osm/stopareas.pbf (100%) rename src/test/resources/{org/opentripplanner/graph_builder/module => }/osm/usf_area.osm.pbf (100%) delete mode 100644 src/test/resources/raptor/speedtest/norway/README.md delete mode 100644 src/test/resources/testagency.zip diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/FlexIntegrationTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/FlexIntegrationTest.java index daf912a420a..482906a58a9 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/FlexIntegrationTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/FlexIntegrationTest.java @@ -3,13 +3,11 @@ 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.graph_builder.module.FakeGraph.getFileForResource; import static org.opentripplanner.routing.api.request.StreetMode.FLEXIBLE; import static org.opentripplanner.street.search.TraverseMode.WALK; import static org.opentripplanner.transit.model.basic.TransitMode.BUS; import java.io.File; -import java.net.URISyntaxException; import java.time.Duration; import java.time.Instant; import java.time.ZonedDateTime; @@ -36,6 +34,7 @@ import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.routing.api.request.request.filter.AllowAllTransitFilter; import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.test.support.ResourceLoader; import org.opentripplanner.transit.service.TransitModel; /** @@ -58,12 +57,12 @@ public class FlexIntegrationTest { @BeforeAll static void setup() { OTPFeature.enableFeatures(Map.of(OTPFeature.FlexRouting, true)); - var osmPath = getAbsolutePath(FlexTest.COBB_OSM); - var cobblincGtfsPath = getAbsolutePath(FlexTest.COBB_BUS_30_GTFS); - var martaGtfsPath = getAbsolutePath(FlexTest.MARTA_BUS_856_GTFS); - var flexGtfsPath = getAbsolutePath(FlexTest.COBB_FLEX_GTFS); + var osmPath = ResourceLoader.file(FlexTest.COBB_OSM); + var cobblincGtfsPath = ResourceLoader.file(FlexTest.COBB_BUS_30_GTFS); + var martaGtfsPath = ResourceLoader.file(FlexTest.MARTA_BUS_856_GTFS); + var flexGtfsPath = ResourceLoader.file(FlexTest.COBB_FLEX_GTFS); - TestOtpModel model = ConstantsForTests.buildOsmGraph(osmPath); + TestOtpModel model = ConstantsForTests.buildOsmGraph(osmPath.getAbsolutePath()); graph = model.graph(); transitModel = model.transitModel(); @@ -176,21 +175,9 @@ static void teardown() { OTPFeature.enableFeatures(Map.of(OTPFeature.FlexRouting, false)); } - private static String getAbsolutePath(String cobbOsm) { - try { - return getFileForResource(cobbOsm).getAbsolutePath(); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - } - - private static void addGtfsToGraph( - Graph graph, - TransitModel transitModel, - List gtfsFiles - ) { + private static void addGtfsToGraph(Graph graph, TransitModel transitModel, List gtfsFiles) { // GTFS - var gtfsBundles = gtfsFiles.stream().map(f -> new GtfsBundle(new File(f))).toList(); + var gtfsBundles = gtfsFiles.stream().map(GtfsBundle::new).toList(); GtfsModule gtfsModule = new GtfsModule( gtfsBundles, transitModel, diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/FlexTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/FlexTest.java index f2cb72ed3da..2170821a21e 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/FlexTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/FlexTest.java @@ -4,7 +4,6 @@ import gnu.trove.set.hash.TIntHashSet; import java.io.File; -import java.net.URISyntaxException; import java.time.LocalDate; import java.time.LocalTime; import java.util.List; @@ -12,11 +11,11 @@ import org.opentripplanner.TestOtpModel; import org.opentripplanner.ext.flex.flexpathcalculator.DirectFlexPathCalculator; import org.opentripplanner.framework.application.OTPFeature; -import org.opentripplanner.graph_builder.module.FakeGraph; import org.opentripplanner.gtfs.graphbuilder.GtfsBundle; import org.opentripplanner.gtfs.graphbuilder.GtfsModule; import org.opentripplanner.model.calendar.ServiceDateInterval; import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.test.support.ResourceLoader; import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.service.StopModel; import org.opentripplanner.transit.service.TransitModel; @@ -40,12 +39,7 @@ public abstract class FlexTest { ); protected static TestOtpModel buildFlexGraph(String fileName) { - File file = null; - try { - file = FakeGraph.getFileForResource(fileName); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } + File file = ResourceLoader.file(fileName); var deduplicator = new Deduplicator(); var graph = new Graph(deduplicator); diff --git a/src/test/java/org/opentripplanner/ConstantsForTests.java b/src/test/java/org/opentripplanner/ConstantsForTests.java index 6e719b487c5..7c39cd55b71 100644 --- a/src/test/java/org/opentripplanner/ConstantsForTests.java +++ b/src/test/java/org/opentripplanner/ConstantsForTests.java @@ -48,9 +48,7 @@ public class ConstantsForTests { public static final String CALTRAIN_GTFS = "src/test/resources/gtfs/caltrain_gtfs.zip"; - public static final String NETEX_MINIMAL = "src/test/resources/netex/netex_minimal.zip"; - - private static final String PORTLAND_GTFS = "src/test/resources/portland/portland.gtfs.zip"; + static final String PORTLAND_GTFS = "src/test/resources/portland/portland.gtfs.zip"; public static final String PORTLAND_CENTRAL_OSM = "src/test/resources/portland/portland-central-filtered.osm.pbf"; @@ -58,16 +56,14 @@ public class ConstantsForTests { private static final String PORTLAND_BIKE_SHARE_CSV = "src/test/resources/portland/portland-vehicle-rental.csv"; - private static final String PORTLAND_NED = "src/test/resources/portland/portland-ned.tif"; - private static final String PORTLAND_NED_WITH_NODATA = "src/test/resources/portland/portland-ned-nodata.tif"; - private static final String OSLO_EAST_OSM = "src/test/resources/oslo-east-filtered.osm.pbf"; + private static final String OSLO_EAST_OSM = "src/test/resources/osm/oslo-east-filtered.osm.pbf"; public static final String KCM_GTFS = "src/test/resources/gtfs/kcm_gtfs.zip"; - public static final String FAKE_GTFS = "src/test/resources/testagency"; + public static final String FAKE_GTFS = "src/test/resources/gtfs/testagency/"; public static final String FARE_COMPONENT_GTFS = "src/test/resources/gtfs/farecomponents.gtfs.zip"; @@ -96,7 +92,7 @@ public class ConstantsForTests { "src/test/resources/germany/herrenberg-minimal.osm.pbf"; public static final String ISLAND_PRUNE_OSM = "src/test/resources/germany/herrenberg-island-prune-nothru.osm.pbf"; - public static final String ADAPTIVE_PRUNE_OSM = "src/test/resources/isoiiluoto.pbf"; + public static final String ADAPTIVE_PRUNE_OSM = "src/test/resources/osm/isoiiluoto.pbf"; /* filenames encoded with cp437 and utf8 */ public static final String UMLAUT_CP437_ZIP = "src/test/resources/umlaut-cp437.zip"; diff --git a/src/test/java/org/opentripplanner/graph_builder/module/FakeGraph.java b/src/test/java/org/opentripplanner/graph_builder/module/FakeGraph.java index b4dfb63d203..160a6e9c87e 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/FakeGraph.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/FakeGraph.java @@ -2,7 +2,6 @@ import java.io.File; import java.net.URISyntaxException; -import java.net.URL; import java.util.List; import org.opentripplanner.TestOtpModel; import org.opentripplanner.graph_builder.module.osm.OsmModule; @@ -18,6 +17,7 @@ import org.opentripplanner.street.model.vertex.TransitStopVertexBuilder; import org.opentripplanner.street.search.TraverseMode; import org.opentripplanner.street.search.TraverseModeSet; +import org.opentripplanner.test.support.ResourceLoader; import org.opentripplanner.transit.model._data.TransitModelForTest; import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.model.site.RegularStop; @@ -31,13 +31,13 @@ public class FakeGraph { /** Build a graph in Columbus, OH with no transit */ - public static TestOtpModel buildGraphNoTransit() throws URISyntaxException { + public static TestOtpModel buildGraphNoTransit() { var deduplicator = new Deduplicator(); var stopModel = new StopModel(); var gg = new Graph(deduplicator); var transitModel = new TransitModel(stopModel, deduplicator); - File file = getFileForResource("columbus.osm.pbf"); + File file = ResourceLoader.file("/osm/columbus.osm.pbf"); OsmProvider provider = new OsmProvider(file, false); OsmModule osmModule = OsmModule.of(provider, gg).build(); @@ -46,17 +46,11 @@ public static TestOtpModel buildGraphNoTransit() throws URISyntaxException { return new TestOtpModel(gg, transitModel); } - public static File getFileForResource(String resource) throws URISyntaxException { - URL resourceUrl = FakeGraph.class.getResource(resource); - return new File(resourceUrl.toURI()); - } - /** * This introduces a 1MB test resource but is only used by TestIntermediatePlaces. */ - public static void addPerpendicularRoutes(Graph graph, TransitModel transitModel) - throws URISyntaxException { - var input = List.of(new GtfsBundle(getFileForResource("addPerpendicularRoutes.gtfs.zip"))); + public static void addPerpendicularRoutes(Graph graph, TransitModel transitModel) { + var input = List.of(new GtfsBundle(ResourceLoader.file("addPerpendicularRoutes.gtfs.zip"))); new GtfsModule(input, transitModel, graph, ServiceDateInterval.unbounded()).buildGraph(); } diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/OpenStreetMapParserTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/OpenStreetMapParserTest.java index 2fdb2b4be1f..9b8f763dde6 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/OpenStreetMapParserTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/OpenStreetMapParserTest.java @@ -13,14 +13,13 @@ import org.opentripplanner.openstreetmap.OsmProvider; import org.opentripplanner.openstreetmap.model.OSMNode; import org.opentripplanner.openstreetmap.model.OSMWay; +import org.opentripplanner.test.support.ResourceLoader; public class OpenStreetMapParserTest { @Test public void testBinaryParser() { - File osmFile = new File( - URLDecoder.decode(getClass().getResource("map.osm.pbf").getPath(), StandardCharsets.UTF_8) - ); + File osmFile = ResourceLoader.file("/osm/map.osm.pbf"); OsmProvider pr = new OsmProvider(osmFile, true); OsmDatabase osmdb = new OsmDatabase(DataImportIssueStore.NOOP, Set.of()); diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java index b8016cb1a29..cd5952a4b9f 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java @@ -10,8 +10,6 @@ import static org.opentripplanner.street.model.StreetTraversalPermission.PEDESTRIAN; import java.io.File; -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; import java.util.HashSet; import java.util.List; import java.util.Locale; @@ -42,6 +40,7 @@ import org.opentripplanner.street.model.vertex.Vertex; import org.opentripplanner.street.model.vertex.VertexLabel; import org.opentripplanner.street.search.state.State; +import org.opentripplanner.test.support.ResourceLoader; import org.opentripplanner.transit.model.framework.Deduplicator; public class OsmModuleTest { @@ -51,9 +50,7 @@ public void testGraphBuilder() { var deduplicator = new Deduplicator(); var gg = new Graph(deduplicator); - File file = new File( - URLDecoder.decode(getClass().getResource("map.osm.pbf").getFile(), StandardCharsets.UTF_8) - ); + File file = ResourceLoader.file("/osm/map.osm.pbf"); OsmProvider provider = new OsmProvider(file, true); @@ -111,12 +108,7 @@ public void testBuildGraphDetailed() { var deduplicator = new Deduplicator(); var gg = new Graph(deduplicator); - File file = new File( - URLDecoder.decode( - getClass().getResource("NYC_small.osm.pbf").getFile(), - StandardCharsets.UTF_8 - ) - ); + File file = ResourceLoader.file("/osm/NYC_small.osm.pbf"); OsmProvider provider = new OsmProvider(file, true); OsmModule osmModule = OsmModule.of(provider, gg).withAreaVisibility(true).build(); @@ -309,7 +301,7 @@ private Graph buildParkingLots() { var graph = new Graph(); var providers = Stream .of("B+R.osm.pbf", "P+R.osm.pbf") - .map(f -> new File(getClass().getResource(f).getFile())) + .map(f -> new File(getClass().getResource("/osm/" + f).getFile())) .map(f -> new OsmProvider(f, false)) .toList(); var module = OsmModule @@ -334,12 +326,7 @@ private void testBuildingAreas(boolean skipVisibility) { var deduplicator = new Deduplicator(); var graph = new Graph(deduplicator); - File file = new File( - URLDecoder.decode( - getClass().getResource("usf_area.osm.pbf").getFile(), - StandardCharsets.UTF_8 - ) - ); + File file = ResourceLoader.file("/osm/usf_area.osm.pbf"); OsmProvider provider = new OsmProvider(file, false); OsmModule loader = OsmModule.of(provider, graph).withAreaVisibility(!skipVisibility).build(); diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/PlatformLinkerTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/PlatformLinkerTest.java index 81fc6d84d6e..fbe0fe7e778 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/PlatformLinkerTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/PlatformLinkerTest.java @@ -3,15 +3,13 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; import org.junit.jupiter.api.Test; -import org.opentripplanner.graph_builder.module.FakeGraph; import org.opentripplanner.openstreetmap.OsmProvider; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.street.model.edge.AreaEdge; import org.opentripplanner.street.model.vertex.Vertex; import org.opentripplanner.street.model.vertex.VertexLabel; +import org.opentripplanner.test.support.ResourceLoader; import org.opentripplanner.transit.model.framework.Deduplicator; public class PlatformLinkerTest { @@ -27,12 +25,7 @@ public void testLinkEntriesToPlatforms() { var deduplicator = new Deduplicator(); var gg = new Graph(deduplicator); - File file = new File( - URLDecoder.decode( - FakeGraph.class.getResource("osm/skoyen.osm.pbf").getFile(), - StandardCharsets.UTF_8 - ) - ); + File file = ResourceLoader.file("/osm/skoyen.osm.pbf"); OsmProvider provider = new OsmProvider(file, false); diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/TestIntermediatePlaces.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/TestIntermediatePlaces.java index 484854477cc..e1cffe6cd85 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/TestIntermediatePlaces.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/TestIntermediatePlaces.java @@ -76,7 +76,7 @@ public static void setUp() { ); } catch (Exception e) { e.printStackTrace(); - assert false : "Could not add transit data: " + e.toString(); + assert false : "Could not add transit data: " + e; } } diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/TriangleInequalityTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/TriangleInequalityTest.java index 0e0fa3df0f9..c1999c0d479 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/TriangleInequalityTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/TriangleInequalityTest.java @@ -8,8 +8,6 @@ import static org.opentripplanner.routing.api.request.StreetMode.CAR; import java.io.File; -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; import java.util.List; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -32,6 +30,7 @@ import org.opentripplanner.street.search.state.State; import org.opentripplanner.street.search.strategy.DominanceFunctions; import org.opentripplanner.street.search.strategy.EuclideanRemainingWeightHeuristic; +import org.opentripplanner.test.support.ResourceLoader; import org.opentripplanner.transit.model.framework.Deduplicator; public class TriangleInequalityTest { @@ -49,12 +48,7 @@ public class TriangleInequalityTest { public static void onlyOnce() { graph = new Graph(new Deduplicator()); - File file = new File( - URLDecoder.decode( - TriangleInequalityTest.class.getResource("NYC_small.osm.pbf").getFile(), - StandardCharsets.UTF_8 - ) - ); + File file = ResourceLoader.file("/osm/NYC_small.osm.pbf"); OsmProvider provider = new OsmProvider(file, true); OsmModule osmModule = OsmModule.of(provider, graph).withAreaVisibility(true).build(); osmModule.buildGraph(); diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/UnconnectedAreasTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/UnconnectedAreasTest.java index 0cabf2a8ecb..09009ee7d13 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/UnconnectedAreasTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/UnconnectedAreasTest.java @@ -36,7 +36,7 @@ public class UnconnectedAreasTest { @Test public void unconnectedCarParkAndRide() { DefaultDataImportIssueStore issueStore = new DefaultDataImportIssueStore(); - Graph gg = buildOSMGraph("P+R.osm.pbf", issueStore); + Graph gg = buildOSMGraph("/osm/P+R.osm.pbf", issueStore); assertEquals(1, getParkAndRideUnlinkedIssueCount(issueStore)); @@ -53,7 +53,7 @@ public void unconnectedCarParkAndRide() { @Test public void unconnectedBikeParkAndRide() { DefaultDataImportIssueStore issueStore = new DefaultDataImportIssueStore(); - Graph gg = buildOSMGraph("B+R.osm.pbf", issueStore); + Graph gg = buildOSMGraph("/osm/B+R.osm.pbf", issueStore); assertEquals(2, getParkAndRideUnlinkedIssueCount(issueStore)); @@ -75,7 +75,11 @@ public void unconnectedBikeParkAndRide() { */ @Test public void testCoincidentNodeUnconnectedParkAndRide() { - List connections = testGeometricGraphWithClasspathFile("hackett_pr.osm.pbf", 4, 8); + List connections = testGeometricGraphWithClasspathFile( + "/osm/hackett_pr.osm.pbf", + 4, + 8 + ); assertTrue(connections.contains(VertexLabel.osm(3096570222L))); assertTrue(connections.contains(VertexLabel.osm(3094264704L))); @@ -90,7 +94,7 @@ public void testCoincidentNodeUnconnectedParkAndRide() { @Test public void testRoadPassingOverNode() { List connections = testGeometricGraphWithClasspathFile( - "coincident_pr.osm.pbf", + "/osm/coincident_pr.osm.pbf", 1, 2 ); @@ -104,7 +108,7 @@ public void testRoadPassingOverNode() { @Test public void testAreaPassingOverNode() { List connections = testGeometricGraphWithClasspathFile( - "coincident_pr_reverse.osm.pbf", + "/osm/coincident_pr_reverse.osm.pbf", 1, 2 ); @@ -118,7 +122,7 @@ public void testAreaPassingOverNode() { @Test public void testRoadPassingOverDuplicatedNode() { List connections = testGeometricGraphWithClasspathFile( - "coincident_pr_dupl.osm.pbf", + "/osm/coincident_pr_dupl.osm.pbf", 1, 2 ); @@ -139,7 +143,7 @@ public void testRoadPassingOverDuplicatedNode() { @Test public void testRoadPassingOverParkRide() { List connections = testGeometricGraphWithClasspathFile( - "coincident_pr_overlap.osm.pbf", + "/osm/coincident_pr_overlap.osm.pbf", 2, 4 ); diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/UnroutableTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/UnroutableTest.java index e1221dbdcef..4bc5c537e88 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/UnroutableTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/UnroutableTest.java @@ -2,10 +2,6 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; -import java.io.File; -import java.net.URL; -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.opentripplanner.astar.model.GraphPath; @@ -20,6 +16,7 @@ import org.opentripplanner.street.search.StreetSearchBuilder; import org.opentripplanner.street.search.state.State; import org.opentripplanner.street.search.strategy.EuclideanRemainingWeightHeuristic; +import org.opentripplanner.test.support.ResourceLoader; import org.opentripplanner.transit.model.framework.Deduplicator; /** @@ -38,8 +35,7 @@ public void setUp() throws Exception { var deduplicator = new Deduplicator(); graph = new Graph(deduplicator); - URL osmDataUrl = getClass().getResource("bridge_construction.osm.pbf"); - File osmDataFile = new File(URLDecoder.decode(osmDataUrl.getFile(), StandardCharsets.UTF_8)); + var osmDataFile = ResourceLoader.file("/osm/bridge_construction.osm.pbf"); OsmProvider provider = new OsmProvider(osmDataFile, true); OsmModule osmBuilder = OsmModule.of(provider, graph).withAreaVisibility(true).build(); osmBuilder.buildGraph(); diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilderTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilderTest.java index 60457314efe..f4a407e3e1e 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilderTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilderTest.java @@ -25,6 +25,7 @@ import org.opentripplanner.street.model.edge.AreaEdge; import org.opentripplanner.street.model.vertex.VertexLabel; import org.opentripplanner.street.model.vertex.VertexLabel.OsmNodeOnLevelLabel; +import org.opentripplanner.test.support.ResourceLoader; import org.opentripplanner.transit.model.framework.Deduplicator; public class WalkableAreaBuilderTest { @@ -43,7 +44,7 @@ public void testSetup(final TestInfo testInfo) { final Set boardingAreaRefTags = Set.of(); final OsmDatabase osmdb = new OsmDatabase(DataImportIssueStore.NOOP, boardingAreaRefTags); - final File file = new File(testInfo.getTestClass().get().getResource(osmFile).getFile()); + final File file = ResourceLoader.file("/osm/" + osmFile); new OsmProvider(file, true).readOSM(osmdb); osmdb.postLoad(); diff --git a/src/test/java/org/opentripplanner/routing/graph/DefaultRoutingServiceTest.java b/src/test/java/org/opentripplanner/routing/graph/DefaultRoutingServiceTest.java index edaa1245376..11b1e07866d 100644 --- a/src/test/java/org/opentripplanner/routing/graph/DefaultRoutingServiceTest.java +++ b/src/test/java/org/opentripplanner/routing/graph/DefaultRoutingServiceTest.java @@ -29,7 +29,7 @@ public class DefaultRoutingServiceTest extends GtfsTest { @Override public String getFeedName() { - return "testagency"; + return "gtfs/testagency"; } @Test diff --git a/src/test/java/org/opentripplanner/routing/stoptimes/AlternativeLegsTest.java b/src/test/java/org/opentripplanner/routing/stoptimes/AlternativeLegsTest.java index 27bda95f506..8aa18bbf503 100644 --- a/src/test/java/org/opentripplanner/routing/stoptimes/AlternativeLegsTest.java +++ b/src/test/java/org/opentripplanner/routing/stoptimes/AlternativeLegsTest.java @@ -23,11 +23,11 @@ class AlternativeLegsTest extends GtfsTest { @Override public String getFeedName() { - return "testagency"; + return "gtfs/testagency"; } @Test - void testPreviousLegs() throws Exception { + void testPreviousLegs() { var transitService = new DefaultTransitService(transitModel); var originalLeg = new ScheduledTransitLegReference( @@ -63,7 +63,7 @@ void testPreviousLegs() throws Exception { } @Test - void testNextLegs() throws Exception { + void testNextLegs() { var transitService = new DefaultTransitService(transitModel); var originalLeg = new ScheduledTransitLegReference( diff --git a/src/test/java/org/opentripplanner/test/support/ResourceLoader.java b/src/test/java/org/opentripplanner/test/support/ResourceLoader.java new file mode 100644 index 00000000000..96c04b05e55 --- /dev/null +++ b/src/test/java/org/opentripplanner/test/support/ResourceLoader.java @@ -0,0 +1,23 @@ +package org.opentripplanner.test.support; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.io.File; +import java.net.URL; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; + +/** + * Loads files from the resources folder. + */ +public class ResourceLoader { + + /** + * Return a File instance for the given path. + */ + public static File file(String path) { + final URL resource = ResourceLoader.class.getResource(path); + assertNotNull(resource, "Resource %s not found on file system".formatted(path)); + return new File(URLDecoder.decode(resource.getFile(), StandardCharsets.UTF_8)); + } +} diff --git a/src/test/resources/generateGtfs.py b/src/test/resources/generateGtfs.py deleted file mode 100755 index dd2e48f5f66..00000000000 --- a/src/test/resources/generateGtfs.py +++ /dev/null @@ -1,149 +0,0 @@ -#!/usr/bin/python - -readme = """A fake GTFS feed for use in testing departure time searches. -It is used to test pickup/dropoff combinations, wheelchair boarding and headsigns at the per-trip -and per-stop levels, bicycle-allowing trips, banned routes and trips, and multiple boarding search -return values all with one feed. - -The feed describes two transit routes named A and B. -Both routes have service every 15 minutes from 06:00 until 23:00. -Trip IDs are the route name concatenated with the zero-padded first departure time, e.g. A0730. -Both routes have seven stops, with each stop serving only one route. -Stops are numbered 0 through 7, and their IDs are the route name concatenated with the stop number. -Stops A3 and B3 are located in a single station complex with ID 'xfer'. -Each stop is 10 minutes away from the previous one on its line. -Arrival times are identical to departure times at stops on route A (i.e. there is no dwell time), -but arrivals are 1 minute before departures on route B (there is a dwell of 1 minute at each stop). - -All trips allow bicycles except those during rush hour (defined as 7-9 AM and 5-7 PM). -Trips departing the first stop at x:00 and x:30 can accommodate wheelchairs, while those departing -at x:15 and x:45 cannot. Wheelchair accessibility also varies at the stop level, with odd-numbered -stops marked inaccessible. - -Starting at 10 PM stops 0 and 1 are pick-up-only, 2 through 4 allow pick-up and drop-off, -and 5 and 6 only allow drop-off. Before 10 PM, all stops allow both pick-up and drop-off. - -Trip-level headsign definitions read EAST on route A and SOUTH on route B. -Stop-level headsign definitions are provided at stops 0, 1, and 2 which append the text 'VIA XFER'. - -""" - -# 1. The wheelchair_boarding field in stops.txt is standard but optional. -# 2. The wheelchair_accessible field in trips.txt is an (optional) extension. -# http://support.google.com/transitpartners/bin/answer.py?hl=en&answer=2450962 -# 3. The trip_bikes_allowed field in trips.txt is an (optional) extension. -# https://groups.google.com/d/msg/gtfs-changes/QqaGOuNmG7o/uKpD70szrbkJ - -WHEELCHAIR_YES = 1 -WHEELCHAIR_NO = 2 -WHEELCHAIR_UNDEF = 0 -BIKE_YES = 2 -BIKE_NO = 1 -BIKE_UNDEF = 0 -LOCATION_STOP = 0 -LOCATION_STATION = 1 -PICKDROP_SCHEDULED = 0 -PICKDROP_NO = 1 -PICKDROP_PHONE = 2 -PICKDROP_COORDINATE = 3 - -# zipfile has no way to add file-like buffers (SpooledTemporaryFile is out), so we have to add the -# intermediate files from the filesystem, subclass ZipFile, or write from strings. We use the latter. -from zipfile import ZipFile -gtfs = ZipFile('generated.gtfs.zip', mode='w') - -# The agency and calendar tables are hard coded (not generated). There should be -# one agency called TEST and one service_id called ALL that runs every day of the week from -# 01-JAN-2012 through 31-DEC-2012. -agency_rows = """agency_id,agency_name,agency_url,agency_timezone -TEST,Gulf of Guinea Transit,http://www.test.com,Africa/Accra -""" -gtfs.writestr('agency.txt', agency_rows) -calendar_rows = """service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date -ALL,1,1,1,1,1,1,1,20120101,20121231 -""" -gtfs.writestr('calendar.txt', calendar_rows) - -from cStringIO import StringIO -routes_file = StringIO() -stops_file = StringIO() -import csv -routes = csv.writer(routes_file) -routes.writerow(['route_id', 'route_short_name', 'route_long_name', 'route_type']) -stops = csv.writer(stops_file) -stops.writerow(['stop_id', 'stop_name', 'stop_lat', 'stop_lon', 'location_type', 'parent_station', 'wheelchair_boarding']) -stops.writerow(['xfer', 'xfer', '1.00', '1.03', LOCATION_STATION, None, WHEELCHAIR_YES]) # station allows wheelchair access -for route in ['A', 'B'] : - routes.writerow((route, route, route, 1)) # make B a different mode for mode param testing? - if route == 'A' : - stop_lon = stop_lat = 1.00 - else : - stop_lon = stop_lat = 1.03 - for stop in range (7) : - stop_id = route + str(stop) - wheelchair = WHEELCHAIR_YES if (stop % 2 == 0) else WHEELCHAIR_NO - parent_station = 'xfer' if stop == 3 else None - row = (stop_id, stop_id, stop_lat, stop_lon, LOCATION_STOP, parent_station, wheelchair) - stops.writerow(row) - if route == 'A' : - stop_lon += 0.01000 - else : - stop_lat -= 0.01000 - -gtfs.writestr('routes.txt', routes_file.getvalue()) -gtfs.writestr('stops.txt', stops_file.getvalue()) - -def sec(h, m, s = 0) : - return (((h * 60) + m) * 60) + s - -def tstr(h, m, s = 0): - # handle (possibly negative) minute overage - s = sec(h, m, s) - m = s / 60 - s = s % 60 - h = m / 60 - m = m % 60 - return '%02d:%02d:%02d' % (h, m, s) - -trips_file = StringIO() -trips = csv.writer(trips_file) -header = ('route_id', 'service_id', 'trip_id', 'trip_headsign') -trips.writerow(header) - -stop_times_file = StringIO() -stop_times = csv.writer(stop_times_file) -header = ('trip_id', 'arrival_time', 'departure_time', 'stop_id', 'stop_sequence', 'stop_headsign', 'pickup_type', 'drop_off_type') -stop_times.writerow(header) - -for route in ['A', 'B'] : - headsign = 'EAST' if route == 'A' else 'SOUTH' - for hour in range (6, 23): - bicycle = not (hour in [7, 8, 17, 18]) - for minute in [0, 15, 30, 45] : - # print 'route %s %02d:%02d' % (route, hour, minute) - trip_id = '%s%02d%02d' % (route, hour, minute) - wheelchair_trip = minute in [0, 30] - arv = dep = minute - if route == 'B' : - arv -= 1 - row = (route, 'ALL', trip_id, headsign) - trips.writerow(row) - for stop in range (7) : - stop_id = route + str(stop) - if hour < 22 : - pickup = drop_off = PICKDROP_SCHEDULED - else : - pickup = PICKDROP_SCHEDULED if stop <= 4 else PICKDROP_NO - drop_off = PICKDROP_SCHEDULED if stop >= 2 else PICKDROP_NO - stop_headsign = headsign + ' VIA XFER' if stop < 3 else None - row = (trip_id, tstr(hour, arv), tstr(hour, dep), stop_id, stop + 100, stop_headsign, pickup, drop_off) - stop_times.writerow(row) - arv += 10 - dep += 10 - -gtfs.writestr('trips.txt', trips_file.getvalue()) -gtfs.writestr('stop_times.txt', stop_times_file.getvalue()) -gtfs.writestr('README', readme) -#interline trips back in the other direction? - - diff --git a/src/test/resources/generated.gtfs.zip b/src/test/resources/generated.gtfs.zip deleted file mode 100644 index 0786dab730b60a056be88436fafccb439d6d753d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 46498 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 Zf$-pyfA&`kTmNeR{>twEEC1(z`+vP1?n?jw diff --git a/src/test/resources/org/opentripplanner/graph_builder/module/addPerpendicularRoutes.gtfs.zip b/src/test/resources/gtfs/addPerpendicularRoutes.gtfs.zip similarity index 100% rename from src/test/resources/org/opentripplanner/graph_builder/module/addPerpendicularRoutes.gtfs.zip rename to src/test/resources/gtfs/addPerpendicularRoutes.gtfs.zip diff --git a/src/test/resources/testagency/agency.txt b/src/test/resources/gtfs/testagency/agency.txt similarity index 100% rename from src/test/resources/testagency/agency.txt rename to src/test/resources/gtfs/testagency/agency.txt diff --git a/src/test/resources/testagency/calendar.txt b/src/test/resources/gtfs/testagency/calendar.txt similarity index 100% rename from src/test/resources/testagency/calendar.txt rename to src/test/resources/gtfs/testagency/calendar.txt diff --git a/src/test/resources/testagency/frequencies.txt b/src/test/resources/gtfs/testagency/frequencies.txt similarity index 100% rename from src/test/resources/testagency/frequencies.txt rename to src/test/resources/gtfs/testagency/frequencies.txt diff --git a/src/test/resources/testagency/pathways.txt b/src/test/resources/gtfs/testagency/pathways.txt similarity index 100% rename from src/test/resources/testagency/pathways.txt rename to src/test/resources/gtfs/testagency/pathways.txt diff --git a/src/test/resources/testagency/routes.txt b/src/test/resources/gtfs/testagency/routes.txt similarity index 100% rename from src/test/resources/testagency/routes.txt rename to src/test/resources/gtfs/testagency/routes.txt diff --git a/src/test/resources/testagency/shapes.txt b/src/test/resources/gtfs/testagency/shapes.txt similarity index 100% rename from src/test/resources/testagency/shapes.txt rename to src/test/resources/gtfs/testagency/shapes.txt diff --git a/src/test/resources/testagency/stop_times.txt b/src/test/resources/gtfs/testagency/stop_times.txt similarity index 100% rename from src/test/resources/testagency/stop_times.txt rename to src/test/resources/gtfs/testagency/stop_times.txt diff --git a/src/test/resources/testagency/stops.txt b/src/test/resources/gtfs/testagency/stops.txt similarity index 100% rename from src/test/resources/testagency/stops.txt rename to src/test/resources/gtfs/testagency/stops.txt diff --git a/src/test/resources/testagency/transfers.txt b/src/test/resources/gtfs/testagency/transfers.txt similarity index 100% rename from src/test/resources/testagency/transfers.txt rename to src/test/resources/gtfs/testagency/transfers.txt diff --git a/src/test/resources/testagency/trips.txt b/src/test/resources/gtfs/testagency/trips.txt similarity index 100% rename from src/test/resources/testagency/trips.txt rename to src/test/resources/gtfs/testagency/trips.txt diff --git a/src/test/resources/org/opentripplanner/graph_builder/module/osm/B+R.osm.pbf b/src/test/resources/osm/B+R.osm.pbf similarity index 100% rename from src/test/resources/org/opentripplanner/graph_builder/module/osm/B+R.osm.pbf rename to src/test/resources/osm/B+R.osm.pbf diff --git a/src/test/resources/org/opentripplanner/graph_builder/module/osm/NYC_small.osm.pbf b/src/test/resources/osm/NYC_small.osm.pbf similarity index 100% rename from src/test/resources/org/opentripplanner/graph_builder/module/osm/NYC_small.osm.pbf rename to src/test/resources/osm/NYC_small.osm.pbf diff --git a/src/test/resources/org/opentripplanner/graph_builder/module/osm/P+R.osm.pbf b/src/test/resources/osm/P+R.osm.pbf similarity index 100% rename from src/test/resources/org/opentripplanner/graph_builder/module/osm/P+R.osm.pbf rename to src/test/resources/osm/P+R.osm.pbf diff --git a/src/test/resources/org/opentripplanner/graph_builder/module/osm/bridge_construction.osm.pbf b/src/test/resources/osm/bridge_construction.osm.pbf similarity index 100% rename from src/test/resources/org/opentripplanner/graph_builder/module/osm/bridge_construction.osm.pbf rename to src/test/resources/osm/bridge_construction.osm.pbf diff --git a/src/test/resources/org/opentripplanner/graph_builder/module/osm/coincident_pr.osm.pbf b/src/test/resources/osm/coincident_pr.osm.pbf similarity index 100% rename from src/test/resources/org/opentripplanner/graph_builder/module/osm/coincident_pr.osm.pbf rename to src/test/resources/osm/coincident_pr.osm.pbf diff --git a/src/test/resources/org/opentripplanner/graph_builder/module/osm/coincident_pr_dupl.osm.pbf b/src/test/resources/osm/coincident_pr_dupl.osm.pbf similarity index 100% rename from src/test/resources/org/opentripplanner/graph_builder/module/osm/coincident_pr_dupl.osm.pbf rename to src/test/resources/osm/coincident_pr_dupl.osm.pbf diff --git a/src/test/resources/org/opentripplanner/graph_builder/module/osm/coincident_pr_overlap.osm.pbf b/src/test/resources/osm/coincident_pr_overlap.osm.pbf similarity index 100% rename from src/test/resources/org/opentripplanner/graph_builder/module/osm/coincident_pr_overlap.osm.pbf rename to src/test/resources/osm/coincident_pr_overlap.osm.pbf diff --git a/src/test/resources/org/opentripplanner/graph_builder/module/osm/coincident_pr_reverse.osm.pbf b/src/test/resources/osm/coincident_pr_reverse.osm.pbf similarity index 100% rename from src/test/resources/org/opentripplanner/graph_builder/module/osm/coincident_pr_reverse.osm.pbf rename to src/test/resources/osm/coincident_pr_reverse.osm.pbf diff --git a/src/test/resources/org/opentripplanner/graph_builder/module/columbus.osm.pbf b/src/test/resources/osm/columbus.osm.pbf similarity index 100% rename from src/test/resources/org/opentripplanner/graph_builder/module/columbus.osm.pbf rename to src/test/resources/osm/columbus.osm.pbf diff --git a/src/test/resources/org/opentripplanner/graph_builder/module/osm/hackett_pr.osm.pbf b/src/test/resources/osm/hackett_pr.osm.pbf similarity index 100% rename from src/test/resources/org/opentripplanner/graph_builder/module/osm/hackett_pr.osm.pbf rename to src/test/resources/osm/hackett_pr.osm.pbf diff --git a/src/test/resources/isoiiluoto.pbf b/src/test/resources/osm/isoiiluoto.pbf similarity index 100% rename from src/test/resources/isoiiluoto.pbf rename to src/test/resources/osm/isoiiluoto.pbf diff --git a/src/test/resources/org/opentripplanner/graph_builder/module/osm/lund-station-sweden.osm.pbf b/src/test/resources/osm/lund-station-sweden.osm.pbf similarity index 100% rename from src/test/resources/org/opentripplanner/graph_builder/module/osm/lund-station-sweden.osm.pbf rename to src/test/resources/osm/lund-station-sweden.osm.pbf diff --git a/src/test/resources/org/opentripplanner/graph_builder/module/osm/map.osm.pbf b/src/test/resources/osm/map.osm.pbf similarity index 100% rename from src/test/resources/org/opentripplanner/graph_builder/module/osm/map.osm.pbf rename to src/test/resources/osm/map.osm.pbf diff --git a/src/test/resources/oslo-east-filtered.osm.pbf b/src/test/resources/osm/oslo-east-filtered.osm.pbf similarity index 100% rename from src/test/resources/oslo-east-filtered.osm.pbf rename to src/test/resources/osm/oslo-east-filtered.osm.pbf diff --git a/src/test/resources/org/opentripplanner/graph_builder/module/osm/otp-multipolygon-test.osm.pbf b/src/test/resources/osm/otp-multipolygon-test.osm.pbf similarity index 100% rename from src/test/resources/org/opentripplanner/graph_builder/module/osm/otp-multipolygon-test.osm.pbf rename to src/test/resources/osm/otp-multipolygon-test.osm.pbf diff --git a/src/test/resources/org/opentripplanner/graph_builder/module/osm/skoyen.osm.pbf b/src/test/resources/osm/skoyen.osm.pbf similarity index 100% rename from src/test/resources/org/opentripplanner/graph_builder/module/osm/skoyen.osm.pbf rename to src/test/resources/osm/skoyen.osm.pbf diff --git a/src/test/resources/org/opentripplanner/graph_builder/module/osm/stopareas.pbf b/src/test/resources/osm/stopareas.pbf similarity index 100% rename from src/test/resources/org/opentripplanner/graph_builder/module/osm/stopareas.pbf rename to src/test/resources/osm/stopareas.pbf diff --git a/src/test/resources/org/opentripplanner/graph_builder/module/osm/usf_area.osm.pbf b/src/test/resources/osm/usf_area.osm.pbf similarity index 100% rename from src/test/resources/org/opentripplanner/graph_builder/module/osm/usf_area.osm.pbf rename to src/test/resources/osm/usf_area.osm.pbf diff --git a/src/test/resources/raptor/speedtest/norway/README.md b/src/test/resources/raptor/speedtest/norway/README.md deleted file mode 100644 index eafa49cb828..00000000000 --- a/src/test/resources/raptor/speedtest/norway/README.md +++ /dev/null @@ -1,12 +0,0 @@ -#### Download GTFS data: - -- https://storage.googleapis.com/marduk-production/outbound/gtfs/rb_norway-aggregated-gtfs.zip - -If the link above do not work you should be able to find it on the ENTUR web: - -- https://www.entur.org/ - -#### Configure the test - -- Set the testDate in the speed-test-config.json - diff --git a/src/test/resources/testagency.zip b/src/test/resources/testagency.zip deleted file mode 100644 index 8fd344583980310ecf5984e6b910e6d4b46b43fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3453 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 Date: Wed, 16 Aug 2023 13:15:38 +0200 Subject: [PATCH 02/95] Consitently use File instance througout tests --- .../ext/flex/FlexIntegrationTest.java | 2 +- .../opentripplanner/ConstantsForTests.java | 83 ++++++++++--------- .../datastore/file/ZipFileDataSourceTest.java | 2 +- .../ZipStreamDataSourceDecoratorTest.java | 2 +- .../graph_builder/module/GtfsModuleTest.java | 2 +- .../OsmBoardingLocationsModuleTest.java | 2 +- .../islandpruning/AdaptivePruningTest.java | 5 +- .../islandpruning/PruneNoThruIslandsTest.java | 3 +- .../gtfs/GtfsContextBuilder.java | 10 +-- 9 files changed, 58 insertions(+), 53 deletions(-) diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/FlexIntegrationTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/FlexIntegrationTest.java index 482906a58a9..cf55a1a2f58 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/FlexIntegrationTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/FlexIntegrationTest.java @@ -62,7 +62,7 @@ static void setup() { var martaGtfsPath = ResourceLoader.file(FlexTest.MARTA_BUS_856_GTFS); var flexGtfsPath = ResourceLoader.file(FlexTest.COBB_FLEX_GTFS); - TestOtpModel model = ConstantsForTests.buildOsmGraph(osmPath.getAbsolutePath()); + TestOtpModel model = ConstantsForTests.buildOsmGraph(osmPath); graph = model.graph(); transitModel = model.transitModel(); diff --git a/src/test/java/org/opentripplanner/ConstantsForTests.java b/src/test/java/org/opentripplanner/ConstantsForTests.java index 7c39cd55b71..145125943b8 100644 --- a/src/test/java/org/opentripplanner/ConstantsForTests.java +++ b/src/test/java/org/opentripplanner/ConstantsForTests.java @@ -39,6 +39,7 @@ import org.opentripplanner.standalone.config.OtpConfigLoader; import org.opentripplanner.street.search.TraverseMode; import org.opentripplanner.street.search.TraverseModeSet; +import org.opentripplanner.test.support.ResourceLoader; import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.service.StopModel; @@ -46,12 +47,13 @@ public class ConstantsForTests { - public static final String CALTRAIN_GTFS = "src/test/resources/gtfs/caltrain_gtfs.zip"; + public static final File CALTRAIN_GTFS = ResourceLoader.file("/gtfs/caltrain_gtfs.zip"); - static final String PORTLAND_GTFS = "src/test/resources/portland/portland.gtfs.zip"; + public static final File PORTLAND_GTFS = ResourceLoader.file("/portland/portland.gtfs.zip"); - public static final String PORTLAND_CENTRAL_OSM = - "src/test/resources/portland/portland-central-filtered.osm.pbf"; + public static final File PORTLAND_CENTRAL_OSM = ResourceLoader.file( + "/portland/portland-central-filtered.osm.pbf" + ); private static final String PORTLAND_BIKE_SHARE_CSV = "src/test/resources/portland/portland-vehicle-rental.csv"; @@ -61,14 +63,15 @@ public class ConstantsForTests { private static final String OSLO_EAST_OSM = "src/test/resources/osm/oslo-east-filtered.osm.pbf"; - public static final String KCM_GTFS = "src/test/resources/gtfs/kcm_gtfs.zip"; + public static final File KCM_GTFS = ResourceLoader.file("/gtfs/kcm_gtfs.zip"); - public static final String FAKE_GTFS = "src/test/resources/gtfs/testagency/"; + public static final File FAKE_GTFS = ResourceLoader.file("/gtfs/testagency/"); - public static final String FARE_COMPONENT_GTFS = - "src/test/resources/gtfs/farecomponents.gtfs.zip"; + public static final File FARE_COMPONENT_GTFS = ResourceLoader.file( + "/gtfs/farecomponents.gtfs.zip" + ); - public static final String SHAPE_DIST_GTFS = "src/test/resources/gtfs/shape_dist_traveled/"; + public static final File SHAPE_DIST_GTFS = ResourceLoader.file("/gtfs/shape_dist_traveled/"); private static final String NETEX_NORDIC_DIR = "src/test/resources/netex/nordic"; @@ -76,23 +79,31 @@ public class ConstantsForTests { private static final String NETEX_EPIP_DIR = "src/test/resources/netex/epip/"; private static final String NETEX_EPIP_DATA_DIR = NETEX_EPIP_DIR + "netex_epip_minimal/"; /* Stuttgart area, Germany */ - public static final String DEUFRINGEN_OSM = - "src/test/resources/germany/deufringen-minimal.osm.pbf"; - public static final String BOEBLINGEN_OSM = - "src/test/resources/germany/boeblingen-minimal.osm.pbf"; - public static final String VVS_BUS_764_ONLY = - "src/test/resources/germany/vvs-bus-764-only.gtfs.zip"; - public static final String VVS_BUS_751_ONLY = - "src/test/resources/germany/vvs-bus-751-only.gtfs.zip"; - public static final String HERRENBERG_HINDENBURG_STR_UNDER_CONSTRUCTION_OSM = - "src/test/resources/germany/herrenberg-hindenburgstr-under-construction.osm.pbf"; - public static final String HERRENBERG_BARRIER_GATES_OSM = - "src/test/resources/germany/herrenberg-barrier-gates.osm.pbf"; - public static final String HERRENBERG_OSM = - "src/test/resources/germany/herrenberg-minimal.osm.pbf"; - public static final String ISLAND_PRUNE_OSM = - "src/test/resources/germany/herrenberg-island-prune-nothru.osm.pbf"; - public static final String ADAPTIVE_PRUNE_OSM = "src/test/resources/osm/isoiiluoto.pbf"; + public static final File DEUFRINGEN_OSM = ResourceLoader.file( + "/germany/deufringen-minimal.osm.pbf" + ); + public static final File BOEBLINGEN_OSM = ResourceLoader.file( + "/germany/boeblingen-minimal.osm.pbf" + ); + public static final File VVS_BUS_764_ONLY = ResourceLoader.file( + "/germany/vvs-bus-764-only.gtfs.zip" + ); + public static final File VVS_BUS_751_ONLY = ResourceLoader.file( + "/germany/vvs-bus-751-only.gtfs.zip" + ); + public static final File HERRENBERG_HINDENBURG_STR_UNDER_CONSTRUCTION_OSM = ResourceLoader.file( + "/germany/herrenberg-hindenburgstr-under-construction.osm.pbf" + ); + public static final File HERRENBERG_BARRIER_GATES_OSM = ResourceLoader.file( + "/germany/herrenberg-barrier-gates.osm.pbf" + ); + public static final File HERRENBERG_OSM = ResourceLoader.file( + "/germany/herrenberg-minimal.osm.pbf" + ); + public static final File ISLAND_PRUNE_OSM = ResourceLoader.file( + "/germany/herrenberg-island-prune-nothru.osm.pbf" + ); + public static final File ADAPTIVE_PRUNE_OSM = ResourceLoader.file("/osm/isoiiluoto.pbf"); /* filenames encoded with cp437 and utf8 */ public static final String UMLAUT_CP437_ZIP = "src/test/resources/umlaut-cp437.zip"; @@ -147,7 +158,7 @@ public static TestOtpModel buildNewPortlandGraph(boolean withElevation) { var transitModel = new TransitModel(new StopModel(), deduplicator); // Add street data from OSM { - File osmFile = new File(PORTLAND_CENTRAL_OSM); + File osmFile = PORTLAND_CENTRAL_OSM; OsmProvider osmProvider = new OsmProvider(osmFile, false); OsmModule osmModule = OsmModule .of(osmProvider, graph) @@ -185,14 +196,13 @@ public static TestOtpModel buildNewPortlandGraph(boolean withElevation) { } } - public static TestOtpModel buildOsmGraph(String osmPath) { + public static TestOtpModel buildOsmGraph(File osmFile) { try { var deduplicator = new Deduplicator(); var stopModel = new StopModel(); var graph = new Graph(deduplicator); var transitModel = new TransitModel(stopModel, deduplicator); // Add street data from OSM - File osmFile = new File(osmPath); OsmProvider osmProvider = new OsmProvider(osmFile, true); OsmModule osmModule = OsmModule.of(osmProvider, graph).build(); osmModule.buildGraph(); @@ -202,7 +212,7 @@ public static TestOtpModel buildOsmGraph(String osmPath) { } } - public static TestOtpModel buildOsmAndGtfsGraph(String osmPath, String gtfsPath) { + public static TestOtpModel buildOsmAndGtfsGraph(File osmPath, File gtfsPath) { var otpModel = buildOsmGraph(osmPath); addGtfsToGraph( @@ -219,19 +229,16 @@ public static TestOtpModel buildOsmAndGtfsGraph(String osmPath, String gtfsPath) return otpModel; } - public static TestOtpModel buildGtfsGraph(String gtfsPath) { + public static TestOtpModel buildGtfsGraph(File gtfsPath) { return buildGtfsGraph(gtfsPath, new DefaultFareServiceFactory()); } - public static TestOtpModel buildGtfsGraph( - String gtfsPath, - FareServiceFactory fareServiceFactory - ) { + public static TestOtpModel buildGtfsGraph(File gtfsFile, FareServiceFactory fareServiceFactory) { var deduplicator = new Deduplicator(); var stopModel = new StopModel(); var graph = new Graph(deduplicator); var transitModel = new TransitModel(stopModel, deduplicator); - addGtfsToGraph(graph, transitModel, gtfsPath, fareServiceFactory, null); + addGtfsToGraph(graph, transitModel, gtfsFile, fareServiceFactory, null); return new TestOtpModel(graph, transitModel); } @@ -294,11 +301,11 @@ public synchronized TestOtpModel getCachedPortlandGraphWithElevation() { public static void addGtfsToGraph( Graph graph, TransitModel transitModel, - String file, + File file, FareServiceFactory fareServiceFactory, @Nullable String feedId ) { - var bundle = new GtfsBundle(new File(file)); + var bundle = new GtfsBundle(file); bundle.setFeedId(new GtfsFeedId.Builder().id(feedId).build()); var module = new GtfsModule( diff --git a/src/test/java/org/opentripplanner/datastore/file/ZipFileDataSourceTest.java b/src/test/java/org/opentripplanner/datastore/file/ZipFileDataSourceTest.java index ba1fc8977eb..1ad90ed7fc4 100644 --- a/src/test/java/org/opentripplanner/datastore/file/ZipFileDataSourceTest.java +++ b/src/test/java/org/opentripplanner/datastore/file/ZipFileDataSourceTest.java @@ -27,7 +27,7 @@ public class ZipFileDataSourceTest { // Sometime close to 2000-01-01 private static final long TIME = 30 * 365 * 24 * 60 * 60 * 1000L; - private static final String FILENAME = ConstantsForTests.CALTRAIN_GTFS; + private static final String FILENAME = ConstantsForTests.CALTRAIN_GTFS.getAbsolutePath(); @Test public void testAccessorsForNoneExistingFile() throws IOException { diff --git a/src/test/java/org/opentripplanner/datastore/file/ZipStreamDataSourceDecoratorTest.java b/src/test/java/org/opentripplanner/datastore/file/ZipStreamDataSourceDecoratorTest.java index b6d82edea41..a165b05ed03 100644 --- a/src/test/java/org/opentripplanner/datastore/file/ZipStreamDataSourceDecoratorTest.java +++ b/src/test/java/org/opentripplanner/datastore/file/ZipStreamDataSourceDecoratorTest.java @@ -21,7 +21,7 @@ class ZipStreamDataSourceDecoratorTest { private static final long TIME = 30 * 365 * 24 * 60 * 60 * 1000L; - private static final String FILENAME = ConstantsForTests.CALTRAIN_GTFS; + private static final String FILENAME = ConstantsForTests.CALTRAIN_GTFS.getAbsolutePath(); static final List EXPECTED_ZIP_ENTRIES = List.of( "trips.txt", "agency.txt", diff --git a/src/test/java/org/opentripplanner/graph_builder/module/GtfsModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/GtfsModuleTest.java index c120b63c08a..61b171d6485 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/GtfsModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/GtfsModuleTest.java @@ -27,7 +27,7 @@ class GtfsModuleTest { public void addShapesForFrequencyTrips() { var model = buildTestModel(); - var bundle = new GtfsBundle(new File(ConstantsForTests.FAKE_GTFS)); + var bundle = new GtfsBundle(ConstantsForTests.FAKE_GTFS); var module = new GtfsModule( List.of(bundle), model.transitModel, diff --git a/src/test/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModuleTest.java index 0195fa9f9ca..43a10188a20 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModuleTest.java @@ -37,7 +37,7 @@ */ class OsmBoardingLocationsModuleTest { - File file = new File(ConstantsForTests.HERRENBERG_OSM); + File file = ConstantsForTests.HERRENBERG_OSM; RegularStop platform = TransitModelForTest .stop("de:08115:4512:4:101") .withCoordinate(48.59328, 8.86128) diff --git a/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/AdaptivePruningTest.java b/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/AdaptivePruningTest.java index 57b8e018662..56ac1cae22d 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/AdaptivePruningTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/AdaptivePruningTest.java @@ -65,14 +65,13 @@ public void mainGraphIsNotRemoved() { ); } - private static Graph buildOsmGraph(String osmPath) { + private static Graph buildOsmGraph(File file) { try { var deduplicator = new Deduplicator(); var graph = new Graph(deduplicator); var transitModel = new TransitModel(new StopModel(), deduplicator); // Add street data from OSM - File osmFile = new File(osmPath); - OsmProvider osmProvider = new OsmProvider(osmFile, true); + OsmProvider osmProvider = new OsmProvider(file , true); OsmModule osmModule = OsmModule.of(osmProvider, graph).withEdgeNamer(new TestNamer()).build(); osmModule.buildGraph(); diff --git a/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/PruneNoThruIslandsTest.java b/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/PruneNoThruIslandsTest.java index 6fd7c752ee5..d143a184299 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/PruneNoThruIslandsTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/PruneNoThruIslandsTest.java @@ -63,13 +63,12 @@ public void pruneFloatingBikeAndWalkIsland() { ); } - private static Graph buildOsmGraph(String osmPath) { + private static Graph buildOsmGraph(File osmFile) { try { var deduplicator = new Deduplicator(); var graph = new Graph(deduplicator); var transitModel = new TransitModel(new StopModel(), deduplicator); // Add street data from OSM - File osmFile = new File(osmPath); OsmProvider osmProvider = new OsmProvider(osmFile, true); OsmModule osmModule = OsmModule.of(osmProvider, graph).withEdgeNamer(new TestNamer()).build(); diff --git a/src/test/java/org/opentripplanner/gtfs/GtfsContextBuilder.java b/src/test/java/org/opentripplanner/gtfs/GtfsContextBuilder.java index e79e993a7cb..9c07e656183 100644 --- a/src/test/java/org/opentripplanner/gtfs/GtfsContextBuilder.java +++ b/src/test/java/org/opentripplanner/gtfs/GtfsContextBuilder.java @@ -37,11 +37,11 @@ public GtfsContextBuilder(GtfsFeedId feedId, OtpTransitServiceBuilder transitBui this.transitBuilder = transitBuilder; } - public static GtfsContextBuilder contextBuilder(String path) throws IOException { - return contextBuilder(null, path); + public static GtfsContextBuilder contextBuilder(File file) throws IOException { + return contextBuilder(null, file); } - public static GtfsContextBuilder contextBuilder(@Nullable String defaultFeedId, String path) + public static GtfsContextBuilder contextBuilder(@Nullable String defaultFeedId, File path) throws IOException { GtfsImport gtfsImport = gtfsImport(defaultFeedId, path); GtfsFeedId feedId = gtfsImport.getFeedId(); @@ -123,8 +123,8 @@ public void repairStopTimesAndGenerateTripPatterns() { /* private stuff */ - private static GtfsImport gtfsImport(String defaultFeedId, String path) throws IOException { - return new GtfsImport(defaultFeedId, new File(path)); + private static GtfsImport gtfsImport(String defaultFeedId, File file) throws IOException { + return new GtfsImport(defaultFeedId, file); } private void repairStopTimesForEachTrip() { From 09fc4b31352c8e2d8f2dcd70901815eb27075dc6 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 16 Aug 2023 13:22:08 +0200 Subject: [PATCH 03/95] Move Germany files into the correct subfolders --- .../opentripplanner/ConstantsForTests.java | 53 +++++++----------- .../islandpruning/AdaptivePruningTest.java | 2 +- .../model/BicycleNetworkRelationsTest.java | 6 +- .../test/support/ResourceLoader.java | 8 ++- .../vvs-bus-751-only.gtfs.zip | Bin .../vvs-bus-764-only.gtfs.zip | Bin .../boeblingen-minimal.osm.pbf | Bin .../deufringen-minimal.osm.pbf | Bin .../{germany => osm}/ehningen-minimal.osm.pbf | Bin .../herrenberg-barrier-gates.osm.pbf | Bin ...g-hindenburgstr-under-construction.osm.pbf | Bin .../herrenberg-island-prune-nothru.osm.pbf | Bin .../herrenberg-minimal.osm.pbf | Bin 13 files changed, 29 insertions(+), 40 deletions(-) rename src/test/resources/{germany => gtfs}/vvs-bus-751-only.gtfs.zip (100%) rename src/test/resources/{germany => gtfs}/vvs-bus-764-only.gtfs.zip (100%) rename src/test/resources/{germany => osm}/boeblingen-minimal.osm.pbf (100%) rename src/test/resources/{germany => osm}/deufringen-minimal.osm.pbf (100%) rename src/test/resources/{germany => osm}/ehningen-minimal.osm.pbf (100%) rename src/test/resources/{germany => osm}/herrenberg-barrier-gates.osm.pbf (100%) rename src/test/resources/{germany => osm}/herrenberg-hindenburgstr-under-construction.osm.pbf (100%) rename src/test/resources/{germany => osm}/herrenberg-island-prune-nothru.osm.pbf (100%) rename src/test/resources/{germany => osm}/herrenberg-minimal.osm.pbf (100%) diff --git a/src/test/java/org/opentripplanner/ConstantsForTests.java b/src/test/java/org/opentripplanner/ConstantsForTests.java index 145125943b8..686a0d120cf 100644 --- a/src/test/java/org/opentripplanner/ConstantsForTests.java +++ b/src/test/java/org/opentripplanner/ConstantsForTests.java @@ -1,5 +1,7 @@ package org.opentripplanner; +import static org.opentripplanner.test.support.ResourceLoader.file; + import com.csvreader.CsvReader; import java.io.File; import java.io.IOException; @@ -39,7 +41,6 @@ import org.opentripplanner.standalone.config.OtpConfigLoader; import org.opentripplanner.street.search.TraverseMode; import org.opentripplanner.street.search.TraverseModeSet; -import org.opentripplanner.test.support.ResourceLoader; import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.service.StopModel; @@ -47,11 +48,11 @@ public class ConstantsForTests { - public static final File CALTRAIN_GTFS = ResourceLoader.file("/gtfs/caltrain_gtfs.zip"); + public static final File CALTRAIN_GTFS = file("/gtfs/caltrain_gtfs.zip"); - public static final File PORTLAND_GTFS = ResourceLoader.file("/portland/portland.gtfs.zip"); + public static final File PORTLAND_GTFS = file("/portland/portland.gtfs.zip"); - public static final File PORTLAND_CENTRAL_OSM = ResourceLoader.file( + public static final File PORTLAND_CENTRAL_OSM = file( "/portland/portland-central-filtered.osm.pbf" ); @@ -63,15 +64,13 @@ public class ConstantsForTests { private static final String OSLO_EAST_OSM = "src/test/resources/osm/oslo-east-filtered.osm.pbf"; - public static final File KCM_GTFS = ResourceLoader.file("/gtfs/kcm_gtfs.zip"); + public static final File KCM_GTFS = file("/gtfs/kcm_gtfs.zip"); - public static final File FAKE_GTFS = ResourceLoader.file("/gtfs/testagency/"); + public static final File FAKE_GTFS = file("/gtfs/testagency/"); - public static final File FARE_COMPONENT_GTFS = ResourceLoader.file( - "/gtfs/farecomponents.gtfs.zip" - ); + public static final File FARE_COMPONENT_GTFS = file("/gtfs/farecomponents.gtfs.zip"); - public static final File SHAPE_DIST_GTFS = ResourceLoader.file("/gtfs/shape_dist_traveled/"); + public static final File SHAPE_DIST_GTFS = file("/gtfs/shape_dist_traveled/"); private static final String NETEX_NORDIC_DIR = "src/test/resources/netex/nordic"; @@ -79,31 +78,19 @@ public class ConstantsForTests { private static final String NETEX_EPIP_DIR = "src/test/resources/netex/epip/"; private static final String NETEX_EPIP_DATA_DIR = NETEX_EPIP_DIR + "netex_epip_minimal/"; /* Stuttgart area, Germany */ - public static final File DEUFRINGEN_OSM = ResourceLoader.file( - "/germany/deufringen-minimal.osm.pbf" - ); - public static final File BOEBLINGEN_OSM = ResourceLoader.file( - "/germany/boeblingen-minimal.osm.pbf" - ); - public static final File VVS_BUS_764_ONLY = ResourceLoader.file( - "/germany/vvs-bus-764-only.gtfs.zip" - ); - public static final File VVS_BUS_751_ONLY = ResourceLoader.file( - "/germany/vvs-bus-751-only.gtfs.zip" - ); - public static final File HERRENBERG_HINDENBURG_STR_UNDER_CONSTRUCTION_OSM = ResourceLoader.file( - "/germany/herrenberg-hindenburgstr-under-construction.osm.pbf" - ); - public static final File HERRENBERG_BARRIER_GATES_OSM = ResourceLoader.file( - "/germany/herrenberg-barrier-gates.osm.pbf" - ); - public static final File HERRENBERG_OSM = ResourceLoader.file( - "/germany/herrenberg-minimal.osm.pbf" + public static final File DEUFRINGEN_OSM = file("/osm/deufringen-minimal.osm.pbf"); + public static final File BOEBLINGEN_OSM = file("/osm/boeblingen-minimal.osm.pbf"); + public static final File VVS_BUS_764_ONLY = file("/gtfs/vvs-bus-764-only.gtfs.zip"); + public static final File VVS_BUS_751_ONLY = file("/gtfs/vvs-bus-751-only.gtfs.zip"); + public static final File HERRENBERG_HINDENBURG_STR_UNDER_CONSTRUCTION_OSM = file( + "/osm/herrenberg-hindenburgstr-under-construction.osm.pbf" ); - public static final File ISLAND_PRUNE_OSM = ResourceLoader.file( - "/germany/herrenberg-island-prune-nothru.osm.pbf" + public static final File HERRENBERG_BARRIER_GATES_OSM = file( + "/osm/herrenberg-barrier-gates.osm.pbf" ); - public static final File ADAPTIVE_PRUNE_OSM = ResourceLoader.file("/osm/isoiiluoto.pbf"); + public static final File HERRENBERG_OSM = file("/osm/herrenberg-minimal.osm.pbf"); + public static final File ISLAND_PRUNE_OSM = file("/osm/herrenberg-island-prune-nothru.osm.pbf"); + public static final File ADAPTIVE_PRUNE_OSM = file("/osm/isoiiluoto.pbf"); /* filenames encoded with cp437 and utf8 */ public static final String UMLAUT_CP437_ZIP = "src/test/resources/umlaut-cp437.zip"; diff --git a/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/AdaptivePruningTest.java b/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/AdaptivePruningTest.java index 56ac1cae22d..8768ffbbcb0 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/AdaptivePruningTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/AdaptivePruningTest.java @@ -71,7 +71,7 @@ private static Graph buildOsmGraph(File file) { var graph = new Graph(deduplicator); var transitModel = new TransitModel(new StopModel(), deduplicator); // Add street data from OSM - OsmProvider osmProvider = new OsmProvider(file , true); + OsmProvider osmProvider = new OsmProvider(file, true); OsmModule osmModule = OsmModule.of(osmProvider, graph).withEdgeNamer(new TestNamer()).build(); osmModule.buildGraph(); diff --git a/src/test/java/org/opentripplanner/openstreetmap/model/BicycleNetworkRelationsTest.java b/src/test/java/org/opentripplanner/openstreetmap/model/BicycleNetworkRelationsTest.java index 96b8a13ebf8..dafaacf3e01 100644 --- a/src/test/java/org/opentripplanner/openstreetmap/model/BicycleNetworkRelationsTest.java +++ b/src/test/java/org/opentripplanner/openstreetmap/model/BicycleNetworkRelationsTest.java @@ -9,6 +9,7 @@ import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.graph_builder.module.osm.OsmDatabase; import org.opentripplanner.openstreetmap.OsmProvider; +import org.opentripplanner.test.support.ResourceLoader; public class BicycleNetworkRelationsTest { @@ -20,10 +21,7 @@ public class BicycleNetworkRelationsTest { public void testBicycleRouteRelations() { var issueStore = DataImportIssueStore.NOOP; var osmdb = new OsmDatabase(issueStore, Set.of()); - var provider = new OsmProvider( - new File("src/test/resources/germany/ehningen-minimal.osm.pbf"), - true - ); + var provider = new OsmProvider(ResourceLoader.file("/osm/ehningen-minimal.osm.pbf"), true); provider.readOSM(osmdb); osmdb.postLoad(); diff --git a/src/test/java/org/opentripplanner/test/support/ResourceLoader.java b/src/test/java/org/opentripplanner/test/support/ResourceLoader.java index 96c04b05e55..98be37fa1ef 100644 --- a/src/test/java/org/opentripplanner/test/support/ResourceLoader.java +++ b/src/test/java/org/opentripplanner/test/support/ResourceLoader.java @@ -1,6 +1,7 @@ package org.opentripplanner.test.support; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.net.URL; @@ -17,7 +18,10 @@ public class ResourceLoader { */ public static File file(String path) { final URL resource = ResourceLoader.class.getResource(path); - assertNotNull(resource, "Resource %s not found on file system".formatted(path)); - return new File(URLDecoder.decode(resource.getFile(), StandardCharsets.UTF_8)); + final String msg = "Resource %s not found on file system".formatted(path); + assertNotNull(resource, msg); + var file = new File(URLDecoder.decode(resource.getFile(), StandardCharsets.UTF_8)); + assertTrue(file.exists(), msg); + return file; } } diff --git a/src/test/resources/germany/vvs-bus-751-only.gtfs.zip b/src/test/resources/gtfs/vvs-bus-751-only.gtfs.zip similarity index 100% rename from src/test/resources/germany/vvs-bus-751-only.gtfs.zip rename to src/test/resources/gtfs/vvs-bus-751-only.gtfs.zip diff --git a/src/test/resources/germany/vvs-bus-764-only.gtfs.zip b/src/test/resources/gtfs/vvs-bus-764-only.gtfs.zip similarity index 100% rename from src/test/resources/germany/vvs-bus-764-only.gtfs.zip rename to src/test/resources/gtfs/vvs-bus-764-only.gtfs.zip diff --git a/src/test/resources/germany/boeblingen-minimal.osm.pbf b/src/test/resources/osm/boeblingen-minimal.osm.pbf similarity index 100% rename from src/test/resources/germany/boeblingen-minimal.osm.pbf rename to src/test/resources/osm/boeblingen-minimal.osm.pbf diff --git a/src/test/resources/germany/deufringen-minimal.osm.pbf b/src/test/resources/osm/deufringen-minimal.osm.pbf similarity index 100% rename from src/test/resources/germany/deufringen-minimal.osm.pbf rename to src/test/resources/osm/deufringen-minimal.osm.pbf diff --git a/src/test/resources/germany/ehningen-minimal.osm.pbf b/src/test/resources/osm/ehningen-minimal.osm.pbf similarity index 100% rename from src/test/resources/germany/ehningen-minimal.osm.pbf rename to src/test/resources/osm/ehningen-minimal.osm.pbf diff --git a/src/test/resources/germany/herrenberg-barrier-gates.osm.pbf b/src/test/resources/osm/herrenberg-barrier-gates.osm.pbf similarity index 100% rename from src/test/resources/germany/herrenberg-barrier-gates.osm.pbf rename to src/test/resources/osm/herrenberg-barrier-gates.osm.pbf diff --git a/src/test/resources/germany/herrenberg-hindenburgstr-under-construction.osm.pbf b/src/test/resources/osm/herrenberg-hindenburgstr-under-construction.osm.pbf similarity index 100% rename from src/test/resources/germany/herrenberg-hindenburgstr-under-construction.osm.pbf rename to src/test/resources/osm/herrenberg-hindenburgstr-under-construction.osm.pbf diff --git a/src/test/resources/germany/herrenberg-island-prune-nothru.osm.pbf b/src/test/resources/osm/herrenberg-island-prune-nothru.osm.pbf similarity index 100% rename from src/test/resources/germany/herrenberg-island-prune-nothru.osm.pbf rename to src/test/resources/osm/herrenberg-island-prune-nothru.osm.pbf diff --git a/src/test/resources/germany/herrenberg-minimal.osm.pbf b/src/test/resources/osm/herrenberg-minimal.osm.pbf similarity index 100% rename from src/test/resources/germany/herrenberg-minimal.osm.pbf rename to src/test/resources/osm/herrenberg-minimal.osm.pbf From 36e483b897a5bbe5743f1f955b27939cdcc459dc Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 16 Aug 2023 13:35:29 +0200 Subject: [PATCH 04/95] Clean up test URL generation --- .../graph_builder/module/GtfsModuleTest.java | 4 +-- .../module/osm/OsmModuleTest.java | 2 +- .../test/support/ResourceLoader.java | 20 +++++++++------ .../GtfsRealtimeTripUpdateSourceTest.java | 3 ++- .../VehiclePositionParsingTest.java | 25 ++++++++++++------- 5 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/test/java/org/opentripplanner/graph_builder/module/GtfsModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/GtfsModuleTest.java index 61b171d6485..63e2a9b1cc7 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/GtfsModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/GtfsModuleTest.java @@ -3,7 +3,6 @@ import static graphql.Assert.assertNotNull; import static org.junit.jupiter.api.Assertions.assertEquals; -import java.io.File; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -16,6 +15,7 @@ import org.opentripplanner.gtfs.graphbuilder.GtfsModule; import org.opentripplanner.model.calendar.ServiceDateInterval; import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.test.support.ResourceLoader; import org.opentripplanner.test.support.VariableSource; import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.service.StopModel; @@ -68,7 +68,7 @@ record TestModels(Graph graph, TransitModel transitModel) {} class Interlining { static GtfsBundle bundle(String feedId) { - var b = new GtfsBundle(new File("src/test/resources/gtfs/interlining")); + var b = new GtfsBundle(ResourceLoader.file("/gtfs/interlining")); b.setFeedId(new GtfsFeedId.Builder().id(feedId).build()); return b; } diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java index cd5952a4b9f..b4ee592d766 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java @@ -301,7 +301,7 @@ private Graph buildParkingLots() { var graph = new Graph(); var providers = Stream .of("B+R.osm.pbf", "P+R.osm.pbf") - .map(f -> new File(getClass().getResource("/osm/" + f).getFile())) + .map(f -> ResourceLoader.file("/osm/" + f)) .map(f -> new OsmProvider(f, false)) .toList(); var module = OsmModule diff --git a/src/test/java/org/opentripplanner/test/support/ResourceLoader.java b/src/test/java/org/opentripplanner/test/support/ResourceLoader.java index 98be37fa1ef..e34fad33ed1 100644 --- a/src/test/java/org/opentripplanner/test/support/ResourceLoader.java +++ b/src/test/java/org/opentripplanner/test/support/ResourceLoader.java @@ -5,8 +5,6 @@ import java.io.File; import java.net.URL; -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; /** * Loads files from the resources folder. @@ -17,11 +15,19 @@ public class ResourceLoader { * Return a File instance for the given path. */ public static File file(String path) { - final URL resource = ResourceLoader.class.getResource(path); - final String msg = "Resource %s not found on file system".formatted(path); - assertNotNull(resource, msg); - var file = new File(URLDecoder.decode(resource.getFile(), StandardCharsets.UTF_8)); - assertTrue(file.exists(), msg); + URL resource = url(path); + var file = new File(resource.getFile()); + assertTrue(file.exists(), "File %s not found on file system".formatted(file.getAbsolutePath())); return file; } + + /** + * Return a URL for the given path. + */ + public static URL url(String s) { + var resource = ResourceLoader.class.getResource(s); + var msg = "Resource %s not found on file system".formatted(s); + assertNotNull(resource, msg); + return resource; + } } diff --git a/src/test/java/org/opentripplanner/updater/trip/GtfsRealtimeTripUpdateSourceTest.java b/src/test/java/org/opentripplanner/updater/trip/GtfsRealtimeTripUpdateSourceTest.java index 965dbee6b15..cd5e7ba4a53 100644 --- a/src/test/java/org/opentripplanner/updater/trip/GtfsRealtimeTripUpdateSourceTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/GtfsRealtimeTripUpdateSourceTest.java @@ -5,6 +5,7 @@ import java.time.Duration; import org.junit.jupiter.api.Test; +import org.opentripplanner.test.support.ResourceLoader; import org.opentripplanner.updater.spi.HttpHeaders; public class GtfsRealtimeTripUpdateSourceTest { @@ -18,7 +19,7 @@ public void parseFeed() { false, BackwardsDelayPropagationType.ALWAYS, "rt", - "file:src/test/resources/gtfs-rt/trip-updates/septa.pbf", + ResourceLoader.url("/gtfs-rt/trip-updates/septa.pbf").toString(), HttpHeaders.empty() ) ); diff --git a/src/test/java/org/opentripplanner/updater/vehicle_position/VehiclePositionParsingTest.java b/src/test/java/org/opentripplanner/updater/vehicle_position/VehiclePositionParsingTest.java index fecbcf79fcd..5cbeb5ac650 100644 --- a/src/test/java/org/opentripplanner/updater/vehicle_position/VehiclePositionParsingTest.java +++ b/src/test/java/org/opentripplanner/updater/vehicle_position/VehiclePositionParsingTest.java @@ -1,8 +1,11 @@ package org.opentripplanner.updater.vehicle_position; -import java.net.URI; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.net.URISyntaxException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.opentripplanner.test.support.ResourceLoader; import org.opentripplanner.updater.spi.HttpHeaders; public class VehiclePositionParsingTest { @@ -14,10 +17,10 @@ public void parseFirstPositionsFeed() { Assertions.assertNotNull(positions); - Assertions.assertEquals(627, positions.size()); + assertEquals(627, positions.size()); var first = positions.get(0); - Assertions.assertEquals("49195152", first.getTrip().getTripId()); + assertEquals("49195152", first.getTrip().getTripId()); } @Test @@ -27,16 +30,20 @@ public void parseSecondPositionsFeed() { Assertions.assertNotNull(positions); - Assertions.assertEquals(570, positions.size()); + assertEquals(570, positions.size()); var first = positions.get(0); - Assertions.assertEquals("49195157", first.getTrip().getTripId()); + assertEquals("49195157", first.getTrip().getTripId()); } private GtfsRealtimeHttpVehiclePositionSource getVehiclePositionSource(String filename) { - return new GtfsRealtimeHttpVehiclePositionSource( - URI.create("file:src/test/resources/gtfs-rt/vehicle-positions/" + filename), - HttpHeaders.empty() - ); + try { + return new GtfsRealtimeHttpVehiclePositionSource( + ResourceLoader.url("/gtfs-rt/vehicle-positions/" + filename).toURI(), + HttpHeaders.empty() + ); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } } } From c9c5c56102ea4a8145520848d6d1d10e81ab3c26 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 16 Aug 2023 14:05:44 +0200 Subject: [PATCH 05/95] Extract separate method for loading OSM files --- .../java/org/opentripplanner/ConstantsForTests.java | 11 ++++++----- .../graph_builder/module/FakeGraph.java | 3 +-- .../module/osm/OpenStreetMapParserTest.java | 4 +--- .../graph_builder/module/osm/OsmModuleTest.java | 8 ++++---- .../graph_builder/module/osm/PlatformLinkerTest.java | 2 +- .../module/osm/TriangleInequalityTest.java | 2 +- .../graph_builder/module/osm/UnroutableTest.java | 2 +- .../module/osm/WalkableAreaBuilderTest.java | 2 +- .../model/BicycleNetworkRelationsTest.java | 5 ++--- .../opentripplanner/test/support/ResourceLoader.java | 7 +++++++ 10 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/test/java/org/opentripplanner/ConstantsForTests.java b/src/test/java/org/opentripplanner/ConstantsForTests.java index 686a0d120cf..fc48b2236e9 100644 --- a/src/test/java/org/opentripplanner/ConstantsForTests.java +++ b/src/test/java/org/opentripplanner/ConstantsForTests.java @@ -1,6 +1,7 @@ package org.opentripplanner; import static org.opentripplanner.test.support.ResourceLoader.file; +import static org.opentripplanner.test.support.ResourceLoader.osmFile; import com.csvreader.CsvReader; import java.io.File; @@ -78,8 +79,8 @@ public class ConstantsForTests { private static final String NETEX_EPIP_DIR = "src/test/resources/netex/epip/"; private static final String NETEX_EPIP_DATA_DIR = NETEX_EPIP_DIR + "netex_epip_minimal/"; /* Stuttgart area, Germany */ - public static final File DEUFRINGEN_OSM = file("/osm/deufringen-minimal.osm.pbf"); - public static final File BOEBLINGEN_OSM = file("/osm/boeblingen-minimal.osm.pbf"); + public static final File DEUFRINGEN_OSM = osmFile("deufringen-minimal.osm.pbf"); + public static final File BOEBLINGEN_OSM = osmFile("boeblingen-minimal.osm.pbf"); public static final File VVS_BUS_764_ONLY = file("/gtfs/vvs-bus-764-only.gtfs.zip"); public static final File VVS_BUS_751_ONLY = file("/gtfs/vvs-bus-751-only.gtfs.zip"); public static final File HERRENBERG_HINDENBURG_STR_UNDER_CONSTRUCTION_OSM = file( @@ -88,9 +89,9 @@ public class ConstantsForTests { public static final File HERRENBERG_BARRIER_GATES_OSM = file( "/osm/herrenberg-barrier-gates.osm.pbf" ); - public static final File HERRENBERG_OSM = file("/osm/herrenberg-minimal.osm.pbf"); - public static final File ISLAND_PRUNE_OSM = file("/osm/herrenberg-island-prune-nothru.osm.pbf"); - public static final File ADAPTIVE_PRUNE_OSM = file("/osm/isoiiluoto.pbf"); + public static final File HERRENBERG_OSM = osmFile("herrenberg-minimal.osm.pbf"); + public static final File ISLAND_PRUNE_OSM = osmFile("herrenberg-island-prune-nothru.osm.pbf"); + public static final File ADAPTIVE_PRUNE_OSM = osmFile("isoiiluoto.pbf"); /* filenames encoded with cp437 and utf8 */ public static final String UMLAUT_CP437_ZIP = "src/test/resources/umlaut-cp437.zip"; diff --git a/src/test/java/org/opentripplanner/graph_builder/module/FakeGraph.java b/src/test/java/org/opentripplanner/graph_builder/module/FakeGraph.java index 160a6e9c87e..3e6d9c5358e 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/FakeGraph.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/FakeGraph.java @@ -1,7 +1,6 @@ package org.opentripplanner.graph_builder.module; import java.io.File; -import java.net.URISyntaxException; import java.util.List; import org.opentripplanner.TestOtpModel; import org.opentripplanner.graph_builder.module.osm.OsmModule; @@ -37,7 +36,7 @@ public static TestOtpModel buildGraphNoTransit() { var gg = new Graph(deduplicator); var transitModel = new TransitModel(stopModel, deduplicator); - File file = ResourceLoader.file("/osm/columbus.osm.pbf"); + File file = ResourceLoader.osmFile("columbus.osm.pbf"); OsmProvider provider = new OsmProvider(file, false); OsmModule osmModule = OsmModule.of(provider, gg).build(); diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/OpenStreetMapParserTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/OpenStreetMapParserTest.java index 9b8f763dde6..9556614b756 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/OpenStreetMapParserTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/OpenStreetMapParserTest.java @@ -5,8 +5,6 @@ import gnu.trove.list.TLongList; import java.io.File; -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; import java.util.Set; import org.junit.jupiter.api.Test; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; @@ -19,7 +17,7 @@ public class OpenStreetMapParserTest { @Test public void testBinaryParser() { - File osmFile = ResourceLoader.file("/osm/map.osm.pbf"); + File osmFile = ResourceLoader.osmFile("map.osm.pbf"); OsmProvider pr = new OsmProvider(osmFile, true); OsmDatabase osmdb = new OsmDatabase(DataImportIssueStore.NOOP, Set.of()); diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java index b4ee592d766..206ff31ae45 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java @@ -50,7 +50,7 @@ public void testGraphBuilder() { var deduplicator = new Deduplicator(); var gg = new Graph(deduplicator); - File file = ResourceLoader.file("/osm/map.osm.pbf"); + File file = ResourceLoader.osmFile("map.osm.pbf"); OsmProvider provider = new OsmProvider(file, true); @@ -108,7 +108,7 @@ public void testBuildGraphDetailed() { var deduplicator = new Deduplicator(); var gg = new Graph(deduplicator); - File file = ResourceLoader.file("/osm/NYC_small.osm.pbf"); + File file = ResourceLoader.osmFile("NYC_small.osm.pbf"); OsmProvider provider = new OsmProvider(file, true); OsmModule osmModule = OsmModule.of(provider, gg).withAreaVisibility(true).build(); @@ -301,7 +301,7 @@ private Graph buildParkingLots() { var graph = new Graph(); var providers = Stream .of("B+R.osm.pbf", "P+R.osm.pbf") - .map(f -> ResourceLoader.file("/osm/" + f)) + .map(ResourceLoader::osmFile) .map(f -> new OsmProvider(f, false)) .toList(); var module = OsmModule @@ -326,7 +326,7 @@ private void testBuildingAreas(boolean skipVisibility) { var deduplicator = new Deduplicator(); var graph = new Graph(deduplicator); - File file = ResourceLoader.file("/osm/usf_area.osm.pbf"); + File file = ResourceLoader.osmFile("usf_area.osm.pbf"); OsmProvider provider = new OsmProvider(file, false); OsmModule loader = OsmModule.of(provider, graph).withAreaVisibility(!skipVisibility).build(); diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/PlatformLinkerTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/PlatformLinkerTest.java index fbe0fe7e778..fc64c93e3e2 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/PlatformLinkerTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/PlatformLinkerTest.java @@ -25,7 +25,7 @@ public void testLinkEntriesToPlatforms() { var deduplicator = new Deduplicator(); var gg = new Graph(deduplicator); - File file = ResourceLoader.file("/osm/skoyen.osm.pbf"); + File file = ResourceLoader.osmFile("skoyen.osm.pbf"); OsmProvider provider = new OsmProvider(file, false); diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/TriangleInequalityTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/TriangleInequalityTest.java index c1999c0d479..3f665faa245 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/TriangleInequalityTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/TriangleInequalityTest.java @@ -48,7 +48,7 @@ public class TriangleInequalityTest { public static void onlyOnce() { graph = new Graph(new Deduplicator()); - File file = ResourceLoader.file("/osm/NYC_small.osm.pbf"); + File file = ResourceLoader.osmFile("NYC_small.osm.pbf"); OsmProvider provider = new OsmProvider(file, true); OsmModule osmModule = OsmModule.of(provider, graph).withAreaVisibility(true).build(); osmModule.buildGraph(); diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/UnroutableTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/UnroutableTest.java index 4bc5c537e88..2ccdad60b32 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/UnroutableTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/UnroutableTest.java @@ -35,7 +35,7 @@ public void setUp() throws Exception { var deduplicator = new Deduplicator(); graph = new Graph(deduplicator); - var osmDataFile = ResourceLoader.file("/osm/bridge_construction.osm.pbf"); + var osmDataFile = ResourceLoader.osmFile("bridge_construction.osm.pbf"); OsmProvider provider = new OsmProvider(osmDataFile, true); OsmModule osmBuilder = OsmModule.of(provider, graph).withAreaVisibility(true).build(); osmBuilder.buildGraph(); diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilderTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilderTest.java index f4a407e3e1e..e34c623473b 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilderTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilderTest.java @@ -44,7 +44,7 @@ public void testSetup(final TestInfo testInfo) { final Set boardingAreaRefTags = Set.of(); final OsmDatabase osmdb = new OsmDatabase(DataImportIssueStore.NOOP, boardingAreaRefTags); - final File file = ResourceLoader.file("/osm/" + osmFile); + final File file = ResourceLoader.osmFile(osmFile); new OsmProvider(file, true).readOSM(osmdb); osmdb.postLoad(); diff --git a/src/test/java/org/opentripplanner/openstreetmap/model/BicycleNetworkRelationsTest.java b/src/test/java/org/opentripplanner/openstreetmap/model/BicycleNetworkRelationsTest.java index dafaacf3e01..d1ad7301fa6 100644 --- a/src/test/java/org/opentripplanner/openstreetmap/model/BicycleNetworkRelationsTest.java +++ b/src/test/java/org/opentripplanner/openstreetmap/model/BicycleNetworkRelationsTest.java @@ -2,14 +2,13 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.opentripplanner.test.support.ResourceLoader.osmFile; -import java.io.File; import java.util.Set; import org.junit.jupiter.api.Test; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.graph_builder.module.osm.OsmDatabase; import org.opentripplanner.openstreetmap.OsmProvider; -import org.opentripplanner.test.support.ResourceLoader; public class BicycleNetworkRelationsTest { @@ -21,7 +20,7 @@ public class BicycleNetworkRelationsTest { public void testBicycleRouteRelations() { var issueStore = DataImportIssueStore.NOOP; var osmdb = new OsmDatabase(issueStore, Set.of()); - var provider = new OsmProvider(ResourceLoader.file("/osm/ehningen-minimal.osm.pbf"), true); + var provider = new OsmProvider(osmFile("ehningen-minimal.osm.pbf"), true); provider.readOSM(osmdb); osmdb.postLoad(); diff --git a/src/test/java/org/opentripplanner/test/support/ResourceLoader.java b/src/test/java/org/opentripplanner/test/support/ResourceLoader.java index e34fad33ed1..b70fab88c7f 100644 --- a/src/test/java/org/opentripplanner/test/support/ResourceLoader.java +++ b/src/test/java/org/opentripplanner/test/support/ResourceLoader.java @@ -21,6 +21,13 @@ public static File file(String path) { return file; } + /** + * Return a File instance for the given name from the /osm subfolder. + */ + public static File osmFile(String osmFile) { + return file("/osm/" + osmFile); + } + /** * Return a URL for the given path. */ From 42a9beacf9b8962405ae1dcce63aafa819d4cbd2 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 16 Aug 2023 15:16:27 +0200 Subject: [PATCH 06/95] Use File directly in FlexTest --- .../ext/flex/FlexIntegrationTest.java | 13 ++++++------ .../opentripplanner/ext/flex/FlexTest.java | 20 +++++++++--------- .../opentripplanner/ConstantsForTests.java | 8 +++---- .../osm/otp-multipolygon-test.osm.pbf | Bin 528 -> 0 bytes 4 files changed, 20 insertions(+), 21 deletions(-) delete mode 100644 src/test/resources/osm/otp-multipolygon-test.osm.pbf diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/FlexIntegrationTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/FlexIntegrationTest.java index cf55a1a2f58..8a927673eca 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/FlexIntegrationTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/FlexIntegrationTest.java @@ -57,16 +57,15 @@ public class FlexIntegrationTest { @BeforeAll static void setup() { OTPFeature.enableFeatures(Map.of(OTPFeature.FlexRouting, true)); - var osmPath = ResourceLoader.file(FlexTest.COBB_OSM); - var cobblincGtfsPath = ResourceLoader.file(FlexTest.COBB_BUS_30_GTFS); - var martaGtfsPath = ResourceLoader.file(FlexTest.MARTA_BUS_856_GTFS); - var flexGtfsPath = ResourceLoader.file(FlexTest.COBB_FLEX_GTFS); - - TestOtpModel model = ConstantsForTests.buildOsmGraph(osmPath); + TestOtpModel model = ConstantsForTests.buildOsmGraph(FlexTest.COBB_OSM); graph = model.graph(); transitModel = model.transitModel(); - addGtfsToGraph(graph, transitModel, List.of(cobblincGtfsPath, martaGtfsPath, flexGtfsPath)); + addGtfsToGraph( + graph, + transitModel, + List.of(FlexTest.COBB_BUS_30_GTFS, FlexTest.MARTA_BUS_856_GTFS, FlexTest.COBB_FLEX_GTFS) + ); service = TestServerContext.createServerContext(graph, transitModel).routingService(); } diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/FlexTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/FlexTest.java index 2170821a21e..e8cd77d3ff6 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/FlexTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/FlexTest.java @@ -1,6 +1,7 @@ package org.opentripplanner.ext.flex; import static graphql.Assert.assertTrue; +import static org.opentripplanner.test.support.ResourceLoader.file; import gnu.trove.set.hash.TIntHashSet; import java.io.File; @@ -15,19 +16,20 @@ import org.opentripplanner.gtfs.graphbuilder.GtfsModule; import org.opentripplanner.model.calendar.ServiceDateInterval; import org.opentripplanner.routing.graph.Graph; -import org.opentripplanner.test.support.ResourceLoader; import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.service.StopModel; import org.opentripplanner.transit.service.TransitModel; public abstract class FlexTest { - protected static final String ASPEN_GTFS = "/flex/aspen-flex-on-demand.gtfs.zip"; - protected static final String COBB_FLEX_GTFS = "/flex/cobblinc-scheduled-deviated-flex.gtfs.zip"; - protected static final String COBB_BUS_30_GTFS = "/flex/cobblinc-bus-30-only.gtfs.zip"; - protected static final String MARTA_BUS_856_GTFS = "/flex/marta-bus-856-only.gtfs.zip"; - protected static final String LINCOLN_COUNTY_GBFS = "/flex/lincoln-county-flex.gtfs.zip"; - protected static final String COBB_OSM = "/flex/cobb-county.filtered.osm.pbf"; + protected static final File ASPEN_GTFS = file("/flex/aspen-flex-on-demand.gtfs.zip"); + protected static final File COBB_FLEX_GTFS = file( + "/flex/cobblinc-scheduled-deviated-flex.gtfs.zip" + ); + protected static final File COBB_BUS_30_GTFS = file("/flex/cobblinc-bus-30-only.gtfs.zip"); + protected static final File MARTA_BUS_856_GTFS = file("/flex/marta-bus-856-only.gtfs.zip"); + protected static final File LINCOLN_COUNTY_GBFS = file("/flex/lincoln-county-flex.gtfs.zip"); + protected static final File COBB_OSM = file("/flex/cobb-county.filtered.osm.pbf"); protected static final DirectFlexPathCalculator calculator = new DirectFlexPathCalculator(); protected static final LocalDate serviceDate = LocalDate.of(2021, 4, 11); @@ -38,9 +40,7 @@ public abstract class FlexTest { new TIntHashSet() ); - protected static TestOtpModel buildFlexGraph(String fileName) { - File file = ResourceLoader.file(fileName); - + protected static TestOtpModel buildFlexGraph(File file) { var deduplicator = new Deduplicator(); var graph = new Graph(deduplicator); var transitModel = new TransitModel(new StopModel(), deduplicator); diff --git a/src/test/java/org/opentripplanner/ConstantsForTests.java b/src/test/java/org/opentripplanner/ConstantsForTests.java index fc48b2236e9..2d8f0ad626d 100644 --- a/src/test/java/org/opentripplanner/ConstantsForTests.java +++ b/src/test/java/org/opentripplanner/ConstantsForTests.java @@ -83,11 +83,11 @@ public class ConstantsForTests { public static final File BOEBLINGEN_OSM = osmFile("boeblingen-minimal.osm.pbf"); public static final File VVS_BUS_764_ONLY = file("/gtfs/vvs-bus-764-only.gtfs.zip"); public static final File VVS_BUS_751_ONLY = file("/gtfs/vvs-bus-751-only.gtfs.zip"); - public static final File HERRENBERG_HINDENBURG_STR_UNDER_CONSTRUCTION_OSM = file( - "/osm/herrenberg-hindenburgstr-under-construction.osm.pbf" + public static final File HERRENBERG_HINDENBURG_STR_UNDER_CONSTRUCTION_OSM = osmFile( + "herrenberg-hindenburgstr-under-construction.osm.pbf" ); - public static final File HERRENBERG_BARRIER_GATES_OSM = file( - "/osm/herrenberg-barrier-gates.osm.pbf" + public static final File HERRENBERG_BARRIER_GATES_OSM = osmFile( + "herrenberg-barrier-gates.osm.pbf" ); public static final File HERRENBERG_OSM = osmFile("herrenberg-minimal.osm.pbf"); public static final File ISLAND_PRUNE_OSM = osmFile("herrenberg-island-prune-nothru.osm.pbf"); diff --git a/src/test/resources/osm/otp-multipolygon-test.osm.pbf b/src/test/resources/osm/otp-multipolygon-test.osm.pbf deleted file mode 100644 index 754c98f1b6fc86831658f50893f0e0128289f53a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 528 zcmZQzVBqEA^bhv+NKH&hEt0Sh(2+8!m=pZyv;H|=zjGdEeXsBuUoZ}SROES9+wbdX ze^1@Kq$g+f&z;pdd)54kvB?Ev1_pzfyul0%3_M)yK!aQoOA;kEFbce4lv>4DG3Rm8 z!=wjE0#zq8O8O^yKiFg`%`MK&J()pRc(TBRYh6zsr8}@X9nxJd|MroK=oF{$r7e03 zj-GGPda$HCx%|1|v)$7r)1Uj-pWO6L_;zRVoMQWpf9Ia7d99y(T5Z|udP(zjN!10t zUoS~-TYWuZ@0V?Fe{Y+*ad-dI7xKqawIj^_ip;3%Nq+3#SY5p1{GB9Ai`g%~=uWdW z+WhUKU(8{%V)=Z5UEB;`EG)}_?&n6hzgS=oqf{Qy^+JzM3H!lbpD2 zZl7grWL(_kv9chiW%bbz)BAHKP7J)xvLcILF7&Lz<~Ms8{O8!4qPn0=AYZBo=zvX6 zkR718Qpe-zBaIWDI-aNXPd$BfQunOpNu3ktSNfbk&#aYlR$tds_fgW>GpD_Eg^YrX z9z=Y7V$Wj0WoT&RX=u1v<71%#hs>>AAN9Ifnwgr}RtpPD?-noKy)#2VSXr52{TW3A E07{qT1^@s6 From 8c90fb362379a3dfeddd3cbe014ae5d6a2c3391e Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 17 Aug 2023 09:50:50 +0200 Subject: [PATCH 07/95] Clean up URI handling --- .../test/support/ResourceLoader.java | 21 ++++++++++++++---- .../VehiclePositionParsingTest.java | 17 +++++--------- ...nty-metro-1.pb => king-county-metro-1.pbf} | Bin ...nty-metro-2.pb => king-county-metro-2.pbf} | Bin 4 files changed, 23 insertions(+), 15 deletions(-) rename src/test/resources/gtfs-rt/vehicle-positions/{king-county-metro-1.pb => king-county-metro-1.pbf} (100%) rename src/test/resources/gtfs-rt/vehicle-positions/{king-county-metro-2.pb => king-county-metro-2.pbf} (100%) diff --git a/src/test/java/org/opentripplanner/test/support/ResourceLoader.java b/src/test/java/org/opentripplanner/test/support/ResourceLoader.java index b70fab88c7f..d79cf2add45 100644 --- a/src/test/java/org/opentripplanner/test/support/ResourceLoader.java +++ b/src/test/java/org/opentripplanner/test/support/ResourceLoader.java @@ -4,6 +4,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; /** @@ -29,12 +31,23 @@ public static File osmFile(String osmFile) { } /** - * Return a URL for the given path. + * Return a URL for the given resource. */ - public static URL url(String s) { - var resource = ResourceLoader.class.getResource(s); - var msg = "Resource %s not found on file system".formatted(s); + public static URL url(String name) { + var resource = ResourceLoader.class.getResource(name); + var msg = "Resource %s not found on file system".formatted(resource); assertNotNull(resource, msg); return resource; } + + /** + * Return a URI for the given resource. + */ + public static URI uri(String s) { + try { + return url(s).toURI(); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e); + } + } } diff --git a/src/test/java/org/opentripplanner/updater/vehicle_position/VehiclePositionParsingTest.java b/src/test/java/org/opentripplanner/updater/vehicle_position/VehiclePositionParsingTest.java index 5cbeb5ac650..5b4a38833fa 100644 --- a/src/test/java/org/opentripplanner/updater/vehicle_position/VehiclePositionParsingTest.java +++ b/src/test/java/org/opentripplanner/updater/vehicle_position/VehiclePositionParsingTest.java @@ -2,7 +2,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import java.net.URISyntaxException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.opentripplanner.test.support.ResourceLoader; @@ -12,7 +11,7 @@ public class VehiclePositionParsingTest { @Test public void parseFirstPositionsFeed() { - var vehiclePositionSource = getVehiclePositionSource("king-county-metro-1.pb"); + var vehiclePositionSource = getVehiclePositionSource("king-county-metro-1.pbf"); var positions = vehiclePositionSource.getPositions(); Assertions.assertNotNull(positions); @@ -25,7 +24,7 @@ public void parseFirstPositionsFeed() { @Test public void parseSecondPositionsFeed() { - var vehiclePositionSource = getVehiclePositionSource("king-county-metro-2.pb"); + var vehiclePositionSource = getVehiclePositionSource("king-county-metro-2.pbf"); var positions = vehiclePositionSource.getPositions(); Assertions.assertNotNull(positions); @@ -37,13 +36,9 @@ public void parseSecondPositionsFeed() { } private GtfsRealtimeHttpVehiclePositionSource getVehiclePositionSource(String filename) { - try { - return new GtfsRealtimeHttpVehiclePositionSource( - ResourceLoader.url("/gtfs-rt/vehicle-positions/" + filename).toURI(), - HttpHeaders.empty() - ); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } + return new GtfsRealtimeHttpVehiclePositionSource( + ResourceLoader.uri("/gtfs-rt/vehicle-positions/" + filename), + HttpHeaders.empty() + ); } } diff --git a/src/test/resources/gtfs-rt/vehicle-positions/king-county-metro-1.pb b/src/test/resources/gtfs-rt/vehicle-positions/king-county-metro-1.pbf similarity index 100% rename from src/test/resources/gtfs-rt/vehicle-positions/king-county-metro-1.pb rename to src/test/resources/gtfs-rt/vehicle-positions/king-county-metro-1.pbf diff --git a/src/test/resources/gtfs-rt/vehicle-positions/king-county-metro-2.pb b/src/test/resources/gtfs-rt/vehicle-positions/king-county-metro-2.pbf similarity index 100% rename from src/test/resources/gtfs-rt/vehicle-positions/king-county-metro-2.pb rename to src/test/resources/gtfs-rt/vehicle-positions/king-county-metro-2.pbf From b5d1a9cf20b4e716008185c720f1699414b01883 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 17 Aug 2023 10:40:56 +0200 Subject: [PATCH 08/95] Rename 'testagency' to 'simple' --- src/test/java/org/opentripplanner/ConstantsForTests.java | 2 +- .../opentripplanner/graph_builder/module/GtfsModuleTest.java | 2 +- .../java/org/opentripplanner/model/TimetableSnapshotTest.java | 2 +- src/test/java/org/opentripplanner/model/TimetableTest.java | 2 +- .../calendar/impl/CalendarServiceDataFactoryImplTest.java | 2 +- .../model/impl/OtpTransitServiceBuilderTest.java | 2 +- .../opentripplanner/model/impl/OtpTransitServiceImplTest.java | 2 +- .../routing/graph/DefaultRoutingServiceTest.java | 2 +- .../routing/stoptimes/AlternativeLegsTest.java | 2 +- .../routing/stoptimes/StopTimesHelperTest.java | 2 +- .../org/opentripplanner/transit/service/TransitModelTest.java | 4 ++-- .../updater/trip/TimetableSnapshotSourceTest.java | 2 +- src/test/resources/gtfs/{testagency => simple}/agency.txt | 0 src/test/resources/gtfs/{testagency => simple}/calendar.txt | 0 .../resources/gtfs/{testagency => simple}/frequencies.txt | 0 src/test/resources/gtfs/{testagency => simple}/pathways.txt | 0 src/test/resources/gtfs/{testagency => simple}/routes.txt | 0 src/test/resources/gtfs/{testagency => simple}/shapes.txt | 0 src/test/resources/gtfs/{testagency => simple}/stop_times.txt | 0 src/test/resources/gtfs/{testagency => simple}/stops.txt | 0 src/test/resources/gtfs/{testagency => simple}/transfers.txt | 0 src/test/resources/gtfs/{testagency => simple}/trips.txt | 0 22 files changed, 13 insertions(+), 13 deletions(-) rename src/test/resources/gtfs/{testagency => simple}/agency.txt (100%) rename src/test/resources/gtfs/{testagency => simple}/calendar.txt (100%) rename src/test/resources/gtfs/{testagency => simple}/frequencies.txt (100%) rename src/test/resources/gtfs/{testagency => simple}/pathways.txt (100%) rename src/test/resources/gtfs/{testagency => simple}/routes.txt (100%) rename src/test/resources/gtfs/{testagency => simple}/shapes.txt (100%) rename src/test/resources/gtfs/{testagency => simple}/stop_times.txt (100%) rename src/test/resources/gtfs/{testagency => simple}/stops.txt (100%) rename src/test/resources/gtfs/{testagency => simple}/transfers.txt (100%) rename src/test/resources/gtfs/{testagency => simple}/trips.txt (100%) diff --git a/src/test/java/org/opentripplanner/ConstantsForTests.java b/src/test/java/org/opentripplanner/ConstantsForTests.java index 2d8f0ad626d..463ea63f365 100644 --- a/src/test/java/org/opentripplanner/ConstantsForTests.java +++ b/src/test/java/org/opentripplanner/ConstantsForTests.java @@ -67,7 +67,7 @@ public class ConstantsForTests { public static final File KCM_GTFS = file("/gtfs/kcm_gtfs.zip"); - public static final File FAKE_GTFS = file("/gtfs/testagency/"); + public static final File SIMPLE_GTFS = file("/gtfs/simple/"); public static final File FARE_COMPONENT_GTFS = file("/gtfs/farecomponents.gtfs.zip"); diff --git a/src/test/java/org/opentripplanner/graph_builder/module/GtfsModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/GtfsModuleTest.java index 63e2a9b1cc7..c6d35846786 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/GtfsModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/GtfsModuleTest.java @@ -27,7 +27,7 @@ class GtfsModuleTest { public void addShapesForFrequencyTrips() { var model = buildTestModel(); - var bundle = new GtfsBundle(ConstantsForTests.FAKE_GTFS); + var bundle = new GtfsBundle(ConstantsForTests.SIMPLE_GTFS); var module = new GtfsModule( List.of(bundle), model.transitModel, diff --git a/src/test/java/org/opentripplanner/model/TimetableSnapshotTest.java b/src/test/java/org/opentripplanner/model/TimetableSnapshotTest.java index 9a0c8a171ee..d0cf9f95a81 100644 --- a/src/test/java/org/opentripplanner/model/TimetableSnapshotTest.java +++ b/src/test/java/org/opentripplanner/model/TimetableSnapshotTest.java @@ -36,7 +36,7 @@ public class TimetableSnapshotTest { @BeforeAll public static void setUp() throws Exception { - TestOtpModel model = ConstantsForTests.buildGtfsGraph(ConstantsForTests.FAKE_GTFS); + TestOtpModel model = ConstantsForTests.buildGtfsGraph(ConstantsForTests.SIMPLE_GTFS); TransitModel transitModel = model.transitModel(); feedId = transitModel.getFeedIds().iterator().next(); diff --git a/src/test/java/org/opentripplanner/model/TimetableTest.java b/src/test/java/org/opentripplanner/model/TimetableTest.java index 5c579415049..80084393c08 100644 --- a/src/test/java/org/opentripplanner/model/TimetableTest.java +++ b/src/test/java/org/opentripplanner/model/TimetableTest.java @@ -50,7 +50,7 @@ public class TimetableTest { @BeforeAll public static void setUp() throws Exception { - TestOtpModel model = ConstantsForTests.buildGtfsGraph(ConstantsForTests.FAKE_GTFS); + TestOtpModel model = ConstantsForTests.buildGtfsGraph(ConstantsForTests.SIMPLE_GTFS); TransitModel transitModel = model.transitModel(); feedId = transitModel.getFeedIds().stream().findFirst().get(); diff --git a/src/test/java/org/opentripplanner/model/calendar/impl/CalendarServiceDataFactoryImplTest.java b/src/test/java/org/opentripplanner/model/calendar/impl/CalendarServiceDataFactoryImplTest.java index 2eb5f060108..135a3045c3c 100644 --- a/src/test/java/org/opentripplanner/model/calendar/impl/CalendarServiceDataFactoryImplTest.java +++ b/src/test/java/org/opentripplanner/model/calendar/impl/CalendarServiceDataFactoryImplTest.java @@ -124,7 +124,7 @@ public void testServiceGetServiceDatesForServiceId() { private static GtfsContext createCtxBuilder() throws IOException { GtfsContextBuilder ctxBuilder = contextBuilder( TransitModelForTest.FEED_ID, - ConstantsForTests.FAKE_GTFS + ConstantsForTests.SIMPLE_GTFS ); OtpTransitServiceBuilder builder = ctxBuilder .withDataImportIssueStore(DataImportIssueStore.NOOP) diff --git a/src/test/java/org/opentripplanner/model/impl/OtpTransitServiceBuilderTest.java b/src/test/java/org/opentripplanner/model/impl/OtpTransitServiceBuilderTest.java index 070b4fe546d..9fd0bd4d8cb 100644 --- a/src/test/java/org/opentripplanner/model/impl/OtpTransitServiceBuilderTest.java +++ b/src/test/java/org/opentripplanner/model/impl/OtpTransitServiceBuilderTest.java @@ -88,7 +88,7 @@ public void testGetAllShapePoints() { /* private methods */ private static OtpTransitServiceBuilder createBuilder() throws IOException { - OtpTransitServiceBuilder builder = contextBuilder(FEED_ID, ConstantsForTests.FAKE_GTFS) + OtpTransitServiceBuilder builder = contextBuilder(FEED_ID, ConstantsForTests.SIMPLE_GTFS) .getTransitBuilder(); Agency agency = agency(builder); diff --git a/src/test/java/org/opentripplanner/model/impl/OtpTransitServiceImplTest.java b/src/test/java/org/opentripplanner/model/impl/OtpTransitServiceImplTest.java index a0031ca3384..9ecdbbeb451 100644 --- a/src/test/java/org/opentripplanner/model/impl/OtpTransitServiceImplTest.java +++ b/src/test/java/org/opentripplanner/model/impl/OtpTransitServiceImplTest.java @@ -38,7 +38,7 @@ public class OtpTransitServiceImplTest { @BeforeAll public static void setup() throws IOException { - GtfsContextBuilder contextBuilder = contextBuilder(FEED_ID, ConstantsForTests.FAKE_GTFS); + GtfsContextBuilder contextBuilder = contextBuilder(FEED_ID, ConstantsForTests.SIMPLE_GTFS); OtpTransitServiceBuilder builder = contextBuilder.getTransitBuilder(); // Supplement test data with at least one entity in all collections diff --git a/src/test/java/org/opentripplanner/routing/graph/DefaultRoutingServiceTest.java b/src/test/java/org/opentripplanner/routing/graph/DefaultRoutingServiceTest.java index 11b1e07866d..e9d286d5a92 100644 --- a/src/test/java/org/opentripplanner/routing/graph/DefaultRoutingServiceTest.java +++ b/src/test/java/org/opentripplanner/routing/graph/DefaultRoutingServiceTest.java @@ -29,7 +29,7 @@ public class DefaultRoutingServiceTest extends GtfsTest { @Override public String getFeedName() { - return "gtfs/testagency"; + return "gtfs/simple"; } @Test diff --git a/src/test/java/org/opentripplanner/routing/stoptimes/AlternativeLegsTest.java b/src/test/java/org/opentripplanner/routing/stoptimes/AlternativeLegsTest.java index 8aa18bbf503..db11e81dabc 100644 --- a/src/test/java/org/opentripplanner/routing/stoptimes/AlternativeLegsTest.java +++ b/src/test/java/org/opentripplanner/routing/stoptimes/AlternativeLegsTest.java @@ -23,7 +23,7 @@ class AlternativeLegsTest extends GtfsTest { @Override public String getFeedName() { - return "gtfs/testagency"; + return "gtfs/simple"; } @Test diff --git a/src/test/java/org/opentripplanner/routing/stoptimes/StopTimesHelperTest.java b/src/test/java/org/opentripplanner/routing/stoptimes/StopTimesHelperTest.java index 6158278e436..f9cf1898a19 100644 --- a/src/test/java/org/opentripplanner/routing/stoptimes/StopTimesHelperTest.java +++ b/src/test/java/org/opentripplanner/routing/stoptimes/StopTimesHelperTest.java @@ -28,7 +28,7 @@ class StopTimesHelperTest { @BeforeAll public static void setUp() throws Exception { - TestOtpModel model = ConstantsForTests.buildGtfsGraph(ConstantsForTests.FAKE_GTFS); + TestOtpModel model = ConstantsForTests.buildGtfsGraph(ConstantsForTests.SIMPLE_GTFS); TransitModel transitModel = model.transitModel(); transitService = new DefaultTransitService(transitModel); feedId = transitModel.getFeedIds().iterator().next(); diff --git a/src/test/java/org/opentripplanner/transit/service/TransitModelTest.java b/src/test/java/org/opentripplanner/transit/service/TransitModelTest.java index 835b27b780e..9f14760010f 100644 --- a/src/test/java/org/opentripplanner/transit/service/TransitModelTest.java +++ b/src/test/java/org/opentripplanner/transit/service/TransitModelTest.java @@ -30,7 +30,7 @@ void validateTimeZones() { ConstantsForTests.addGtfsToGraph( graph, transitModel, - ConstantsForTests.FAKE_GTFS, + ConstantsForTests.SIMPLE_GTFS, new DefaultFareServiceFactory(), FAKE_FEED_ID ); @@ -77,7 +77,7 @@ void validateTimeZonesWithExplicitTimeZone() { ConstantsForTests.addGtfsToGraph( graph, transitModel, - ConstantsForTests.FAKE_GTFS, + ConstantsForTests.SIMPLE_GTFS, new DefaultFareServiceFactory(), FAKE_FEED_ID ); diff --git a/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java b/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java index 044a5d18bca..a1e499e6d02 100644 --- a/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java @@ -66,7 +66,7 @@ public class TimetableSnapshotSourceTest { @BeforeEach public void setUp() { - TestOtpModel model = ConstantsForTests.buildGtfsGraph(ConstantsForTests.FAKE_GTFS); + TestOtpModel model = ConstantsForTests.buildGtfsGraph(ConstantsForTests.SIMPLE_GTFS); transitModel = model.transitModel(); feedId = transitModel.getFeedIds().stream().findFirst().get(); diff --git a/src/test/resources/gtfs/testagency/agency.txt b/src/test/resources/gtfs/simple/agency.txt similarity index 100% rename from src/test/resources/gtfs/testagency/agency.txt rename to src/test/resources/gtfs/simple/agency.txt diff --git a/src/test/resources/gtfs/testagency/calendar.txt b/src/test/resources/gtfs/simple/calendar.txt similarity index 100% rename from src/test/resources/gtfs/testagency/calendar.txt rename to src/test/resources/gtfs/simple/calendar.txt diff --git a/src/test/resources/gtfs/testagency/frequencies.txt b/src/test/resources/gtfs/simple/frequencies.txt similarity index 100% rename from src/test/resources/gtfs/testagency/frequencies.txt rename to src/test/resources/gtfs/simple/frequencies.txt diff --git a/src/test/resources/gtfs/testagency/pathways.txt b/src/test/resources/gtfs/simple/pathways.txt similarity index 100% rename from src/test/resources/gtfs/testagency/pathways.txt rename to src/test/resources/gtfs/simple/pathways.txt diff --git a/src/test/resources/gtfs/testagency/routes.txt b/src/test/resources/gtfs/simple/routes.txt similarity index 100% rename from src/test/resources/gtfs/testagency/routes.txt rename to src/test/resources/gtfs/simple/routes.txt diff --git a/src/test/resources/gtfs/testagency/shapes.txt b/src/test/resources/gtfs/simple/shapes.txt similarity index 100% rename from src/test/resources/gtfs/testagency/shapes.txt rename to src/test/resources/gtfs/simple/shapes.txt diff --git a/src/test/resources/gtfs/testagency/stop_times.txt b/src/test/resources/gtfs/simple/stop_times.txt similarity index 100% rename from src/test/resources/gtfs/testagency/stop_times.txt rename to src/test/resources/gtfs/simple/stop_times.txt diff --git a/src/test/resources/gtfs/testagency/stops.txt b/src/test/resources/gtfs/simple/stops.txt similarity index 100% rename from src/test/resources/gtfs/testagency/stops.txt rename to src/test/resources/gtfs/simple/stops.txt diff --git a/src/test/resources/gtfs/testagency/transfers.txt b/src/test/resources/gtfs/simple/transfers.txt similarity index 100% rename from src/test/resources/gtfs/testagency/transfers.txt rename to src/test/resources/gtfs/simple/transfers.txt diff --git a/src/test/resources/gtfs/testagency/trips.txt b/src/test/resources/gtfs/simple/trips.txt similarity index 100% rename from src/test/resources/gtfs/testagency/trips.txt rename to src/test/resources/gtfs/simple/trips.txt From 659ea0db705aec1ab667f7beebfc2c6d93158dd6 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Mon, 28 Aug 2023 15:35:33 +0200 Subject: [PATCH 09/95] Fix flex access itinerary mapping --- .../mapping/RaptorPathToItineraryMapper.java | 1 + .../RaptorPathToItineraryMapperTest.java | 86 ++++++++++++++++++- 2 files changed, 83 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapper.java index 497bac3bbfd..7bd8b474529 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapper.java @@ -351,6 +351,7 @@ private ZonedDateTime createZonedDateTime(int timeInSeconds) { */ private boolean includeTransferInItinerary(Leg transitLegBeforeTransfer) { return ( + transitLegBeforeTransfer == null || transitLegBeforeTransfer.getTransferToNextLeg() == null || !transitLegBeforeTransfer.getTransferToNextLeg().getTransferConstraint().isStaySeated() ); diff --git a/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java b/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java index ba0c5c70f2d..213fbeb1811 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java @@ -8,9 +8,12 @@ import java.time.LocalDateTime; import java.time.Month; import java.util.ArrayList; +import java.util.HashMap; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.opentripplanner._support.time.ZoneIds; +import org.opentripplanner.ext.flex.FlexAccessEgress; import org.opentripplanner.framework.time.TimeUtils; import org.opentripplanner.model.PickDrop; import org.opentripplanner.model.StopTime; @@ -20,12 +23,27 @@ 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.RaptorTransfer; +import org.opentripplanner.raptor.api.model.RaptorTripSchedule; +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.path.Path; import org.opentripplanner.raptor.spi.RaptorCostCalculator; +import org.opentripplanner.routing.algorithm.raptoradapter.transit.DefaultAccessEgress; +import org.opentripplanner.routing.algorithm.raptoradapter.transit.DefaultRaptorTransfer; +import org.opentripplanner.routing.algorithm.raptoradapter.transit.FlexAccessEgressAdapter; +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; +import org.opentripplanner.street.search.state.TestStateBuilder; import org.opentripplanner.transit.model.basic.TransitMode; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.network.Route; @@ -34,6 +52,7 @@ import org.opentripplanner.transit.model.organization.Agency; import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.service.DefaultTransitService; +import org.opentripplanner.transit.service.StopModel; import org.opentripplanner.transit.service.TransitModel; public class RaptorPathToItineraryMapperTest { @@ -57,6 +76,16 @@ public class RaptorPathToItineraryMapperTest { STOP_COSTS ); + private static final RegularStop S1 = RegularStop + .of(new FeedScopedId("TestFeed", "STOP1")) + .withCoordinate(0.0, 0.0) + .build(); + + private static final RegularStop S2 = RegularStop + .of(new FeedScopedId("TestFeed", "STOP2")) + .withCoordinate(0.0, 0.0) + .build(); + @ParameterizedTest @ValueSource(strings = { "0", "3000", "-3000" }) public void createItineraryTestZeroDurationEgress(int LAST_LEG_COST) { @@ -82,8 +111,41 @@ public void createItineraryTestZeroDurationEgress(int LAST_LEG_COST) { ); } + /** + * Create a minimalist path FlexAccess-->Transfer-->Egress (without transit) and check that the 3 legs are properly mapped in the itinerary. + * + */ + @Test + public void createItineraryWithOnBoardFlexAccess() { + RaptorPathToItineraryMapper mapper = getRaptorPathToItineraryMapper(); + + State state = TestStateBuilder.ofWalking().streetEdge().streetEdge().build(); + FlexAccessEgress flexAccessEgress = new FlexAccessEgress(S1, null, 0, 1, null, state, true); + RaptorAccessEgress access = new FlexAccessEgressAdapter(flexAccessEgress, false); + Transfer transfer = new Transfer(S2.getIndex(), 0); + RaptorTransfer raptorTransfer = new DefaultRaptorTransfer(S1.getIndex(), 0, 0, transfer); + RaptorAccessEgress egress = new DefaultAccessEgress(S2.getIndex(), state); + PathLeg egressLeg = new EgressPathLeg<>(egress, 0, 0, 0); + PathLeg transferLeg = new TransferPathLeg<>( + S1.getIndex(), + 0, + 0, + 0, + raptorTransfer, + egressLeg + ); + AccessPathLeg accessLeg = new AccessPathLeg(access, 0, 0, 0, transferLeg); + RaptorPath path = new Path<>(0, accessLeg, 0); + // Act + var itinerary = mapper.createItinerary(path); + + // Assert + assertNotNull(itinerary); + assertEquals(3, itinerary.getLegs().size(), "The wrong number of legs was returned"); + } + private TripPattern getOriginalPattern(TestTripPattern pattern) { - ArrayList stopTimes = new ArrayList(); + ArrayList stopTimes = new ArrayList<>(); for (int i = 0; i < pattern.numberOfStopsInPattern(); i++) { var stop = RegularStop @@ -114,15 +176,31 @@ private RaptorPathToItineraryMapper getRaptorPathToItineraryMa .of(2022, Month.OCTOBER, 10, 12, 0, 0) .atZone(ZoneIds.STOCKHOLM) .toInstant(); - return new RaptorPathToItineraryMapper( + TransitModel transitModel = new TransitModel(); + transitModel.initTimeZone(ZoneIds.CET); + return new RaptorPathToItineraryMapper<>( new Graph(), - new DefaultTransitService(new TransitModel()), - null, + new DefaultTransitService(transitModel), + getTransitLayer(), dateTime.atZone(ZoneIds.CET), new RouteRequest() ); } + private static TransitLayer getTransitLayer() { + return new TransitLayer( + new HashMap<>(), + null, + null, + StopModel.of().withRegularStop(S1).withRegularStop(S2).build(), + null, + null, + null, + null, + null + ); + } + private TestTripSchedule getTestTripSchedule() { var agency = Agency .of(new FeedScopedId("TestFeed", "Auth_1")) From d902f4ea44bad734c5c377fc826e0cd02aef9cc2 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Tue, 29 Aug 2023 14:09:03 +0200 Subject: [PATCH 10/95] Apply review suggestion --- .../mapping/RaptorPathToItineraryMapperTest.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java b/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java index 213fbeb1811..6fac1fe49dd 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java @@ -44,6 +44,7 @@ import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.street.search.state.State; import org.opentripplanner.street.search.state.TestStateBuilder; +import org.opentripplanner.transit.model._data.TransitModelForTest; import org.opentripplanner.transit.model.basic.TransitMode; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.network.Route; @@ -76,15 +77,9 @@ public class RaptorPathToItineraryMapperTest { STOP_COSTS ); - private static final RegularStop S1 = RegularStop - .of(new FeedScopedId("TestFeed", "STOP1")) - .withCoordinate(0.0, 0.0) - .build(); + private static final RegularStop S1 = TransitModelForTest.stopForTest("STOP1", 0.0, 0.0); - private static final RegularStop S2 = RegularStop - .of(new FeedScopedId("TestFeed", "STOP2")) - .withCoordinate(0.0, 0.0) - .build(); + private static final RegularStop S2 = TransitModelForTest.stopForTest("STOP2", 1.0, 1.0); @ParameterizedTest @ValueSource(strings = { "0", "3000", "-3000" }) From c049d9fd7688c61560171728288f8c401ad98ad6 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Tue, 29 Aug 2023 15:28:48 +0200 Subject: [PATCH 11/95] Fix filtering of rental vehicle in vehicle station BBox search --- .../internal/DefaultVehicleRentalService.java | 1 + .../DefaultVehicleRentalServiceTest.java | 40 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 src/test/java/org/opentripplanner/service/vehiclerental/internal/DefaultVehicleRentalServiceTest.java diff --git a/src/main/java/org/opentripplanner/service/vehiclerental/internal/DefaultVehicleRentalService.java b/src/main/java/org/opentripplanner/service/vehiclerental/internal/DefaultVehicleRentalService.java index 1c888f8e21a..5fe536fa1ca 100644 --- a/src/main/java/org/opentripplanner/service/vehiclerental/internal/DefaultVehicleRentalService.java +++ b/src/main/java/org/opentripplanner/service/vehiclerental/internal/DefaultVehicleRentalService.java @@ -114,6 +114,7 @@ public List getVehicleRentalStationForEnvelope( return rentalPlaces .values() .stream() + .filter(VehicleRentalStation.class::isInstance) .filter(b -> envelope.contains(new Coordinate(b.getLongitude(), b.getLatitude()))) .toList(); } diff --git a/src/test/java/org/opentripplanner/service/vehiclerental/internal/DefaultVehicleRentalServiceTest.java b/src/test/java/org/opentripplanner/service/vehiclerental/internal/DefaultVehicleRentalServiceTest.java new file mode 100644 index 00000000000..2c88b1fe90c --- /dev/null +++ b/src/test/java/org/opentripplanner/service/vehiclerental/internal/DefaultVehicleRentalServiceTest.java @@ -0,0 +1,40 @@ +package org.opentripplanner.service.vehiclerental.internal; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; +import org.junit.jupiter.api.Test; +import org.opentripplanner.service.vehiclerental.VehicleRentalService; +import org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace; +import org.opentripplanner.service.vehiclerental.model.VehicleRentalStation; +import org.opentripplanner.service.vehiclerental.model.VehicleRentalVehicle; +import org.opentripplanner.transit.model.framework.FeedScopedId; + +class DefaultVehicleRentalServiceTest { + + @Test + void getVehicleRentalStationForEnvelopeShouldExcludeVehicleRentalVehicle() { + DefaultVehicleRentalService defaultVehicleRentalService = new DefaultVehicleRentalService(); + + VehicleRentalStation vehicleRentalStation = new VehicleRentalStation(); + vehicleRentalStation.id = new FeedScopedId("Feed1", "VehicleRentalStation1"); + vehicleRentalStation.latitude = 1; + vehicleRentalStation.longitude = 1; + defaultVehicleRentalService.addVehicleRentalStation(vehicleRentalStation); + + VehicleRentalVehicle vehicleRentalVehicle = new VehicleRentalVehicle(); + vehicleRentalVehicle.id = new FeedScopedId("Feed1", "VehicleRentalVehicle1"); + vehicleRentalVehicle.latitude = 2; + vehicleRentalVehicle.longitude = 2; + defaultVehicleRentalService.addVehicleRentalStation(vehicleRentalVehicle); + + List vehicleRentalStationForEnvelope = defaultVehicleRentalService.getVehicleRentalStationForEnvelope( + 0, + 0, + 10, + 10 + ); + assertEquals(1, vehicleRentalStationForEnvelope.size()); + assertEquals(vehicleRentalStation, vehicleRentalStationForEnvelope.get(0)); + } +} From eb8ec70fcecbfeddb5dec0edd5ba4b561ebad013 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Tue, 29 Aug 2023 16:03:46 +0200 Subject: [PATCH 12/95] Apply review suggestion --- .../service/vehiclerental/VehicleRentalService.java | 4 ++-- .../vehiclerental/internal/DefaultVehicleRentalService.java | 3 ++- .../internal/DefaultVehicleRentalServiceTest.java | 4 +--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/opentripplanner/service/vehiclerental/VehicleRentalService.java b/src/main/java/org/opentripplanner/service/vehiclerental/VehicleRentalService.java index 473f210ab4b..93fe12a1c7c 100644 --- a/src/main/java/org/opentripplanner/service/vehiclerental/VehicleRentalService.java +++ b/src/main/java/org/opentripplanner/service/vehiclerental/VehicleRentalService.java @@ -9,7 +9,7 @@ /** * The read-only service for getting information about rental vehicles. - * + *

* For writing data see {@link VehicleRentalRepository} */ public interface VehicleRentalService { @@ -32,7 +32,7 @@ public interface VehicleRentalService { * over a set, but we could use a spatial index if the number of vehicle rental stations is high * enough for performance to be a concern. */ - List getVehicleRentalStationForEnvelope( + List getVehicleRentalStationForEnvelope( double minLon, double minLat, double maxLon, diff --git a/src/main/java/org/opentripplanner/service/vehiclerental/internal/DefaultVehicleRentalService.java b/src/main/java/org/opentripplanner/service/vehiclerental/internal/DefaultVehicleRentalService.java index 5fe536fa1ca..ab8bc3d780c 100644 --- a/src/main/java/org/opentripplanner/service/vehiclerental/internal/DefaultVehicleRentalService.java +++ b/src/main/java/org/opentripplanner/service/vehiclerental/internal/DefaultVehicleRentalService.java @@ -100,7 +100,7 @@ public boolean hasRentalBikes() { } @Override - public List getVehicleRentalStationForEnvelope( + public List getVehicleRentalStationForEnvelope( double minLon, double minLat, double maxLon, @@ -116,6 +116,7 @@ public List getVehicleRentalStationForEnvelope( .stream() .filter(VehicleRentalStation.class::isInstance) .filter(b -> envelope.contains(new Coordinate(b.getLongitude(), b.getLatitude()))) + .map(VehicleRentalStation.class::cast) .toList(); } } diff --git a/src/test/java/org/opentripplanner/service/vehiclerental/internal/DefaultVehicleRentalServiceTest.java b/src/test/java/org/opentripplanner/service/vehiclerental/internal/DefaultVehicleRentalServiceTest.java index 2c88b1fe90c..7f9d882fdeb 100644 --- a/src/test/java/org/opentripplanner/service/vehiclerental/internal/DefaultVehicleRentalServiceTest.java +++ b/src/test/java/org/opentripplanner/service/vehiclerental/internal/DefaultVehicleRentalServiceTest.java @@ -4,8 +4,6 @@ import java.util.List; import org.junit.jupiter.api.Test; -import org.opentripplanner.service.vehiclerental.VehicleRentalService; -import org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace; import org.opentripplanner.service.vehiclerental.model.VehicleRentalStation; import org.opentripplanner.service.vehiclerental.model.VehicleRentalVehicle; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -28,7 +26,7 @@ void getVehicleRentalStationForEnvelopeShouldExcludeVehicleRentalVehicle() { vehicleRentalVehicle.longitude = 2; defaultVehicleRentalService.addVehicleRentalStation(vehicleRentalVehicle); - List vehicleRentalStationForEnvelope = defaultVehicleRentalService.getVehicleRentalStationForEnvelope( + List vehicleRentalStationForEnvelope = defaultVehicleRentalService.getVehicleRentalStationForEnvelope( 0, 0, 10, From e68eb35561fea48a99182d7b9aedac8ac7e87f26 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 29 Aug 2023 17:50:12 +0200 Subject: [PATCH 13/95] Move GTFS GraphQL API out of the sandbox --- .github/workflows/cibuild.yml | 4 +- doc-templates/GraphQL-Tutorial.md | 2 +- docs/Configuration.md | 2 +- docs/GraphQL-Tutorial.md | 2 +- .../datafetchers/StopRelationshipImpl.java | 29 ----- .../mapping/AlertCauseMapper.java | 35 ------ .../mapping/AlertEffectMapper.java | 33 ----- .../mapping/DirectionMapper.java | 46 ------- .../mapping/SeverityMapper.java | 26 ---- .../model/RideHailingProvider.java | 3 - .../api/configuration/APIEndpoints.java | 2 +- .../apis/gtfs}/GraphQLRequestContext.java | 2 +- .../apis/gtfs}/GraphQLScalars.java | 2 +- .../apis/gtfs}/GraphQLUtils.java | 14 +-- .../apis/gtfs}/GtfsGraphQLAPI.java | 4 +- .../apis/gtfs}/GtfsGraphQLIndex.java | 116 +++++++++--------- .../apis/gtfs}/IntrospectionTypeWiring.java | 2 +- .../apis/gtfs}/datafetchers/AgencyImpl.java | 8 +- .../datafetchers/AlertEntityTypeResolver.java | 10 +- .../apis/gtfs}/datafetchers/AlertImpl.java | 26 ++-- .../apis/gtfs}/datafetchers/BikeParkImpl.java | 4 +- .../datafetchers/BikeRentalStationImpl.java | 4 +- .../gtfs}/datafetchers/BookingInfoImpl.java | 4 +- .../gtfs}/datafetchers/BookingTimeImpl.java | 4 +- .../apis/gtfs}/datafetchers/CarParkImpl.java | 4 +- .../gtfs}/datafetchers/ContactInfoImpl.java | 4 +- .../gtfs}/datafetchers/CoordinatesImpl.java | 4 +- .../apis/gtfs}/datafetchers/CurrencyImpl.java | 4 +- .../datafetchers/DefaultFareProductImpl.java | 4 +- .../gtfs}/datafetchers/DepartureRowImpl.java | 10 +- .../datafetchers/FareProductTypeResolver.java | 2 +- .../datafetchers/FareProductUseImpl.java | 4 +- .../apis/gtfs}/datafetchers/FeedImpl.java | 8 +- .../apis/gtfs}/datafetchers/GeometryImpl.java | 4 +- .../gtfs}/datafetchers/ItineraryImpl.java | 6 +- .../apis/gtfs}/datafetchers/LegImpl.java | 10 +- .../apis/gtfs}/datafetchers/MoneyImpl.java | 4 +- .../gtfs}/datafetchers/NodeTypeResolver.java | 2 +- .../gtfs}/datafetchers/OpeningHoursImpl.java | 4 +- .../apis/gtfs}/datafetchers/PatternImpl.java | 8 +- .../apis/gtfs}/datafetchers/PlaceImpl.java | 10 +- .../PlaceInterfaceTypeResolver.java | 16 ++- .../apis/gtfs}/datafetchers/PlanImpl.java | 4 +- .../gtfs}/datafetchers/QueryTypeImpl.java | 18 +-- .../gtfs}/datafetchers/RentalVehicleImpl.java | 4 +- .../datafetchers/RentalVehicleTypeImpl.java | 8 +- .../datafetchers/RideHailingEstimateImpl.java | 6 +- .../apis/gtfs}/datafetchers/RouteImpl.java | 12 +- .../gtfs}/datafetchers/RouteTypeImpl.java | 8 +- .../gtfs}/datafetchers/RoutingErrorImpl.java | 8 +- .../datafetchers/StopGeometriesImpl.java | 4 +- .../apis/gtfs}/datafetchers/StopImpl.java | 40 +++--- .../gtfs}/datafetchers/StopOnRouteImpl.java | 6 +- .../gtfs}/datafetchers/StopOnTripImpl.java | 6 +- .../datafetchers/StopRelationshipImpl.java | 29 +++++ .../apis/gtfs}/datafetchers/StoptimeImpl.java | 4 +- .../datafetchers/StoptimesInPatternImpl.java | 4 +- .../gtfs}/datafetchers/SystemNoticeImpl.java | 4 +- .../gtfs}/datafetchers/TicketTypeImpl.java | 4 +- .../datafetchers/TranslatedStringImpl.java | 4 +- .../apis/gtfs}/datafetchers/TripImpl.java | 13 +- .../apis/gtfs}/datafetchers/UnknownImpl.java | 6 +- .../datafetchers/VehicleParkingImpl.java | 4 +- .../datafetchers/VehiclePositionImpl.java | 6 +- .../VehicleRentalStationImpl.java | 4 +- .../gtfs}/datafetchers/debugOutputImpl.java | 4 +- .../elevationProfileComponentImpl.java | 4 +- .../gtfs}/datafetchers/fareComponentImpl.java | 6 +- .../apis/gtfs}/datafetchers/fareImpl.java | 4 +- .../datafetchers/placeAtDistanceImpl.java | 4 +- .../datafetchers/serviceTimeRangeImpl.java | 6 +- .../apis/gtfs}/datafetchers/stepImpl.java | 15 ++- .../datafetchers/stopAtDistanceImpl.java | 4 +- .../gtfs}/generated/GraphQLDataFetchers.java | 38 +++--- .../apis/gtfs}/generated/GraphQLTypes.java | 2 +- .../apis/gtfs}/generated/README.md | 0 .../apis/gtfs}/generated/graphql-codegen.yml | 36 +++--- .../apis/gtfs}/generated/package.json | 0 .../apis/gtfs}/generated/yarn.lock | 0 .../apis/gtfs/mapping/AlertCauseMapper.java | 34 +++++ .../apis/gtfs/mapping/AlertEffectMapper.java | 33 +++++ .../apis/gtfs/mapping/DirectionMapper.java | 45 +++++++ .../apis/gtfs}/mapping/NumberMapper.java | 2 +- .../gtfs}/mapping/RouteRequestMapper.java | 4 +- .../apis/gtfs/mapping/SeverityMapper.java | 29 +++++ .../apis/gtfs}/mapping/StreetNoteMapper.java | 2 +- .../apis/gtfs/model/RideHailingProvider.java | 3 + .../apis/gtfs}/model/RouteTypeModel.java | 2 +- .../apis/gtfs}/model/StopOnRouteModel.java | 2 +- .../apis/gtfs}/model/StopOnTripModel.java | 2 +- .../apis/gtfs}/model/StopPosition.java | 4 +- .../apis/gtfs}/model/UnknownModel.java | 2 +- .../framework/application/OTPFeature.java | 2 +- .../apis/gtfs}/GraphQLIndexTest.java | 2 +- .../apis/gtfs}/GraphQLIntegrationTest.java | 4 +- .../apis/gtfs}/GraphQLScalarsTest.java | 2 +- .../apis/gtfs}/TestRoutingService.java | 2 +- .../gtfs}/datafetchers/QueryTypeImplTest.java | 4 +- .../gtfs}/mapping/DirectionMapperTest.java | 2 +- .../gtfs}/mapping/RouteRequestMapperTest.java | 6 +- .../gtfs}/mapping/StreetNoteMapperTest.java | 2 +- .../gtfsgraphqlapi/expectations/patterns.json | 0 .../expectations/plan-extended.json | 0 .../expectations/plan-fares.json | 0 .../expectations/plan-stop-positions.json | 0 .../gtfsgraphqlapi/expectations/plan.json | 0 .../gtfsgraphqlapi/expectations/routes.json | 0 .../gtfsgraphqlapi/expectations/stops.json | 0 .../expectations/vehicle-parking.json | 0 .../expectations/walk-steps.json | 0 .../gtfsgraphqlapi/queries/patterns.graphql | 0 .../queries/plan-extended.graphql | 0 .../gtfsgraphqlapi/queries/plan-fares.graphql | 0 .../queries/plan-stop-positions.graphql | 0 .../gtfsgraphqlapi/queries/plan.graphql | 0 .../gtfsgraphqlapi/queries/routes.graphql | 0 .../gtfsgraphqlapi/queries/stops.graphql | 0 .../queries/vehicle-parking.graphql | 0 .../gtfsgraphqlapi/queries/walk-steps.graphql | 0 119 files changed, 501 insertions(+), 510 deletions(-) delete mode 100644 src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/StopRelationshipImpl.java delete mode 100644 src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/AlertCauseMapper.java delete mode 100644 src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/AlertEffectMapper.java delete mode 100644 src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/DirectionMapper.java delete mode 100644 src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/SeverityMapper.java delete mode 100644 src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/model/RideHailingProvider.java rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/GraphQLRequestContext.java (97%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/GraphQLScalars.java (98%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/GraphQLUtils.java (87%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/GtfsGraphQLAPI.java (97%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/GtfsGraphQLIndex.java (64%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/IntrospectionTypeWiring.java (98%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/AgencyImpl.java (93%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/AlertEntityTypeResolver.java (81%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/AlertImpl.java (92%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/BikeParkImpl.java (93%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/BikeRentalStationImpl.java (96%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/BookingInfoImpl.java (91%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/BookingTimeImpl.java (82%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/CarParkImpl.java (93%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/ContactInfoImpl.java (89%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/CoordinatesImpl.java (79%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/CurrencyImpl.java (79%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/DefaultFareProductImpl.java (88%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/DepartureRowImpl.java (86%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/FareProductTypeResolver.java (90%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/FareProductUseImpl.java (81%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/FeedImpl.java (90%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/GeometryImpl.java (82%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/ItineraryImpl.java (93%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/LegImpl.java (96%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/MoneyImpl.java (80%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/NodeTypeResolver.java (98%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/OpeningHoursImpl.java (85%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/PatternImpl.java (96%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/PlaceImpl.java (92%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/PlaceInterfaceTypeResolver.java (77%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/PlanImpl.java (96%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/QueryTypeImpl.java (97%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/RentalVehicleImpl.java (93%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/RentalVehicleTypeImpl.java (82%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/RideHailingEstimateImpl.java (82%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/RouteImpl.java (94%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/RouteTypeImpl.java (83%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/RoutingErrorImpl.java (75%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/StopGeometriesImpl.java (82%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/StopImpl.java (92%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/StopOnRouteImpl.java (73%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/StopOnTripImpl.java (73%) create mode 100644 src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StopRelationshipImpl.java rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/StoptimeImpl.java (96%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/StoptimesInPatternImpl.java (83%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/SystemNoticeImpl.java (79%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/TicketTypeImpl.java (91%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/TranslatedStringImpl.java (80%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/TripImpl.java (96%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/UnknownImpl.java (66%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/VehicleParkingImpl.java (95%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/VehiclePositionImpl.java (86%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/VehicleRentalStationImpl.java (95%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/debugOutputImpl.java (88%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/elevationProfileComponentImpl.java (81%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/fareComponentImpl.java (87%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/fareImpl.java (87%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/placeAtDistanceImpl.java (94%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/serviceTimeRangeImpl.java (77%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/stepImpl.java (78%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/datafetchers/stopAtDistanceImpl.java (86%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/generated/GraphQLDataFetchers.java (94%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/generated/GraphQLTypes.java (99%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/generated/README.md (100%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/generated/graphql-codegen.yml (72%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/generated/package.json (100%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/generated/yarn.lock (100%) create mode 100644 src/main/java/org/opentripplanner/apis/gtfs/mapping/AlertCauseMapper.java create mode 100644 src/main/java/org/opentripplanner/apis/gtfs/mapping/AlertEffectMapper.java create mode 100644 src/main/java/org/opentripplanner/apis/gtfs/mapping/DirectionMapper.java rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/mapping/NumberMapper.java (84%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/mapping/RouteRequestMapper.java (99%) create mode 100644 src/main/java/org/opentripplanner/apis/gtfs/mapping/SeverityMapper.java rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/mapping/StreetNoteMapper.java (95%) create mode 100644 src/main/java/org/opentripplanner/apis/gtfs/model/RideHailingProvider.java rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/model/RouteTypeModel.java (93%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/model/StopOnRouteModel.java (93%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/model/StopOnTripModel.java (93%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/model/StopPosition.java (84%) rename src/{ext/java/org/opentripplanner/ext/gtfsgraphqlapi => main/java/org/opentripplanner/apis/gtfs}/model/UnknownModel.java (87%) rename src/{ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi => test/java/org/opentripplanner/apis/gtfs}/GraphQLIndexTest.java (97%) rename src/{ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi => test/java/org/opentripplanner/apis/gtfs}/GraphQLIntegrationTest.java (98%) rename src/{ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi => test/java/org/opentripplanner/apis/gtfs}/GraphQLScalarsTest.java (96%) rename src/{ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi => test/java/org/opentripplanner/apis/gtfs}/TestRoutingService.java (96%) rename src/{ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi => test/java/org/opentripplanner/apis/gtfs}/datafetchers/QueryTypeImplTest.java (97%) rename src/{ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi => test/java/org/opentripplanner/apis/gtfs}/mapping/DirectionMapperTest.java (92%) rename src/{ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi => test/java/org/opentripplanner/apis/gtfs}/mapping/RouteRequestMapperTest.java (97%) rename src/{ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi => test/java/org/opentripplanner/apis/gtfs}/mapping/StreetNoteMapperTest.java (97%) rename src/{ext-test => test}/resources/gtfsgraphqlapi/expectations/patterns.json (100%) rename src/{ext-test => test}/resources/gtfsgraphqlapi/expectations/plan-extended.json (100%) rename src/{ext-test => test}/resources/gtfsgraphqlapi/expectations/plan-fares.json (100%) rename src/{ext-test => test}/resources/gtfsgraphqlapi/expectations/plan-stop-positions.json (100%) rename src/{ext-test => test}/resources/gtfsgraphqlapi/expectations/plan.json (100%) rename src/{ext-test => test}/resources/gtfsgraphqlapi/expectations/routes.json (100%) rename src/{ext-test => test}/resources/gtfsgraphqlapi/expectations/stops.json (100%) rename src/{ext-test => test}/resources/gtfsgraphqlapi/expectations/vehicle-parking.json (100%) rename src/{ext-test => test}/resources/gtfsgraphqlapi/expectations/walk-steps.json (100%) rename src/{ext-test => test}/resources/gtfsgraphqlapi/queries/patterns.graphql (100%) rename src/{ext-test => test}/resources/gtfsgraphqlapi/queries/plan-extended.graphql (100%) rename src/{ext-test => test}/resources/gtfsgraphqlapi/queries/plan-fares.graphql (100%) rename src/{ext-test => test}/resources/gtfsgraphqlapi/queries/plan-stop-positions.graphql (100%) rename src/{ext-test => test}/resources/gtfsgraphqlapi/queries/plan.graphql (100%) rename src/{ext-test => test}/resources/gtfsgraphqlapi/queries/routes.graphql (100%) rename src/{ext-test => test}/resources/gtfsgraphqlapi/queries/stops.graphql (100%) rename src/{ext-test => test}/resources/gtfsgraphqlapi/queries/vehicle-parking.graphql (100%) rename src/{ext-test => test}/resources/gtfsgraphqlapi/queries/walk-steps.graphql (100%) diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml index 3d087b646d1..306b091715f 100644 --- a/.github/workflows/cibuild.yml +++ b/.github/workflows/cibuild.yml @@ -152,7 +152,7 @@ jobs: # schema hasn't changed. # example commit: https://github.com/opentripplanner/docs/commit/45e6ddf8e4a4 - SCHEMA_FILE_MODIFIED=`git log -n 1 --pretty=format:%ct src/ext/resources/gtfsgraphqlapi/schema.graphqls` + SCHEMA_FILE_MODIFIED=`git log -n 1 --pretty=format:%ct src/main/resources/gtfsgraphqlapi/schema.graphqls` echo "schema modified at ${SCHEMA_FILE_MODIFIED}" git checkout $LOCAL_BRANCH DOCS_MODIFIED=`git log -n 1 --pretty=format:%ct api/dev-2.x/graphql-gtfs/introduction.html` @@ -179,7 +179,7 @@ jobs: with: node-version: 16 - name: Run code generator - working-directory: src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/generated + working-directory: src/main/java/org/opentripplanner/apis/gtfs/generated run: | yarn install yarn generate diff --git a/doc-templates/GraphQL-Tutorial.md b/doc-templates/GraphQL-Tutorial.md index fd3d901b576..d35199667a4 100644 --- a/doc-templates/GraphQL-Tutorial.md +++ b/doc-templates/GraphQL-Tutorial.md @@ -51,4 +51,4 @@ Most people want to get routing results out of OTP, so lets see the query for th Again, please use the autocomplete and documentation viewers to figure out what each input parameter and property means. -More examples for a variety of queries can also be found [in the test code](https://github.com/opentripplanner/OpenTripPlanner/tree/dev-2.x/src/ext-test/resources/gtfsgraphqlapi/queries). \ No newline at end of file +More examples for a variety of queries can also be found [in the test code](https://github.com/opentripplanner/OpenTripPlanner/tree/dev-2.x/src/test/resources/gtfsgraphqlapi/queries). \ No newline at end of file diff --git a/docs/Configuration.md b/docs/Configuration.md index 4a36c764c19..a874256bcea 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -228,7 +228,7 @@ Here is a list of all features which can be toggled on/off and their default val | `ConsiderPatternsForDirectTransfers` | Enable limiting transfers so that there is only a single transfer to each pattern. | ✓️ | | | `DebugClient` | Enable the debug web client located at the root of the web server. | ✓️ | | | `FloatingBike` | Enable floating bike routing. | ✓️ | | -| `GtfsGraphQlApi` | Enable GTFS GraphQL API. | ✓️ | ✓️ | +| `GtfsGraphQlApi` | Enable GTFS GraphQL API. | ✓️ | | | `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. | | | diff --git a/docs/GraphQL-Tutorial.md b/docs/GraphQL-Tutorial.md index 73fba7d743e..8f0ace9c8e3 100644 --- a/docs/GraphQL-Tutorial.md +++ b/docs/GraphQL-Tutorial.md @@ -126,4 +126,4 @@ Most people want to get routing results out of OTP, so lets see the query for th Again, please use the autocomplete and documentation viewers to figure out what each input parameter and property means. -More examples for a variety of queries can also be found [in the test code](https://github.com/opentripplanner/OpenTripPlanner/tree/dev-2.x/src/ext-test/resources/gtfsgraphqlapi/queries). \ No newline at end of file +More examples for a variety of queries can also be found [in the test code](https://github.com/opentripplanner/OpenTripPlanner/tree/dev-2.x/src/test/resources/gtfsgraphqlapi/queries). \ No newline at end of file diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/StopRelationshipImpl.java b/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/StopRelationshipImpl.java deleted file mode 100644 index 1fccf47e828..00000000000 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/StopRelationshipImpl.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; - -import graphql.schema.DataFetcher; -import graphql.schema.DataFetchingEnvironment; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers.GraphQLStopRelationship; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLVehicleStopStatus; -import org.opentripplanner.service.vehiclepositions.model.RealtimeVehiclePosition.StopRelationship; - -public class StopRelationshipImpl implements GraphQLStopRelationship { - - @Override - public DataFetcher status() { - return env -> - switch (getSource(env).status()) { - case INCOMING_AT -> GraphQLVehicleStopStatus.INCOMING_AT; - case IN_TRANSIT_TO -> GraphQLVehicleStopStatus.IN_TRANSIT_TO; - case STOPPED_AT -> GraphQLVehicleStopStatus.STOPPED_AT; - }; - } - - @Override - public DataFetcher stop() { - return env -> getSource(env).stop(); - } - - private StopRelationship getSource(DataFetchingEnvironment environment) { - return environment.getSource(); - } -} diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/AlertCauseMapper.java b/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/AlertCauseMapper.java deleted file mode 100644 index f45bd8dd1d2..00000000000 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/AlertCauseMapper.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.mapping; - -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLAlertCauseType; -import org.opentripplanner.routing.alertpatch.AlertCause; - -/** - * Class for mapping {@link AlertCause} to GraphQL API cause (GTFS RT). - */ -public class AlertCauseMapper { - - /** - * Returns GraphQL API string counter part for internal {@link AlertCause} enum. Defaults to - * returning UNKNOWN_CAUSE. - */ - public static GraphQLTypes.GraphQLAlertCauseType getGraphQLCause(AlertCause cause) { - if (cause == null) { - return GraphQLTypes.GraphQLAlertCauseType.UNKNOWN_CAUSE; - } - return switch (cause) { - case UNKNOWN_CAUSE -> GraphQLAlertCauseType.UNKNOWN_CAUSE; - case OTHER_CAUSE -> GraphQLAlertCauseType.OTHER_CAUSE; - case TECHNICAL_PROBLEM -> GraphQLAlertCauseType.TECHNICAL_PROBLEM; - case STRIKE -> GraphQLAlertCauseType.STRIKE; - case DEMONSTRATION -> GraphQLAlertCauseType.DEMONSTRATION; - case ACCIDENT -> GraphQLAlertCauseType.ACCIDENT; - case HOLIDAY -> GraphQLAlertCauseType.HOLIDAY; - case WEATHER -> GraphQLAlertCauseType.WEATHER; - case MAINTENANCE -> GraphQLAlertCauseType.MAINTENANCE; - case CONSTRUCTION -> GraphQLAlertCauseType.CONSTRUCTION; - case POLICE_ACTIVITY -> GraphQLAlertCauseType.POLICE_ACTIVITY; - case MEDICAL_EMERGENCY -> GraphQLAlertCauseType.MEDICAL_EMERGENCY; - }; - } -} diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/AlertEffectMapper.java b/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/AlertEffectMapper.java deleted file mode 100644 index 232c3e3e663..00000000000 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/AlertEffectMapper.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.mapping; - -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLAlertEffectType; -import org.opentripplanner.routing.alertpatch.AlertEffect; - -/** - * Class for mapping {@link AlertEffect} to GraphQL API effect (GTFS RT). - */ -public class AlertEffectMapper { - - /** - * Returns GraphQL API string counter part for internal {@link AlertEffect} enum. Defaults - * to returning UNKNOWN_Effect. - */ - public static GraphQLAlertEffectType getGraphQLEffect(AlertEffect effect) { - if (effect == null) { - return GraphQLAlertEffectType.UNKNOWN_EFFECT; - } - return switch (effect) { - case NO_SERVICE -> GraphQLAlertEffectType.NO_SERVICE; - case REDUCED_SERVICE -> GraphQLAlertEffectType.REDUCED_SERVICE; - case SIGNIFICANT_DELAYS -> GraphQLAlertEffectType.SIGNIFICANT_DELAYS; - case DETOUR -> GraphQLAlertEffectType.DETOUR; - case ADDITIONAL_SERVICE -> GraphQLAlertEffectType.ADDITIONAL_SERVICE; - case MODIFIED_SERVICE -> GraphQLAlertEffectType.MODIFIED_SERVICE; - case OTHER_EFFECT -> GraphQLAlertEffectType.OTHER_EFFECT; - case UNKNOWN_EFFECT -> GraphQLAlertEffectType.UNKNOWN_EFFECT; - case STOP_MOVED -> GraphQLAlertEffectType.STOP_MOVED; - case NO_EFFECT -> GraphQLAlertEffectType.NO_EFFECT; - case ACCESSIBILITY_ISSUE -> GraphQLAlertEffectType.ACCESSIBILITY_ISSUE; - }; - } -} diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/DirectionMapper.java b/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/DirectionMapper.java deleted file mode 100644 index 3a906601cbd..00000000000 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/DirectionMapper.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.mapping; - -import javax.annotation.Nonnull; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLAbsoluteDirection; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLRelativeDirection; -import org.opentripplanner.model.plan.AbsoluteDirection; -import org.opentripplanner.model.plan.RelativeDirection; - -public final class DirectionMapper { - - @Nonnull - public static GraphQLAbsoluteDirection map(AbsoluteDirection dir) { - return switch (dir) { - case NORTH -> GraphQLAbsoluteDirection.NORTH; - case NORTHEAST -> GraphQLAbsoluteDirection.NORTHEAST; - case EAST -> GraphQLAbsoluteDirection.EAST; - case SOUTHEAST -> GraphQLAbsoluteDirection.SOUTHEAST; - case SOUTH -> GraphQLAbsoluteDirection.SOUTH; - case SOUTHWEST -> GraphQLAbsoluteDirection.SOUTHWEST; - case WEST -> GraphQLAbsoluteDirection.WEST; - case NORTHWEST -> GraphQLAbsoluteDirection.NORTHWEST; - }; - } - - @Nonnull - public static GraphQLRelativeDirection map(RelativeDirection relativeDirection) { - return switch (relativeDirection) { - case DEPART -> GraphQLRelativeDirection.DEPART; - case HARD_LEFT -> GraphQLRelativeDirection.HARD_LEFT; - case LEFT -> GraphQLRelativeDirection.LEFT; - case SLIGHTLY_LEFT -> GraphQLRelativeDirection.SLIGHTLY_LEFT; - case CONTINUE -> GraphQLRelativeDirection.CONTINUE; - case SLIGHTLY_RIGHT -> GraphQLRelativeDirection.SLIGHTLY_RIGHT; - case RIGHT -> GraphQLRelativeDirection.RIGHT; - case HARD_RIGHT -> GraphQLRelativeDirection.HARD_RIGHT; - case CIRCLE_CLOCKWISE -> GraphQLRelativeDirection.CIRCLE_CLOCKWISE; - case CIRCLE_COUNTERCLOCKWISE -> GraphQLRelativeDirection.CIRCLE_COUNTERCLOCKWISE; - case ELEVATOR -> GraphQLRelativeDirection.ELEVATOR; - case UTURN_LEFT -> GraphQLRelativeDirection.UTURN_LEFT; - case UTURN_RIGHT -> GraphQLRelativeDirection.UTURN_RIGHT; - case ENTER_STATION -> GraphQLRelativeDirection.ENTER_STATION; - case EXIT_STATION -> GraphQLRelativeDirection.EXIT_STATION; - case FOLLOW_SIGNS -> GraphQLRelativeDirection.FOLLOW_SIGNS; - }; - } -} diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/SeverityMapper.java b/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/SeverityMapper.java deleted file mode 100644 index fb2d5d6f03a..00000000000 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/SeverityMapper.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.mapping; - -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLAlertSeverityLevelType; -import org.opentripplanner.routing.alertpatch.AlertSeverity; - -/** - * Class for mapping {@link AlertSeverity} to GraphQL API severity (GTFS RT). - */ -public class SeverityMapper { - - /** - * Returns GraphQL API string counter part for internal {@link AlertSeverity} enum. Defaults - * to returning UNKNOWN_SEVERITY. - */ - public static GraphQLAlertSeverityLevelType getGraphQLSeverity(AlertSeverity severity) { - if (severity == null) { - return GraphQLAlertSeverityLevelType.UNKNOWN_SEVERITY; - } - return switch (severity) { - case INFO -> GraphQLAlertSeverityLevelType.INFO; - case VERY_SLIGHT, SLIGHT, WARNING -> GraphQLAlertSeverityLevelType.WARNING; - case VERY_SEVERE, SEVERE -> GraphQLAlertSeverityLevelType.SEVERE; - case UNDEFINED, UNKNOWN_SEVERITY -> GraphQLAlertSeverityLevelType.UNKNOWN_SEVERITY; - }; - } -} diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/model/RideHailingProvider.java b/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/model/RideHailingProvider.java deleted file mode 100644 index 2d4310da9e0..00000000000 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/model/RideHailingProvider.java +++ /dev/null @@ -1,3 +0,0 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.model; - -public record RideHailingProvider(String id) {} diff --git a/src/main/java/org/opentripplanner/api/configuration/APIEndpoints.java b/src/main/java/org/opentripplanner/api/configuration/APIEndpoints.java index c52a3717f78..7873724dcf4 100644 --- a/src/main/java/org/opentripplanner/api/configuration/APIEndpoints.java +++ b/src/main/java/org/opentripplanner/api/configuration/APIEndpoints.java @@ -24,9 +24,9 @@ import org.opentripplanner.api.resource.Routers; import org.opentripplanner.api.resource.ServerInfo; import org.opentripplanner.api.resource.UpdaterStatusResource; +import org.opentripplanner.apis.gtfs.GtfsGraphQLAPI; import org.opentripplanner.ext.actuator.ActuatorAPI; import org.opentripplanner.ext.geocoder.GeocoderResource; -import org.opentripplanner.ext.gtfsgraphqlapi.GtfsGraphQLAPI; import org.opentripplanner.ext.parkAndRideApi.ParkAndRideResource; import org.opentripplanner.ext.reportapi.resource.ReportResource; import org.opentripplanner.ext.transmodelapi.TransmodelAPI; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/GraphQLRequestContext.java b/src/main/java/org/opentripplanner/apis/gtfs/GraphQLRequestContext.java similarity index 97% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/GraphQLRequestContext.java rename to src/main/java/org/opentripplanner/apis/gtfs/GraphQLRequestContext.java index 166aa93fd35..82424276930 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/GraphQLRequestContext.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/GraphQLRequestContext.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi; +package org.opentripplanner.apis.gtfs; import javax.annotation.Nonnull; import org.opentripplanner.routing.api.RoutingService; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/GraphQLScalars.java b/src/main/java/org/opentripplanner/apis/gtfs/GraphQLScalars.java similarity index 98% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/GraphQLScalars.java rename to src/main/java/org/opentripplanner/apis/gtfs/GraphQLScalars.java index 4aa4a2dfac6..07292cfca08 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/GraphQLScalars.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/GraphQLScalars.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi; +package org.opentripplanner.apis.gtfs; import com.bedatadriven.jackson.datatype.jts.JtsModule; import com.fasterxml.jackson.databind.JsonNode; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/GraphQLUtils.java b/src/main/java/org/opentripplanner/apis/gtfs/GraphQLUtils.java similarity index 87% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/GraphQLUtils.java rename to src/main/java/org/opentripplanner/apis/gtfs/GraphQLUtils.java index b0d1d65e971..18f5a7730d5 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/GraphQLUtils.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/GraphQLUtils.java @@ -1,13 +1,13 @@ -package org.opentripplanner.ext.gtfsgraphqlapi; +package org.opentripplanner.apis.gtfs; import java.time.Instant; import java.util.Locale; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLFilterPlaceType; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLFormFactor; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLInputField; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLRoutingErrorCode; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLTransitMode; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLWheelchairBoarding; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLFilterPlaceType; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLFormFactor; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLInputField; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRoutingErrorCode; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLTransitMode; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLWheelchairBoarding; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.routing.api.response.InputField; import org.opentripplanner.routing.api.response.RoutingErrorCode; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/GtfsGraphQLAPI.java b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLAPI.java similarity index 97% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/GtfsGraphQLAPI.java rename to src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLAPI.java index 0f6b1f165b4..20d15b1cfe8 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/GtfsGraphQLAPI.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLAPI.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi; +package org.opentripplanner.apis.gtfs; import com.fasterxml.jackson.databind.ObjectMapper; import graphql.ExecutionResult; @@ -26,8 +26,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -// TODO move to org.opentripplanner.api.resource, this is a Jersey resource class - @Path("/routers/{ignoreRouterId}/index/graphql") @Produces(MediaType.APPLICATION_JSON) // One @Produces annotation for all endpoints. public class GtfsGraphQLAPI { diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/GtfsGraphQLIndex.java b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java similarity index 64% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/GtfsGraphQLIndex.java rename to src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java index 9a8651a2389..877e7c7259f 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/GtfsGraphQLIndex.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi; +package org.opentripplanner.apis.gtfs; import com.google.common.io.Resources; import graphql.ExecutionInput; @@ -28,64 +28,64 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.opentripplanner.api.json.GraphQLResponseSerializer; +import org.opentripplanner.apis.gtfs.datafetchers.AgencyImpl; +import org.opentripplanner.apis.gtfs.datafetchers.AlertEntityTypeResolver; +import org.opentripplanner.apis.gtfs.datafetchers.AlertImpl; +import org.opentripplanner.apis.gtfs.datafetchers.BikeParkImpl; +import org.opentripplanner.apis.gtfs.datafetchers.BikeRentalStationImpl; +import org.opentripplanner.apis.gtfs.datafetchers.BookingInfoImpl; +import org.opentripplanner.apis.gtfs.datafetchers.BookingTimeImpl; +import org.opentripplanner.apis.gtfs.datafetchers.CarParkImpl; +import org.opentripplanner.apis.gtfs.datafetchers.ContactInfoImpl; +import org.opentripplanner.apis.gtfs.datafetchers.CoordinatesImpl; +import org.opentripplanner.apis.gtfs.datafetchers.CurrencyImpl; +import org.opentripplanner.apis.gtfs.datafetchers.DefaultFareProductImpl; +import org.opentripplanner.apis.gtfs.datafetchers.DepartureRowImpl; +import org.opentripplanner.apis.gtfs.datafetchers.FareProductTypeResolver; +import org.opentripplanner.apis.gtfs.datafetchers.FareProductUseImpl; +import org.opentripplanner.apis.gtfs.datafetchers.FeedImpl; +import org.opentripplanner.apis.gtfs.datafetchers.GeometryImpl; +import org.opentripplanner.apis.gtfs.datafetchers.ItineraryImpl; +import org.opentripplanner.apis.gtfs.datafetchers.LegImpl; +import org.opentripplanner.apis.gtfs.datafetchers.MoneyImpl; +import org.opentripplanner.apis.gtfs.datafetchers.NodeTypeResolver; +import org.opentripplanner.apis.gtfs.datafetchers.OpeningHoursImpl; +import org.opentripplanner.apis.gtfs.datafetchers.PatternImpl; +import org.opentripplanner.apis.gtfs.datafetchers.PlaceImpl; +import org.opentripplanner.apis.gtfs.datafetchers.PlaceInterfaceTypeResolver; +import org.opentripplanner.apis.gtfs.datafetchers.PlanImpl; +import org.opentripplanner.apis.gtfs.datafetchers.QueryTypeImpl; +import org.opentripplanner.apis.gtfs.datafetchers.RentalVehicleImpl; +import org.opentripplanner.apis.gtfs.datafetchers.RentalVehicleTypeImpl; +import org.opentripplanner.apis.gtfs.datafetchers.RideHailingEstimateImpl; +import org.opentripplanner.apis.gtfs.datafetchers.RouteImpl; +import org.opentripplanner.apis.gtfs.datafetchers.RouteTypeImpl; +import org.opentripplanner.apis.gtfs.datafetchers.RoutingErrorImpl; +import org.opentripplanner.apis.gtfs.datafetchers.StopGeometriesImpl; +import org.opentripplanner.apis.gtfs.datafetchers.StopImpl; +import org.opentripplanner.apis.gtfs.datafetchers.StopOnRouteImpl; +import org.opentripplanner.apis.gtfs.datafetchers.StopOnTripImpl; +import org.opentripplanner.apis.gtfs.datafetchers.StopRelationshipImpl; +import org.opentripplanner.apis.gtfs.datafetchers.StoptimeImpl; +import org.opentripplanner.apis.gtfs.datafetchers.StoptimesInPatternImpl; +import org.opentripplanner.apis.gtfs.datafetchers.SystemNoticeImpl; +import org.opentripplanner.apis.gtfs.datafetchers.TicketTypeImpl; +import org.opentripplanner.apis.gtfs.datafetchers.TranslatedStringImpl; +import org.opentripplanner.apis.gtfs.datafetchers.TripImpl; +import org.opentripplanner.apis.gtfs.datafetchers.UnknownImpl; +import org.opentripplanner.apis.gtfs.datafetchers.VehicleParkingImpl; +import org.opentripplanner.apis.gtfs.datafetchers.VehiclePositionImpl; +import org.opentripplanner.apis.gtfs.datafetchers.VehicleRentalStationImpl; +import org.opentripplanner.apis.gtfs.datafetchers.debugOutputImpl; +import org.opentripplanner.apis.gtfs.datafetchers.elevationProfileComponentImpl; +import org.opentripplanner.apis.gtfs.datafetchers.fareComponentImpl; +import org.opentripplanner.apis.gtfs.datafetchers.fareImpl; +import org.opentripplanner.apis.gtfs.datafetchers.placeAtDistanceImpl; +import org.opentripplanner.apis.gtfs.datafetchers.serviceTimeRangeImpl; +import org.opentripplanner.apis.gtfs.datafetchers.stepImpl; +import org.opentripplanner.apis.gtfs.datafetchers.stopAtDistanceImpl; +import org.opentripplanner.apis.gtfs.model.StopPosition; import org.opentripplanner.ext.actuator.MicrometerGraphQLInstrumentation; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.AgencyImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.AlertEntityTypeResolver; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.AlertImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.BikeParkImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.BikeRentalStationImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.BookingInfoImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.BookingTimeImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.CarParkImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.ContactInfoImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.CoordinatesImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.CurrencyImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.DefaultFareProductImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.DepartureRowImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.FareProductTypeResolver; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.FareProductUseImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.FeedImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.GeometryImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.ItineraryImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.LegImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.MoneyImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.NodeTypeResolver; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.OpeningHoursImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.PatternImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.PlaceImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.PlaceInterfaceTypeResolver; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.PlanImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.QueryTypeImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.RentalVehicleImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.RentalVehicleTypeImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.RideHailingEstimateImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.RouteImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.RouteTypeImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.RoutingErrorImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.StopGeometriesImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.StopImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.StopOnRouteImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.StopOnTripImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.StopRelationshipImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.StoptimeImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.StoptimesInPatternImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.SystemNoticeImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.TicketTypeImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.TranslatedStringImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.TripImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.UnknownImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.VehicleParkingImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.VehiclePositionImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.VehicleRentalStationImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.debugOutputImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.elevationProfileComponentImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.fareComponentImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.fareImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.placeAtDistanceImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.serviceTimeRangeImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.stepImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.stopAtDistanceImpl; -import org.opentripplanner.ext.gtfsgraphqlapi.model.StopPosition; import org.opentripplanner.framework.application.OTPFeature; import org.opentripplanner.framework.concurrent.OtpRequestThreadFactory; import org.slf4j.Logger; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/IntrospectionTypeWiring.java b/src/main/java/org/opentripplanner/apis/gtfs/IntrospectionTypeWiring.java similarity index 98% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/IntrospectionTypeWiring.java rename to src/main/java/org/opentripplanner/apis/gtfs/IntrospectionTypeWiring.java index 9d63199450f..78a6619a6b9 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/IntrospectionTypeWiring.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/IntrospectionTypeWiring.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi; +package org.opentripplanner.apis.gtfs; import graphql.language.ObjectTypeDefinition; import graphql.language.TypeDefinition; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/AgencyImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/AgencyImpl.java similarity index 93% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/AgencyImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/AgencyImpl.java index d9ce3c03dbf..8a6d55bb078 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/AgencyImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/AgencyImpl.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.relay.Relay; import graphql.schema.DataFetcher; @@ -7,9 +7,9 @@ import java.util.Collection; import java.util.List; import java.util.stream.Collectors; -import org.opentripplanner.ext.gtfsgraphqlapi.GraphQLRequestContext; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes; +import org.opentripplanner.apis.gtfs.GraphQLRequestContext; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.routing.alertpatch.EntitySelector; import org.opentripplanner.routing.alertpatch.TransitAlert; import org.opentripplanner.routing.services.TransitAlertService; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/AlertEntityTypeResolver.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/AlertEntityTypeResolver.java similarity index 81% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/AlertEntityTypeResolver.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/AlertEntityTypeResolver.java index 4fd0f964d2a..210cfbca27a 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/AlertEntityTypeResolver.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/AlertEntityTypeResolver.java @@ -1,13 +1,13 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.TypeResolutionEnvironment; import graphql.schema.GraphQLObjectType; import graphql.schema.GraphQLSchema; import graphql.schema.TypeResolver; -import org.opentripplanner.ext.gtfsgraphqlapi.model.RouteTypeModel; -import org.opentripplanner.ext.gtfsgraphqlapi.model.StopOnRouteModel; -import org.opentripplanner.ext.gtfsgraphqlapi.model.StopOnTripModel; -import org.opentripplanner.ext.gtfsgraphqlapi.model.UnknownModel; +import org.opentripplanner.apis.gtfs.model.RouteTypeModel; +import org.opentripplanner.apis.gtfs.model.StopOnRouteModel; +import org.opentripplanner.apis.gtfs.model.StopOnTripModel; +import org.opentripplanner.apis.gtfs.model.UnknownModel; import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.organization.Agency; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/AlertImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/AlertImpl.java similarity index 92% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/AlertImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/AlertImpl.java index 5708a151c31..faf2ec80fe6 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/AlertImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/AlertImpl.java @@ -1,7 +1,7 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; -import static org.opentripplanner.ext.gtfsgraphqlapi.mapping.AlertEffectMapper.getGraphQLEffect; -import static org.opentripplanner.ext.gtfsgraphqlapi.mapping.SeverityMapper.getGraphQLSeverity; +import static org.opentripplanner.apis.gtfs.mapping.AlertEffectMapper.getGraphQLEffect; +import static org.opentripplanner.apis.gtfs.mapping.SeverityMapper.getGraphQLSeverity; import graphql.relay.Relay; import graphql.schema.DataFetcher; @@ -13,16 +13,16 @@ import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; -import org.opentripplanner.ext.gtfsgraphqlapi.GraphQLRequestContext; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLAlertEffectType; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLAlertSeverityLevelType; -import org.opentripplanner.ext.gtfsgraphqlapi.mapping.AlertCauseMapper; -import org.opentripplanner.ext.gtfsgraphqlapi.model.RouteTypeModel; -import org.opentripplanner.ext.gtfsgraphqlapi.model.StopOnRouteModel; -import org.opentripplanner.ext.gtfsgraphqlapi.model.StopOnTripModel; -import org.opentripplanner.ext.gtfsgraphqlapi.model.UnknownModel; +import org.opentripplanner.apis.gtfs.GraphQLRequestContext; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAlertEffectType; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAlertSeverityLevelType; +import org.opentripplanner.apis.gtfs.mapping.AlertCauseMapper; +import org.opentripplanner.apis.gtfs.model.RouteTypeModel; +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; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/BikeParkImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/BikeParkImpl.java similarity index 93% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/BikeParkImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/BikeParkImpl.java index f0e07a0d61e..81166034ee5 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/BikeParkImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/BikeParkImpl.java @@ -1,9 +1,9 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.relay.Relay; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.framework.graphql.GraphQLUtils; import org.opentripplanner.model.calendar.openinghours.OHCalendar; import org.opentripplanner.routing.vehicle_parking.VehicleParking; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/BikeRentalStationImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/BikeRentalStationImpl.java similarity index 96% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/BikeRentalStationImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/BikeRentalStationImpl.java index fc8545d4979..147b361986a 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/BikeRentalStationImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/BikeRentalStationImpl.java @@ -1,10 +1,10 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.relay.Relay; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import java.util.List; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace; import org.opentripplanner.service.vehiclerental.model.VehicleRentalStationUris; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/BookingInfoImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/BookingInfoImpl.java similarity index 91% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/BookingInfoImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/BookingInfoImpl.java index af06f4fbcab..9ba2c21f62d 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/BookingInfoImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/BookingInfoImpl.java @@ -1,8 +1,8 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.model.BookingInfo; import org.opentripplanner.model.BookingTime; import org.opentripplanner.transit.model.organization.ContactInfo; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/BookingTimeImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/BookingTimeImpl.java similarity index 82% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/BookingTimeImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/BookingTimeImpl.java index 796b7a6dadd..8c6e581eba4 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/BookingTimeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/BookingTimeImpl.java @@ -1,9 +1,9 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import java.time.format.DateTimeFormatter; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.model.BookingTime; public class BookingTimeImpl implements GraphQLDataFetchers.GraphQLBookingTime { diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/CarParkImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CarParkImpl.java similarity index 93% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/CarParkImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CarParkImpl.java index a03e56b668c..ff990b4f65c 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/CarParkImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CarParkImpl.java @@ -1,9 +1,9 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.relay.Relay; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.framework.graphql.GraphQLUtils; import org.opentripplanner.model.calendar.openinghours.OHCalendar; import org.opentripplanner.routing.vehicle_parking.VehicleParking; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/ContactInfoImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/ContactInfoImpl.java similarity index 89% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/ContactInfoImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/ContactInfoImpl.java index 1ec1a645b26..d19b661942f 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/ContactInfoImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/ContactInfoImpl.java @@ -1,8 +1,8 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.transit.model.organization.ContactInfo; public class ContactInfoImpl implements GraphQLDataFetchers.GraphQLContactInfo { diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/CoordinatesImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CoordinatesImpl.java similarity index 79% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/CoordinatesImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CoordinatesImpl.java index de4651f0bb1..7d12ea237b7 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/CoordinatesImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CoordinatesImpl.java @@ -1,9 +1,9 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import org.locationtech.jts.geom.Coordinate; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; public class CoordinatesImpl implements GraphQLDataFetchers.GraphQLCoordinates { diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/CurrencyImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CurrencyImpl.java similarity index 79% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/CurrencyImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CurrencyImpl.java index b4f853dd885..77bbfdaa687 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/CurrencyImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CurrencyImpl.java @@ -1,9 +1,9 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import java.util.Currency; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; public class CurrencyImpl implements GraphQLDataFetchers.GraphQLCurrency { diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/DefaultFareProductImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DefaultFareProductImpl.java similarity index 88% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/DefaultFareProductImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DefaultFareProductImpl.java index 0ba4f21fbc5..86b988fcb21 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/DefaultFareProductImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DefaultFareProductImpl.java @@ -1,8 +1,8 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.model.fare.FareMedium; import org.opentripplanner.model.fare.FareProduct; import org.opentripplanner.model.fare.RiderCategory; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/DepartureRowImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DepartureRowImpl.java similarity index 86% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/DepartureRowImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DepartureRowImpl.java index 86aa90d90c4..64b84c4c132 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/DepartureRowImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DepartureRowImpl.java @@ -1,13 +1,13 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.relay.Relay; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import java.time.Duration; -import org.opentripplanner.ext.gtfsgraphqlapi.GraphQLRequestContext; -import org.opentripplanner.ext.gtfsgraphqlapi.GraphQLUtils; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes; +import org.opentripplanner.apis.gtfs.GraphQLRequestContext; +import org.opentripplanner.apis.gtfs.GraphQLUtils; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.model.TripTimeOnDate; import org.opentripplanner.routing.graphfinder.PatternAtStop; import org.opentripplanner.routing.stoptimes.ArrivalDeparture; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/FareProductTypeResolver.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FareProductTypeResolver.java similarity index 90% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/FareProductTypeResolver.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FareProductTypeResolver.java index dc862108fbe..2a1a4c3baa0 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/FareProductTypeResolver.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FareProductTypeResolver.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.TypeResolutionEnvironment; import graphql.schema.GraphQLObjectType; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/FareProductUseImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FareProductUseImpl.java similarity index 81% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/FareProductUseImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FareProductUseImpl.java index a8d374dda4a..8d9c82bdf63 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/FareProductUseImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FareProductUseImpl.java @@ -1,8 +1,8 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.model.fare.FareProduct; import org.opentripplanner.model.fare.FareProductUse; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/FeedImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FeedImpl.java similarity index 90% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/FeedImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FeedImpl.java index 8db18f45700..7d7c62136b6 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/FeedImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FeedImpl.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; @@ -6,9 +6,9 @@ import java.util.Collection; import java.util.List; import java.util.stream.Collectors; -import org.opentripplanner.ext.gtfsgraphqlapi.GraphQLRequestContext; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes; +import org.opentripplanner.apis.gtfs.GraphQLRequestContext; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.routing.alertpatch.EntitySelector; import org.opentripplanner.routing.alertpatch.TransitAlert; import org.opentripplanner.routing.services.TransitAlertService; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/GeometryImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/GeometryImpl.java similarity index 82% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/GeometryImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/GeometryImpl.java index a1f7a4268eb..863e17f3544 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/GeometryImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/GeometryImpl.java @@ -1,9 +1,9 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import org.locationtech.jts.geom.Geometry; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.framework.geometry.EncodedPolyline; public class GeometryImpl implements GraphQLDataFetchers.GraphQLGeometry { diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/ItineraryImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/ItineraryImpl.java similarity index 93% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/ItineraryImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/ItineraryImpl.java index 5ea0ff8f895..afc78479de1 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/ItineraryImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/ItineraryImpl.java @@ -1,12 +1,12 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; -import org.opentripplanner.ext.gtfsgraphqlapi.mapping.NumberMapper; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.mapping.NumberMapper; import org.opentripplanner.model.SystemNotice; import org.opentripplanner.model.fare.ItineraryFares; import org.opentripplanner.model.plan.Itinerary; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/LegImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java similarity index 96% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/LegImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java index 8b1e0fa77f7..b13f6123d4d 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/LegImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; @@ -7,10 +7,10 @@ import java.util.stream.Collectors; import org.locationtech.jts.geom.Geometry; import org.opentripplanner.api.mapping.LocalDateMapper; -import org.opentripplanner.ext.gtfsgraphqlapi.GraphQLRequestContext; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes; -import org.opentripplanner.ext.gtfsgraphqlapi.mapping.NumberMapper; +import org.opentripplanner.apis.gtfs.GraphQLRequestContext; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; +import org.opentripplanner.apis.gtfs.mapping.NumberMapper; import org.opentripplanner.ext.ridehailing.model.RideEstimate; import org.opentripplanner.ext.ridehailing.model.RideHailingLeg; import org.opentripplanner.framework.graphql.GraphQLUtils; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/MoneyImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/MoneyImpl.java similarity index 80% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/MoneyImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/MoneyImpl.java index 15f97ddb729..9dbebc13ff2 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/MoneyImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/MoneyImpl.java @@ -1,9 +1,9 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import java.util.Currency; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.transit.model.basic.Money; public class MoneyImpl implements GraphQLDataFetchers.GraphQLMoney { diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/NodeTypeResolver.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/NodeTypeResolver.java similarity index 98% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/NodeTypeResolver.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/NodeTypeResolver.java index c63f4a6b611..437d75e03e9 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/NodeTypeResolver.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/NodeTypeResolver.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.TypeResolutionEnvironment; import graphql.language.InlineFragment; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/OpeningHoursImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/OpeningHoursImpl.java similarity index 85% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/OpeningHoursImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/OpeningHoursImpl.java index 382e2170a41..9960c90fcb6 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/OpeningHoursImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/OpeningHoursImpl.java @@ -1,8 +1,8 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.model.calendar.openinghours.OHCalendar; import org.opentripplanner.model.calendar.openinghours.OsmOpeningHoursSupport; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/PatternImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/PatternImpl.java similarity index 96% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/PatternImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/PatternImpl.java index f38a024b721..ad16e8718d2 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/PatternImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/PatternImpl.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import gnu.trove.set.TIntSet; import graphql.relay.Relay; @@ -14,9 +14,9 @@ import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.LineString; import org.opentripplanner.api.support.SemanticHash; -import org.opentripplanner.ext.gtfsgraphqlapi.GraphQLRequestContext; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes; +import org.opentripplanner.apis.gtfs.GraphQLRequestContext; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.framework.graphql.GraphQLUtils; import org.opentripplanner.framework.time.ServiceDateUtils; import org.opentripplanner.routing.alertpatch.EntitySelector; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/PlaceImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/PlaceImpl.java similarity index 92% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/PlaceImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/PlaceImpl.java index c9a40debbb2..ce3ca0986b1 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/PlaceImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/PlaceImpl.java @@ -1,11 +1,11 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLVertexType; -import org.opentripplanner.ext.gtfsgraphqlapi.model.StopPosition; -import org.opentripplanner.ext.gtfsgraphqlapi.model.StopPosition.PositionAtStop; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLVertexType; +import org.opentripplanner.apis.gtfs.model.StopPosition; +import org.opentripplanner.apis.gtfs.model.StopPosition.PositionAtStop; import org.opentripplanner.framework.graphql.GraphQLUtils; import org.opentripplanner.model.plan.Place; import org.opentripplanner.model.plan.StopArrival; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/PlaceInterfaceTypeResolver.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/PlaceInterfaceTypeResolver.java similarity index 77% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/PlaceInterfaceTypeResolver.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/PlaceInterfaceTypeResolver.java index d79e758cd4c..67ec004759d 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/PlaceInterfaceTypeResolver.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/PlaceInterfaceTypeResolver.java @@ -1,6 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; - -import static org.opentripplanner.ext.gtfsgraphqlapi.datafetchers.NodeTypeResolver.queryContainsFragment; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.TypeResolutionEnvironment; import graphql.schema.GraphQLObjectType; @@ -21,16 +19,22 @@ public GraphQLObjectType getType(TypeResolutionEnvironment environment) { if (o instanceof VehicleParking) { var vehicleParking = (VehicleParking) o; - if (queryContainsFragment("BikePark", environment) && vehicleParking.hasBicyclePlaces()) { + if ( + NodeTypeResolver.queryContainsFragment("BikePark", environment) && + vehicleParking.hasBicyclePlaces() + ) { return schema.getObjectType("BikePark"); } - if (queryContainsFragment("CarPark", environment) && vehicleParking.hasAnyCarPlaces()) { + if ( + NodeTypeResolver.queryContainsFragment("CarPark", environment) && + vehicleParking.hasAnyCarPlaces() + ) { return schema.getObjectType("CarPark"); } return schema.getObjectType("VehicleParking"); } if (o instanceof VehicleRentalStation) { - if (queryContainsFragment("BikeRentalStation", environment)) { + if (NodeTypeResolver.queryContainsFragment("BikeRentalStation", environment)) { return schema.getObjectType("BikeRentalStation"); } return schema.getObjectType("VehicleRentalStation"); diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/PlanImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/PlanImpl.java similarity index 96% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/PlanImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/PlanImpl.java index 064bd071dd6..4e5c4584495 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/PlanImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/PlanImpl.java @@ -1,11 +1,11 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import java.util.stream.Collectors; import org.opentripplanner.api.mapping.PlannerErrorMapper; import org.opentripplanner.api.resource.DebugOutput; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.model.plan.Itinerary; import org.opentripplanner.model.plan.StopArrival; import org.opentripplanner.model.plan.pagecursor.PageCursor; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/QueryTypeImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java similarity index 97% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/QueryTypeImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java index 11a297a0011..03b9fd75188 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/QueryTypeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java @@ -1,8 +1,8 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; -import static org.opentripplanner.ext.gtfsgraphqlapi.mapping.AlertCauseMapper.getGraphQLCause; -import static org.opentripplanner.ext.gtfsgraphqlapi.mapping.AlertEffectMapper.getGraphQLEffect; -import static org.opentripplanner.ext.gtfsgraphqlapi.mapping.SeverityMapper.getGraphQLSeverity; +import static org.opentripplanner.apis.gtfs.mapping.AlertCauseMapper.getGraphQLCause; +import static org.opentripplanner.apis.gtfs.mapping.AlertEffectMapper.getGraphQLEffect; +import static org.opentripplanner.apis.gtfs.mapping.SeverityMapper.getGraphQLSeverity; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimaps; @@ -24,14 +24,14 @@ import java.util.stream.Stream; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Envelope; +import org.opentripplanner.apis.gtfs.GraphQLRequestContext; +import org.opentripplanner.apis.gtfs.GraphQLUtils; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; +import org.opentripplanner.apis.gtfs.mapping.RouteRequestMapper; import org.opentripplanner.ext.fares.impl.DefaultFareService; import org.opentripplanner.ext.fares.impl.GtfsFaresService; import org.opentripplanner.ext.fares.model.FareRuleSet; -import org.opentripplanner.ext.gtfsgraphqlapi.GraphQLRequestContext; -import org.opentripplanner.ext.gtfsgraphqlapi.GraphQLUtils; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes; -import org.opentripplanner.ext.gtfsgraphqlapi.mapping.RouteRequestMapper; import org.opentripplanner.framework.time.ServiceDateUtils; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.gtfs.mapping.DirectionMapper; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/RentalVehicleImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RentalVehicleImpl.java similarity index 93% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/RentalVehicleImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RentalVehicleImpl.java index ead62691036..53c3ba1343d 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/RentalVehicleImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RentalVehicleImpl.java @@ -1,9 +1,9 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.relay.Relay; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.service.vehiclerental.model.RentalVehicleType; import org.opentripplanner.service.vehiclerental.model.VehicleRentalStationUris; import org.opentripplanner.service.vehiclerental.model.VehicleRentalVehicle; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/RentalVehicleTypeImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RentalVehicleTypeImpl.java similarity index 82% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/RentalVehicleTypeImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RentalVehicleTypeImpl.java index 04e3d8470a2..204410644b3 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/RentalVehicleTypeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RentalVehicleTypeImpl.java @@ -1,10 +1,10 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers.GraphQLRentalVehicleType; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLFormFactor; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLPropulsionType; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers.GraphQLRentalVehicleType; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLFormFactor; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLPropulsionType; import org.opentripplanner.service.vehiclerental.model.RentalVehicleType; public class RentalVehicleTypeImpl implements GraphQLRentalVehicleType { diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/RideHailingEstimateImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RideHailingEstimateImpl.java similarity index 82% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/RideHailingEstimateImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RideHailingEstimateImpl.java index 0b36f182fe1..9813a3324ca 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/RideHailingEstimateImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RideHailingEstimateImpl.java @@ -1,10 +1,10 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import java.time.Duration; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; -import org.opentripplanner.ext.gtfsgraphqlapi.model.RideHailingProvider; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.model.RideHailingProvider; import org.opentripplanner.ext.ridehailing.model.RideEstimate; import org.opentripplanner.transit.model.basic.Money; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/RouteImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java similarity index 94% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/RouteImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java index c61020db759..0fe93a59602 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/RouteImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.relay.Relay; import graphql.schema.DataFetcher; @@ -7,11 +7,11 @@ import java.util.Collection; import java.util.List; import java.util.stream.Collectors; -import org.opentripplanner.ext.gtfsgraphqlapi.GraphQLRequestContext; -import org.opentripplanner.ext.gtfsgraphqlapi.GraphQLUtils; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLTransitMode; +import org.opentripplanner.apis.gtfs.GraphQLRequestContext; +import org.opentripplanner.apis.gtfs.GraphQLUtils; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLTransitMode; import org.opentripplanner.routing.alertpatch.EntitySelector; import org.opentripplanner.routing.alertpatch.TransitAlert; import org.opentripplanner.routing.services.TransitAlertService; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/RouteTypeImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteTypeImpl.java similarity index 83% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/RouteTypeImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteTypeImpl.java index db1f2d95878..bd8b9a7e019 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/RouteTypeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteTypeImpl.java @@ -1,11 +1,11 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import java.util.stream.Collectors; -import org.opentripplanner.ext.gtfsgraphqlapi.GraphQLRequestContext; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; -import org.opentripplanner.ext.gtfsgraphqlapi.model.RouteTypeModel; +import org.opentripplanner.apis.gtfs.GraphQLRequestContext; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.model.RouteTypeModel; import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.transit.model.organization.Agency; import org.opentripplanner.transit.service.TransitService; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/RoutingErrorImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RoutingErrorImpl.java similarity index 75% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/RoutingErrorImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RoutingErrorImpl.java index 88cb4142800..7c516930888 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/RoutingErrorImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RoutingErrorImpl.java @@ -1,12 +1,12 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; -import static org.opentripplanner.ext.gtfsgraphqlapi.GraphQLUtils.toGraphQL; +import static org.opentripplanner.apis.gtfs.GraphQLUtils.toGraphQL; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import org.opentripplanner.api.mapping.PlannerErrorMapper; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.routing.api.response.RoutingError; public class RoutingErrorImpl implements GraphQLDataFetchers.GraphQLRoutingError { diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/StopGeometriesImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StopGeometriesImpl.java similarity index 82% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/StopGeometriesImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StopGeometriesImpl.java index e1cbdfbe567..d463dfa2007 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/StopGeometriesImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StopGeometriesImpl.java @@ -1,10 +1,10 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import java.util.ArrayList; import org.locationtech.jts.geom.Geometry; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers.GraphQLStopGeometries; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers.GraphQLStopGeometries; public class StopGeometriesImpl implements GraphQLStopGeometries { diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/StopImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StopImpl.java similarity index 92% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/StopImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StopImpl.java index 99a6af7a140..9353027439b 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/StopImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StopImpl.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.relay.Relay; import graphql.schema.DataFetcher; @@ -14,12 +14,10 @@ import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.opentripplanner.ext.gtfsgraphqlapi.GraphQLRequestContext; -import org.opentripplanner.ext.gtfsgraphqlapi.GraphQLUtils; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLStopAlertType; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLWheelchairBoarding; +import org.opentripplanner.apis.gtfs.GraphQLRequestContext; +import org.opentripplanner.apis.gtfs.GraphQLUtils; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.framework.time.ServiceDateUtils; import org.opentripplanner.model.StopTimesInPattern; import org.opentripplanner.model.TripTimeOnDate; @@ -49,12 +47,12 @@ public DataFetcher> alerts() { FeedScopedId id = getValue(environment, StopLocation::getId, AbstractTransitEntity::getId); if (types != null) { Collection alerts = new ArrayList<>(); - if (types.contains(GraphQLStopAlertType.STOP)) { + if (types.contains(GraphQLTypes.GraphQLStopAlertType.STOP)) { alerts.addAll(alertService.getStopAlerts(id)); } if ( - types.contains(GraphQLStopAlertType.STOP_ON_ROUTES) || - types.contains(GraphQLStopAlertType.STOP_ON_TRIPS) + types.contains(GraphQLTypes.GraphQLStopAlertType.STOP_ON_ROUTES) || + types.contains(GraphQLTypes.GraphQLStopAlertType.STOP_ON_TRIPS) ) { alerts.addAll( alertService @@ -66,12 +64,12 @@ public DataFetcher> alerts() { .stream() .anyMatch(entity -> ( - types.contains(GraphQLStopAlertType.STOP_ON_ROUTES) && + types.contains(GraphQLTypes.GraphQLStopAlertType.STOP_ON_ROUTES) && entity instanceof StopAndRoute stopAndRoute && stopAndRoute.stopId().equals(id) ) || ( - types.contains(GraphQLStopAlertType.STOP_ON_TRIPS) && + types.contains(GraphQLTypes.GraphQLStopAlertType.STOP_ON_TRIPS) && entity instanceof EntitySelector.StopAndTrip stopAndTrip && stopAndTrip.stopId().equals(id) ) @@ -81,12 +79,12 @@ public DataFetcher> alerts() { ); } if ( - types.contains(GraphQLStopAlertType.PATTERNS) || - types.contains(GraphQLStopAlertType.TRIPS) + types.contains(GraphQLTypes.GraphQLStopAlertType.PATTERNS) || + types.contains(GraphQLTypes.GraphQLStopAlertType.TRIPS) ) { getPatterns(environment) .forEach(pattern -> { - if (types.contains(GraphQLStopAlertType.PATTERNS)) { + if (types.contains(GraphQLTypes.GraphQLStopAlertType.PATTERNS)) { alerts.addAll( alertService.getDirectionAndRouteAlerts( pattern.getDirection(), @@ -94,7 +92,7 @@ public DataFetcher> alerts() { ) ); } - if (types.contains(GraphQLStopAlertType.TRIPS)) { + if (types.contains(GraphQLTypes.GraphQLStopAlertType.TRIPS)) { pattern .scheduledTripsAsStream() .forEach(trip -> alerts.addAll(alertService.getTripAlerts(trip.getId(), null))); @@ -102,15 +100,15 @@ public DataFetcher> alerts() { }); } if ( - types.contains(GraphQLStopAlertType.ROUTES) || - types.contains(GraphQLStopAlertType.AGENCIES_OF_ROUTES) + types.contains(GraphQLTypes.GraphQLStopAlertType.ROUTES) || + types.contains(GraphQLTypes.GraphQLStopAlertType.AGENCIES_OF_ROUTES) ) { getRoutes(environment) .forEach(route -> { - if (types.contains(GraphQLStopAlertType.ROUTES)) { + if (types.contains(GraphQLTypes.GraphQLStopAlertType.ROUTES)) { alerts.addAll(alertService.getRouteAlerts(route.getId())); } - if (types.contains(GraphQLStopAlertType.AGENCIES_OF_ROUTES)) { + if (types.contains(GraphQLTypes.GraphQLStopAlertType.AGENCIES_OF_ROUTES)) { alerts.addAll(alertService.getAgencyAlerts(route.getAgency().getId())); } }); @@ -463,7 +461,7 @@ public DataFetcher vehicleType() { } @Override - public DataFetcher wheelchairBoarding() { + public DataFetcher wheelchairBoarding() { return environment -> { var boarding = getValue( environment, diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/StopOnRouteImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StopOnRouteImpl.java similarity index 73% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/StopOnRouteImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StopOnRouteImpl.java index e76a58d14d7..e1b7f814eb4 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/StopOnRouteImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StopOnRouteImpl.java @@ -1,9 +1,9 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; -import org.opentripplanner.ext.gtfsgraphqlapi.model.StopOnRouteModel; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.model.StopOnRouteModel; import org.opentripplanner.transit.model.network.Route; public class StopOnRouteImpl implements GraphQLDataFetchers.GraphQLStopOnRoute { diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/StopOnTripImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StopOnTripImpl.java similarity index 73% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/StopOnTripImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StopOnTripImpl.java index 74c0469cbdd..aa96397d34c 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/StopOnTripImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StopOnTripImpl.java @@ -1,9 +1,9 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; -import org.opentripplanner.ext.gtfsgraphqlapi.model.StopOnTripModel; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.model.StopOnTripModel; import org.opentripplanner.transit.model.timetable.Trip; public class StopOnTripImpl implements GraphQLDataFetchers.GraphQLStopOnTrip { diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StopRelationshipImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StopRelationshipImpl.java new file mode 100644 index 00000000000..3625c025148 --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StopRelationshipImpl.java @@ -0,0 +1,29 @@ +package org.opentripplanner.apis.gtfs.datafetchers; + +import graphql.schema.DataFetcher; +import graphql.schema.DataFetchingEnvironment; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; +import org.opentripplanner.service.vehiclepositions.model.RealtimeVehiclePosition.StopRelationship; + +public class StopRelationshipImpl implements GraphQLDataFetchers.GraphQLStopRelationship { + + @Override + public DataFetcher status() { + return env -> + switch (getSource(env).status()) { + case INCOMING_AT -> GraphQLTypes.GraphQLVehicleStopStatus.INCOMING_AT; + case IN_TRANSIT_TO -> GraphQLTypes.GraphQLVehicleStopStatus.IN_TRANSIT_TO; + case STOPPED_AT -> GraphQLTypes.GraphQLVehicleStopStatus.STOPPED_AT; + }; + } + + @Override + public DataFetcher stop() { + return env -> getSource(env).stop(); + } + + private StopRelationship getSource(DataFetchingEnvironment environment) { + return environment.getSource(); + } +} diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/StoptimeImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StoptimeImpl.java similarity index 96% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/StoptimeImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StoptimeImpl.java index 6632e10d74d..a74d4f9461d 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/StoptimeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StoptimeImpl.java @@ -1,8 +1,8 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.framework.graphql.GraphQLUtils; import org.opentripplanner.model.StopTime; import org.opentripplanner.model.TripTimeOnDate; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/StoptimesInPatternImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StoptimesInPatternImpl.java similarity index 83% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/StoptimesInPatternImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StoptimesInPatternImpl.java index ecbaf7c24d8..6f76e8fee71 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/StoptimesInPatternImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StoptimesInPatternImpl.java @@ -1,8 +1,8 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.model.StopTimesInPattern; import org.opentripplanner.model.TripTimeOnDate; import org.opentripplanner.transit.model.network.TripPattern; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/SystemNoticeImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/SystemNoticeImpl.java similarity index 79% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/SystemNoticeImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/SystemNoticeImpl.java index a108ef05e22..4721b41b9e1 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/SystemNoticeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/SystemNoticeImpl.java @@ -1,8 +1,8 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.model.SystemNotice; public class SystemNoticeImpl implements GraphQLDataFetchers.GraphQLSystemNotice { diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/TicketTypeImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TicketTypeImpl.java similarity index 91% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/TicketTypeImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TicketTypeImpl.java index c5012c693cf..1d1c0082126 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/TicketTypeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TicketTypeImpl.java @@ -1,11 +1,11 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.relay.Relay; import graphql.schema.DataFetcher; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.ext.fares.model.FareRuleSet; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; public class TicketTypeImpl implements GraphQLDataFetchers.GraphQLTicketType { diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/TranslatedStringImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TranslatedStringImpl.java similarity index 80% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/TranslatedStringImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TranslatedStringImpl.java index bdb874dcb66..2cb8bf2cb97 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/TranslatedStringImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TranslatedStringImpl.java @@ -1,9 +1,9 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import java.util.Map; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; public class TranslatedStringImpl implements GraphQLDataFetchers.GraphQLTranslatedString { diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/TripImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripImpl.java similarity index 96% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/TripImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripImpl.java index a12cedff82c..af3a59182b3 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/TripImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripImpl.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.relay.Relay; import graphql.schema.DataFetcher; @@ -16,11 +16,10 @@ import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.LineString; import org.opentripplanner.api.support.SemanticHash; -import org.opentripplanner.ext.gtfsgraphqlapi.GraphQLRequestContext; -import org.opentripplanner.ext.gtfsgraphqlapi.GraphQLUtils; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLWheelchairBoarding; +import org.opentripplanner.apis.gtfs.GraphQLRequestContext; +import org.opentripplanner.apis.gtfs.GraphQLUtils; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.framework.time.ServiceDateUtils; import org.opentripplanner.model.Timetable; import org.opentripplanner.model.TripTimeOnDate; @@ -368,7 +367,7 @@ public DataFetcher tripShortName() { } @Override - public DataFetcher wheelchairAccessible() { + public DataFetcher wheelchairAccessible() { return environment -> GraphQLUtils.toGraphQL(getSource(environment).getWheelchairBoarding()); } diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/UnknownImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/UnknownImpl.java similarity index 66% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/UnknownImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/UnknownImpl.java index 7e9c5cc4b4a..afb41ae0e8a 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/UnknownImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/UnknownImpl.java @@ -1,9 +1,9 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; -import org.opentripplanner.ext.gtfsgraphqlapi.model.UnknownModel; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.model.UnknownModel; public class UnknownImpl implements GraphQLDataFetchers.GraphQLUnknown { diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/VehicleParkingImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/VehicleParkingImpl.java similarity index 95% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/VehicleParkingImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/VehicleParkingImpl.java index eb89c0ac816..0b55beac689 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/VehicleParkingImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/VehicleParkingImpl.java @@ -1,9 +1,9 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.relay.Relay; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.framework.graphql.GraphQLUtils; import org.opentripplanner.model.calendar.openinghours.OHCalendar; import org.opentripplanner.routing.vehicle_parking.VehicleParking; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/VehiclePositionImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/VehiclePositionImpl.java similarity index 86% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/VehiclePositionImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/VehiclePositionImpl.java index 861c4b61310..0118eaa4a1a 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/VehiclePositionImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/VehiclePositionImpl.java @@ -1,13 +1,13 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers.GraphQLVehiclePosition; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.service.vehiclepositions.model.RealtimeVehiclePosition; import org.opentripplanner.service.vehiclepositions.model.RealtimeVehiclePosition.StopRelationship; import org.opentripplanner.transit.model.timetable.Trip; -public class VehiclePositionImpl implements GraphQLVehiclePosition { +public class VehiclePositionImpl implements GraphQLDataFetchers.GraphQLVehiclePosition { @Override public DataFetcher heading() { diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/VehicleRentalStationImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/VehicleRentalStationImpl.java similarity index 95% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/VehicleRentalStationImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/VehicleRentalStationImpl.java index be3dd2b86c5..c1256f15c3f 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/VehicleRentalStationImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/VehicleRentalStationImpl.java @@ -1,9 +1,9 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.relay.Relay; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace; import org.opentripplanner.service.vehiclerental.model.VehicleRentalStationUris; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/debugOutputImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/debugOutputImpl.java similarity index 88% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/debugOutputImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/debugOutputImpl.java index 4ac67031ef8..aa6e24c04f9 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/debugOutputImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/debugOutputImpl.java @@ -1,9 +1,9 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import org.opentripplanner.api.resource.DebugOutput; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; public class debugOutputImpl implements GraphQLDataFetchers.GraphQLDebugOutput { diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/elevationProfileComponentImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/elevationProfileComponentImpl.java similarity index 81% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/elevationProfileComponentImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/elevationProfileComponentImpl.java index 8acd294fd3d..12cfd7eddc4 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/elevationProfileComponentImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/elevationProfileComponentImpl.java @@ -1,8 +1,8 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.model.plan.ElevationProfile; public class elevationProfileComponentImpl diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/fareComponentImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/fareComponentImpl.java similarity index 87% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/fareComponentImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/fareComponentImpl.java index 586e7b68dcf..a8c1b4d38f5 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/fareComponentImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/fareComponentImpl.java @@ -1,10 +1,10 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import java.util.stream.Collectors; -import org.opentripplanner.ext.gtfsgraphqlapi.GraphQLRequestContext; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.GraphQLRequestContext; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.routing.core.FareComponent; import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.transit.service.TransitService; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/fareImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/fareImpl.java similarity index 87% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/fareImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/fareImpl.java index 2ff0aca03a8..e1986d215be 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/fareImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/fareImpl.java @@ -1,9 +1,9 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import java.util.Map; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.routing.core.FareComponent; import org.opentripplanner.transit.model.basic.Money; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/placeAtDistanceImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/placeAtDistanceImpl.java similarity index 94% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/placeAtDistanceImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/placeAtDistanceImpl.java index 89489134b31..4c137cd3769 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/placeAtDistanceImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/placeAtDistanceImpl.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.execution.TypeResolutionParameters; import graphql.relay.Relay; @@ -9,7 +9,7 @@ import graphql.schema.GraphQLInterfaceType; import graphql.schema.GraphQLObjectType; import graphql.schema.TypeResolver; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.routing.graphfinder.PlaceAtDistance; public class placeAtDistanceImpl implements GraphQLDataFetchers.GraphQLPlaceAtDistance { diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/serviceTimeRangeImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/serviceTimeRangeImpl.java similarity index 77% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/serviceTimeRangeImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/serviceTimeRangeImpl.java index 45d6356688b..fe0ec3f56ee 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/serviceTimeRangeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/serviceTimeRangeImpl.java @@ -1,9 +1,9 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; -import org.opentripplanner.ext.gtfsgraphqlapi.GraphQLRequestContext; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.GraphQLRequestContext; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.transit.service.TransitService; public class serviceTimeRangeImpl implements GraphQLDataFetchers.GraphQLServiceTimeRange { diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/stepImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/stepImpl.java similarity index 78% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/stepImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/stepImpl.java index ed724c13609..6bd51ae5f29 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/stepImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/stepImpl.java @@ -1,12 +1,11 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLAbsoluteDirection; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLRelativeDirection; -import org.opentripplanner.ext.gtfsgraphqlapi.mapping.DirectionMapper; -import org.opentripplanner.ext.gtfsgraphqlapi.mapping.StreetNoteMapper; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; +import org.opentripplanner.apis.gtfs.mapping.DirectionMapper; +import org.opentripplanner.apis.gtfs.mapping.StreetNoteMapper; import org.opentripplanner.model.plan.ElevationProfile.Step; import org.opentripplanner.model.plan.WalkStep; import org.opentripplanner.routing.alertpatch.TransitAlert; @@ -14,7 +13,7 @@ public class stepImpl implements GraphQLDataFetchers.GraphQLStep { @Override - public DataFetcher absoluteDirection() { + public DataFetcher absoluteDirection() { return environment -> getSource(environment).getAbsoluteDirection().map(DirectionMapper::map).orElse(null); } @@ -65,7 +64,7 @@ public DataFetcher lon() { } @Override - public DataFetcher relativeDirection() { + public DataFetcher relativeDirection() { return environment -> DirectionMapper.map(getSource(environment).getRelativeDirection()); } diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/stopAtDistanceImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/stopAtDistanceImpl.java similarity index 86% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/stopAtDistanceImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/stopAtDistanceImpl.java index 1b599696181..192c4fa17c8 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/stopAtDistanceImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/stopAtDistanceImpl.java @@ -1,9 +1,9 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import graphql.relay.Relay; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.routing.graphfinder.NearbyStop; public class stopAtDistanceImpl implements GraphQLDataFetchers.GraphQLStopAtDistance { diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/generated/GraphQLDataFetchers.java b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java similarity index 94% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/generated/GraphQLDataFetchers.java rename to src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java index 318c4fc2a11..68d1f84827d 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/generated/GraphQLDataFetchers.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -1,5 +1,5 @@ //THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -package org.opentripplanner.ext.gtfsgraphqlapi.generated; +package org.opentripplanner.apis.gtfs.generated; import graphql.relay.Connection; import graphql.relay.Edge; @@ -10,17 +10,9 @@ import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; import org.opentripplanner.api.resource.DebugOutput; +import org.opentripplanner.apis.gtfs.model.RideHailingProvider; +import org.opentripplanner.apis.gtfs.model.StopPosition; import org.opentripplanner.ext.fares.model.FareRuleSet; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLAbsoluteDirection; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLAlertCauseType; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLAlertEffectType; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLAlertSeverityLevelType; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLInputField; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLRelativeDirection; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLRoutingErrorCode; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLTransitMode; -import org.opentripplanner.ext.gtfsgraphqlapi.model.RideHailingProvider; -import org.opentripplanner.ext.gtfsgraphqlapi.model.StopPosition; import org.opentripplanner.ext.ridehailing.model.RideEstimate; import org.opentripplanner.model.StopTimesInPattern; import org.opentripplanner.model.SystemNotice; @@ -85,13 +77,13 @@ public interface GraphQLAgency { public interface GraphQLAlert { public DataFetcher agency(); - public DataFetcher alertCause(); + public DataFetcher alertCause(); public DataFetcher alertDescriptionText(); public DataFetcher>> alertDescriptionTextTranslations(); - public DataFetcher alertEffect(); + public DataFetcher alertEffect(); public DataFetcher alertHash(); @@ -99,7 +91,7 @@ public interface GraphQLAlert { public DataFetcher>> alertHeaderTextTranslations(); - public DataFetcher alertSeverityLevel(); + public DataFetcher alertSeverityLevel(); public DataFetcher alertUrl(); @@ -748,9 +740,9 @@ public interface GraphQLRentalVehicle { } public interface GraphQLRentalVehicleType { - public DataFetcher formFactor(); + public DataFetcher formFactor(); - public DataFetcher propulsionType(); + public DataFetcher propulsionType(); } /** An estimate for a ride on a hailed vehicle, like an Uber car. */ @@ -801,7 +793,7 @@ public interface GraphQLRoute { public DataFetcher longName(); - public DataFetcher mode(); + public DataFetcher mode(); public DataFetcher> patterns(); @@ -832,11 +824,11 @@ public interface GraphQLRouteType { /** Description of the reason, why the planner did not return any results */ public interface GraphQLRoutingError { - public DataFetcher code(); + public DataFetcher code(); public DataFetcher description(); - public DataFetcher inputField(); + public DataFetcher inputField(); } /** @@ -897,7 +889,7 @@ public interface GraphQLStop { public DataFetcher vehicleType(); - public DataFetcher wheelchairBoarding(); + public DataFetcher wheelchairBoarding(); public DataFetcher zoneId(); } @@ -1053,7 +1045,7 @@ public interface GraphQLTrip { public DataFetcher tripShortName(); - public DataFetcher wheelchairAccessible(); + public DataFetcher wheelchairAccessible(); } /** This is used for alert entities that we don't explicitly handle or they are missing. */ @@ -1244,7 +1236,7 @@ public interface GraphQLServiceTimeRange { } public interface GraphQLStep { - public DataFetcher absoluteDirection(); + public DataFetcher absoluteDirection(); public DataFetcher> alerts(); @@ -1262,7 +1254,7 @@ public interface GraphQLStep { public DataFetcher lon(); - public DataFetcher relativeDirection(); + public DataFetcher relativeDirection(); public DataFetcher stayOn(); diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/generated/GraphQLTypes.java b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java similarity index 99% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/generated/GraphQLTypes.java rename to src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java index 3861f0e49ef..3593145e274 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/generated/GraphQLTypes.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java @@ -1,5 +1,5 @@ // THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -package org.opentripplanner.ext.gtfsgraphqlapi.generated; +package org.opentripplanner.apis.gtfs.generated; import java.util.List; import java.util.Map; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/generated/README.md b/src/main/java/org/opentripplanner/apis/gtfs/generated/README.md similarity index 100% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/generated/README.md rename to src/main/java/org/opentripplanner/apis/gtfs/generated/README.md diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/generated/graphql-codegen.yml b/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml similarity index 72% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/generated/graphql-codegen.yml rename to src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml index 7408e72cbca..2f068befe90 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/generated/graphql-codegen.yml +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml @@ -18,7 +18,7 @@ generates: className: GraphQLDataFetchers config: - package: org.opentripplanner.ext.gtfsgraphqlapi.generated + package: org.opentripplanner.apis.gtfs.generated typesPrefix: GraphQL scalars: ID: graphql.relay.Relay.ResolvedGlobalId @@ -27,13 +27,13 @@ config: GeoJson: org.locationtech.jts.geom.Geometry Duration: java.time.Duration mappers: - AbsoluteDirection: org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLAbsoluteDirection#GraphQLAbsoluteDirection + AbsoluteDirection: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAbsoluteDirection#GraphQLAbsoluteDirection Agency: org.opentripplanner.transit.model.organization.Agency#Agency - RouteType: org.opentripplanner.ext.gtfsgraphqlapi.model.RouteTypeModel#RouteTypeModel + RouteType: org.opentripplanner.apis.gtfs.model.RouteTypeModel#RouteTypeModel Alert: org.opentripplanner.routing.alertpatch.TransitAlert#TransitAlert - AlertCauseType: org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLAlertCauseType#GraphQLAlertCauseType - AlertEffectType: org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLAlertEffectType#GraphQLAlertEffectType - AlertSeverityLevelType: org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLAlertSeverityLevelType#GraphQLAlertSeverityLevelType + AlertCauseType: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAlertCauseType#GraphQLAlertCauseType + AlertEffectType: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAlertEffectType#GraphQLAlertEffectType + AlertSeverityLevelType: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAlertSeverityLevelType#GraphQLAlertSeverityLevelType BikePark: org.opentripplanner.routing.vehicle_parking.VehicleParking#VehicleParking BikeRentalStation: org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace#VehicleRentalPlace BikeRentalStationUris: org.opentripplanner.service.vehiclerental.model.VehicleRentalStationUris#VehicleRentalStationUris @@ -54,11 +54,11 @@ config: fareComponent: org.opentripplanner.routing.core.FareComponent#FareComponent Feed: String Geometry: org.locationtech.jts.geom.Geometry#Geometry - InputField: org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLInputField#GraphQLInputField + InputField: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLInputField#GraphQLInputField Itinerary: org.opentripplanner.model.plan.Itinerary#Itinerary Leg: org.opentripplanner.model.plan.Leg#Leg Mode: String - TransitMode: org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLTransitMode#GraphQLTransitMode + TransitMode: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLTransitMode#GraphQLTransitMode PageInfo: Object Pattern: org.opentripplanner.transit.model.network.TripPattern#TripPattern PickupDropoffType: String @@ -68,24 +68,24 @@ config: placeAtDistanceEdge: graphql.relay.Edge#Edge Plan: graphql.execution.DataFetcherResult RealtimeState: String - RelativeDirection: org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLRelativeDirection#GraphQLRelativeDirection + RelativeDirection: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRelativeDirection#GraphQLRelativeDirection Route: org.opentripplanner.transit.model.network.Route#Route RoutingError: org.opentripplanner.routing.api.response.RoutingError#RoutingError - RoutingErrorCode: org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLRoutingErrorCode#GraphQLRoutingErrorCode + RoutingErrorCode: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRoutingErrorCode#GraphQLRoutingErrorCode serviceTimeRange: Object step: org.opentripplanner.model.plan.WalkStep#WalkStep Stop: Object # Can be either Stop or Station stopAtDistance: org.opentripplanner.routing.graphfinder.NearbyStop#NearbyStop stopAtDistanceConnection: graphql.relay.Connection#Connection stopAtDistanceEdge: graphql.relay.Edge#Edge - StopOnRoute: org.opentripplanner.ext.gtfsgraphqlapi.model.StopOnRouteModel#StopOnRouteModel - StopOnTrip: org.opentripplanner.ext.gtfsgraphqlapi.model.StopOnTripModel#StopOnTripModel + StopOnRoute: org.opentripplanner.apis.gtfs.model.StopOnRouteModel#StopOnRouteModel + StopOnTrip: org.opentripplanner.apis.gtfs.model.StopOnTripModel#StopOnTripModel Stoptime: org.opentripplanner.model.TripTimeOnDate#TripTimeOnDate StoptimesInPattern: org.opentripplanner.model.StopTimesInPattern#StopTimesInPattern TicketType: org.opentripplanner.ext.fares.model.FareRuleSet#FareRuleSet TranslatedString: java.util.Map#Map.Entry Trip: org.opentripplanner.transit.model.timetable.Trip#Trip - Unknown: org.opentripplanner.ext.gtfsgraphqlapi.model.UnknownModel#UnknownModel + Unknown: org.opentripplanner.apis.gtfs.model.UnknownModel#UnknownModel VehicleParking: org.opentripplanner.routing.vehicle_parking.VehicleParking#VehicleParking VehicleParkingSpaces: org.opentripplanner.routing.vehicle_parking.VehicleParkingSpaces#VehicleParkingSpaces VehicleParkingState: org.opentripplanner.routing.vehicle_parking.VehicleParkingState#VehicleParkingState @@ -93,17 +93,17 @@ config: SystemNotice: org.opentripplanner.model.SystemNotice#SystemNotice VehiclePosition: org.opentripplanner.service.vehiclepositions.model.RealtimeVehiclePosition#RealtimeVehiclePosition StopRelationship: org.opentripplanner.service.vehiclepositions.model.RealtimeVehiclePosition.StopRelationship#StopRelationship - WheelchairBoarding: org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLWheelchairBoarding - FormFactor: org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLFormFactor - PropulsionType: org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLPropulsionType + WheelchairBoarding: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLWheelchairBoarding + FormFactor: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLFormFactor + PropulsionType: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLPropulsionType RentalVehicleType: org.opentripplanner.service.vehiclerental.model.RentalVehicleType#RentalVehicleType OpeningHours: org.opentripplanner.model.calendar.openinghours.OHCalendar#OHCalendar RideHailingEstimate: org.opentripplanner.ext.ridehailing.model.RideEstimate#RideEstimate - RideHailingProvider: org.opentripplanner.ext.gtfsgraphqlapi.model.RideHailingProvider#RideHailingProvider + RideHailingProvider: org.opentripplanner.apis.gtfs.model.RideHailingProvider#RideHailingProvider Currency: java.util.Currency#Currency Money: org.opentripplanner.transit.model.basic.Money#Money FareProductUse: org.opentripplanner.model.fare.FareProductUse#FareProductUse FareProduct: org.opentripplanner.model.fare.FareProduct#FareProduct FareMedium: org.opentripplanner.model.fare.FareMedium#FareMedium RiderCategory: org.opentripplanner.model.fare.RiderCategory#RiderCategory - StopPosition: org.opentripplanner.ext.gtfsgraphqlapi.model.StopPosition#StopPosition + StopPosition: org.opentripplanner.apis.gtfs.model.StopPosition#StopPosition diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/generated/package.json b/src/main/java/org/opentripplanner/apis/gtfs/generated/package.json similarity index 100% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/generated/package.json rename to src/main/java/org/opentripplanner/apis/gtfs/generated/package.json diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/generated/yarn.lock b/src/main/java/org/opentripplanner/apis/gtfs/generated/yarn.lock similarity index 100% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/generated/yarn.lock rename to src/main/java/org/opentripplanner/apis/gtfs/generated/yarn.lock diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/AlertCauseMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/AlertCauseMapper.java new file mode 100644 index 00000000000..860b4cf7726 --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/AlertCauseMapper.java @@ -0,0 +1,34 @@ +package org.opentripplanner.apis.gtfs.mapping; + +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; +import org.opentripplanner.routing.alertpatch.AlertCause; + +/** + * Class for mapping {@link AlertCause} to GraphQL API cause (GTFS RT). + */ +public class AlertCauseMapper { + + /** + * Returns GraphQL API string counter part for internal {@link AlertCause} enum. Defaults to + * returning UNKNOWN_CAUSE. + */ + public static GraphQLTypes.GraphQLAlertCauseType getGraphQLCause(AlertCause cause) { + if (cause == null) { + return GraphQLTypes.GraphQLAlertCauseType.UNKNOWN_CAUSE; + } + return switch (cause) { + case UNKNOWN_CAUSE -> GraphQLTypes.GraphQLAlertCauseType.UNKNOWN_CAUSE; + case OTHER_CAUSE -> GraphQLTypes.GraphQLAlertCauseType.OTHER_CAUSE; + case TECHNICAL_PROBLEM -> GraphQLTypes.GraphQLAlertCauseType.TECHNICAL_PROBLEM; + case STRIKE -> GraphQLTypes.GraphQLAlertCauseType.STRIKE; + case DEMONSTRATION -> GraphQLTypes.GraphQLAlertCauseType.DEMONSTRATION; + case ACCIDENT -> GraphQLTypes.GraphQLAlertCauseType.ACCIDENT; + case HOLIDAY -> GraphQLTypes.GraphQLAlertCauseType.HOLIDAY; + case WEATHER -> GraphQLTypes.GraphQLAlertCauseType.WEATHER; + case MAINTENANCE -> GraphQLTypes.GraphQLAlertCauseType.MAINTENANCE; + case CONSTRUCTION -> GraphQLTypes.GraphQLAlertCauseType.CONSTRUCTION; + case POLICE_ACTIVITY -> GraphQLTypes.GraphQLAlertCauseType.POLICE_ACTIVITY; + case MEDICAL_EMERGENCY -> GraphQLTypes.GraphQLAlertCauseType.MEDICAL_EMERGENCY; + }; + } +} diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/AlertEffectMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/AlertEffectMapper.java new file mode 100644 index 00000000000..555d89adf7d --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/AlertEffectMapper.java @@ -0,0 +1,33 @@ +package org.opentripplanner.apis.gtfs.mapping; + +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; +import org.opentripplanner.routing.alertpatch.AlertEffect; + +/** + * Class for mapping {@link AlertEffect} to GraphQL API effect (GTFS RT). + */ +public class AlertEffectMapper { + + /** + * Returns GraphQL API string counter part for internal {@link AlertEffect} enum. Defaults + * to returning UNKNOWN_Effect. + */ + public static GraphQLTypes.GraphQLAlertEffectType getGraphQLEffect(AlertEffect effect) { + if (effect == null) { + return GraphQLTypes.GraphQLAlertEffectType.UNKNOWN_EFFECT; + } + return switch (effect) { + case NO_SERVICE -> GraphQLTypes.GraphQLAlertEffectType.NO_SERVICE; + case REDUCED_SERVICE -> GraphQLTypes.GraphQLAlertEffectType.REDUCED_SERVICE; + case SIGNIFICANT_DELAYS -> GraphQLTypes.GraphQLAlertEffectType.SIGNIFICANT_DELAYS; + case DETOUR -> GraphQLTypes.GraphQLAlertEffectType.DETOUR; + case ADDITIONAL_SERVICE -> GraphQLTypes.GraphQLAlertEffectType.ADDITIONAL_SERVICE; + case MODIFIED_SERVICE -> GraphQLTypes.GraphQLAlertEffectType.MODIFIED_SERVICE; + case OTHER_EFFECT -> GraphQLTypes.GraphQLAlertEffectType.OTHER_EFFECT; + case UNKNOWN_EFFECT -> GraphQLTypes.GraphQLAlertEffectType.UNKNOWN_EFFECT; + case STOP_MOVED -> GraphQLTypes.GraphQLAlertEffectType.STOP_MOVED; + case NO_EFFECT -> GraphQLTypes.GraphQLAlertEffectType.NO_EFFECT; + case ACCESSIBILITY_ISSUE -> GraphQLTypes.GraphQLAlertEffectType.ACCESSIBILITY_ISSUE; + }; + } +} diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/DirectionMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/DirectionMapper.java new file mode 100644 index 00000000000..5de932b2753 --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/DirectionMapper.java @@ -0,0 +1,45 @@ +package org.opentripplanner.apis.gtfs.mapping; + +import javax.annotation.Nonnull; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; +import org.opentripplanner.model.plan.AbsoluteDirection; +import org.opentripplanner.model.plan.RelativeDirection; + +public final class DirectionMapper { + + @Nonnull + public static GraphQLTypes.GraphQLAbsoluteDirection map(AbsoluteDirection dir) { + return switch (dir) { + case NORTH -> GraphQLTypes.GraphQLAbsoluteDirection.NORTH; + case NORTHEAST -> GraphQLTypes.GraphQLAbsoluteDirection.NORTHEAST; + case EAST -> GraphQLTypes.GraphQLAbsoluteDirection.EAST; + case SOUTHEAST -> GraphQLTypes.GraphQLAbsoluteDirection.SOUTHEAST; + case SOUTH -> GraphQLTypes.GraphQLAbsoluteDirection.SOUTH; + case SOUTHWEST -> GraphQLTypes.GraphQLAbsoluteDirection.SOUTHWEST; + case WEST -> GraphQLTypes.GraphQLAbsoluteDirection.WEST; + case NORTHWEST -> GraphQLTypes.GraphQLAbsoluteDirection.NORTHWEST; + }; + } + + @Nonnull + public static GraphQLTypes.GraphQLRelativeDirection map(RelativeDirection relativeDirection) { + return switch (relativeDirection) { + case DEPART -> GraphQLTypes.GraphQLRelativeDirection.DEPART; + case HARD_LEFT -> GraphQLTypes.GraphQLRelativeDirection.HARD_LEFT; + case LEFT -> GraphQLTypes.GraphQLRelativeDirection.LEFT; + case SLIGHTLY_LEFT -> GraphQLTypes.GraphQLRelativeDirection.SLIGHTLY_LEFT; + case CONTINUE -> GraphQLTypes.GraphQLRelativeDirection.CONTINUE; + case SLIGHTLY_RIGHT -> GraphQLTypes.GraphQLRelativeDirection.SLIGHTLY_RIGHT; + case RIGHT -> GraphQLTypes.GraphQLRelativeDirection.RIGHT; + case HARD_RIGHT -> GraphQLTypes.GraphQLRelativeDirection.HARD_RIGHT; + case CIRCLE_CLOCKWISE -> GraphQLTypes.GraphQLRelativeDirection.CIRCLE_CLOCKWISE; + case CIRCLE_COUNTERCLOCKWISE -> GraphQLTypes.GraphQLRelativeDirection.CIRCLE_COUNTERCLOCKWISE; + case ELEVATOR -> GraphQLTypes.GraphQLRelativeDirection.ELEVATOR; + case UTURN_LEFT -> GraphQLTypes.GraphQLRelativeDirection.UTURN_LEFT; + case UTURN_RIGHT -> GraphQLTypes.GraphQLRelativeDirection.UTURN_RIGHT; + case ENTER_STATION -> GraphQLTypes.GraphQLRelativeDirection.ENTER_STATION; + case EXIT_STATION -> GraphQLTypes.GraphQLRelativeDirection.EXIT_STATION; + case FOLLOW_SIGNS -> GraphQLTypes.GraphQLRelativeDirection.FOLLOW_SIGNS; + }; + } +} diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/NumberMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/NumberMapper.java similarity index 84% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/NumberMapper.java rename to src/main/java/org/opentripplanner/apis/gtfs/mapping/NumberMapper.java index 877baad8105..ddb6bf31a03 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/NumberMapper.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/NumberMapper.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.mapping; +package org.opentripplanner.apis.gtfs.mapping; import javax.annotation.Nullable; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/RouteRequestMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java similarity index 99% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/RouteRequestMapper.java rename to src/main/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java index 0c45851d9a1..9b8d8e02dec 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/RouteRequestMapper.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.mapping; +package org.opentripplanner.apis.gtfs.mapping; import graphql.schema.DataFetchingEnvironment; import java.time.Duration; @@ -15,7 +15,7 @@ import org.opentripplanner.api.common.LocationStringParser; import org.opentripplanner.api.parameter.QualifiedMode; import org.opentripplanner.api.parameter.QualifiedModeSet; -import org.opentripplanner.ext.gtfsgraphqlapi.GraphQLRequestContext; +import org.opentripplanner.apis.gtfs.GraphQLRequestContext; import org.opentripplanner.framework.graphql.GraphQLUtils; import org.opentripplanner.model.GenericLocation; import org.opentripplanner.routing.api.request.RouteRequest; diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/SeverityMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/SeverityMapper.java new file mode 100644 index 00000000000..2286205e52b --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/SeverityMapper.java @@ -0,0 +1,29 @@ +package org.opentripplanner.apis.gtfs.mapping; + +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; +import org.opentripplanner.routing.alertpatch.AlertSeverity; + +/** + * Class for mapping {@link AlertSeverity} to GraphQL API severity (GTFS RT). + */ +public class SeverityMapper { + + /** + * Returns GraphQL API string counter part for internal {@link AlertSeverity} enum. Defaults + * to returning UNKNOWN_SEVERITY. + */ + public static GraphQLTypes.GraphQLAlertSeverityLevelType getGraphQLSeverity( + AlertSeverity severity + ) { + if (severity == null) { + return GraphQLTypes.GraphQLAlertSeverityLevelType.UNKNOWN_SEVERITY; + } + return switch (severity) { + case INFO -> GraphQLTypes.GraphQLAlertSeverityLevelType.INFO; + case VERY_SLIGHT, SLIGHT, WARNING -> GraphQLTypes.GraphQLAlertSeverityLevelType.WARNING; + case VERY_SEVERE, SEVERE -> GraphQLTypes.GraphQLAlertSeverityLevelType.SEVERE; + case UNDEFINED, + UNKNOWN_SEVERITY -> GraphQLTypes.GraphQLAlertSeverityLevelType.UNKNOWN_SEVERITY; + }; + } +} diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/StreetNoteMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/StreetNoteMapper.java similarity index 95% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/StreetNoteMapper.java rename to src/main/java/org/opentripplanner/apis/gtfs/mapping/StreetNoteMapper.java index 713d6766cc0..baa601c7448 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/StreetNoteMapper.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/StreetNoteMapper.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.mapping; +package org.opentripplanner.apis.gtfs.mapping; import org.opentripplanner.api.mapping.StreetNoteMaperMapper; import org.opentripplanner.framework.i18n.NonLocalizedString; diff --git a/src/main/java/org/opentripplanner/apis/gtfs/model/RideHailingProvider.java b/src/main/java/org/opentripplanner/apis/gtfs/model/RideHailingProvider.java new file mode 100644 index 00000000000..27f80ecb218 --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/gtfs/model/RideHailingProvider.java @@ -0,0 +1,3 @@ +package org.opentripplanner.apis.gtfs.model; + +public record RideHailingProvider(String id) {} diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/model/RouteTypeModel.java b/src/main/java/org/opentripplanner/apis/gtfs/model/RouteTypeModel.java similarity index 93% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/model/RouteTypeModel.java rename to src/main/java/org/opentripplanner/apis/gtfs/model/RouteTypeModel.java index 25cf92d3078..8b428ef41e3 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/model/RouteTypeModel.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/model/RouteTypeModel.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.model; +package org.opentripplanner.apis.gtfs.model; import org.opentripplanner.transit.model.organization.Agency; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/model/StopOnRouteModel.java b/src/main/java/org/opentripplanner/apis/gtfs/model/StopOnRouteModel.java similarity index 93% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/model/StopOnRouteModel.java rename to src/main/java/org/opentripplanner/apis/gtfs/model/StopOnRouteModel.java index 56e079123d2..3d085f4e7eb 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/model/StopOnRouteModel.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/model/StopOnRouteModel.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.model; +package org.opentripplanner.apis.gtfs.model; import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.transit.model.site.StopLocation; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/model/StopOnTripModel.java b/src/main/java/org/opentripplanner/apis/gtfs/model/StopOnTripModel.java similarity index 93% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/model/StopOnTripModel.java rename to src/main/java/org/opentripplanner/apis/gtfs/model/StopOnTripModel.java index 93c56e97171..1df27d8b86d 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/model/StopOnTripModel.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/model/StopOnTripModel.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.model; +package org.opentripplanner.apis.gtfs.model; import org.opentripplanner.transit.model.site.StopLocation; import org.opentripplanner.transit.model.timetable.Trip; diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/model/StopPosition.java b/src/main/java/org/opentripplanner/apis/gtfs/model/StopPosition.java similarity index 84% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/model/StopPosition.java rename to src/main/java/org/opentripplanner/apis/gtfs/model/StopPosition.java index e836bf1ae0f..6050953cc4e 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/model/StopPosition.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/model/StopPosition.java @@ -1,8 +1,8 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.model; +package org.opentripplanner.apis.gtfs.model; import graphql.TypeResolutionEnvironment; import graphql.schema.GraphQLObjectType; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; public interface StopPosition extends GraphQLDataFetchers.GraphQLStopPosition { record PositionAtStop(int position) implements StopPosition {} diff --git a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/model/UnknownModel.java b/src/main/java/org/opentripplanner/apis/gtfs/model/UnknownModel.java similarity index 87% rename from src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/model/UnknownModel.java rename to src/main/java/org/opentripplanner/apis/gtfs/model/UnknownModel.java index 5ab9b40c1a5..d5c70c70dce 100644 --- a/src/ext/java/org/opentripplanner/ext/gtfsgraphqlapi/model/UnknownModel.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/model/UnknownModel.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.model; +package org.opentripplanner.apis.gtfs.model; /** * Class for unknown entities. Either no entity was defined or an entity that we don't support yet diff --git a/src/main/java/org/opentripplanner/framework/application/OTPFeature.java b/src/main/java/org/opentripplanner/framework/application/OTPFeature.java index 0854032a665..0652fb667a8 100644 --- a/src/main/java/org/opentripplanner/framework/application/OTPFeature.java +++ b/src/main/java/org/opentripplanner/framework/application/OTPFeature.java @@ -29,7 +29,7 @@ public enum OTPFeature { ), DebugClient(true, false, "Enable the debug web client located at the root of the web server."), FloatingBike(true, false, "Enable floating bike routing."), - GtfsGraphQlApi(true, true, "Enable GTFS GraphQL API."), + GtfsGraphQlApi(true, false, "Enable GTFS GraphQL API."), /** * If this feature flag is switched on, then the minimum transfer time is not the minimum transfer * time, but the definitive transfer time. Use this to override what we think the transfer will diff --git a/src/ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi/GraphQLIndexTest.java b/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIndexTest.java similarity index 97% rename from src/ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi/GraphQLIndexTest.java rename to src/test/java/org/opentripplanner/apis/gtfs/GraphQLIndexTest.java index a55ccaca0fd..0013f7df202 100644 --- a/src/ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi/GraphQLIndexTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIndexTest.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi; +package org.opentripplanner.apis.gtfs; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; diff --git a/src/ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi/GraphQLIntegrationTest.java b/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java similarity index 98% rename from src/ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi/GraphQLIntegrationTest.java rename to src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java index 1d358e17a71..399c5861790 100644 --- a/src/ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi/GraphQLIntegrationTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi; +package org.opentripplanner.apis.gtfs; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; @@ -209,7 +209,7 @@ public List getModesOfStopLocation(StopLocation stop) { ); } - @FilePatternSource(pattern = "src/ext-test/resources/gtfsgraphqlapi/queries/*.graphql") + @FilePatternSource(pattern = "src/test/resources/gtfsgraphqlapi/queries/*.graphql") @ParameterizedTest(name = "Check GraphQL query in {0}") void graphQL(Path path) throws IOException { var query = Files.readString(path); diff --git a/src/ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi/GraphQLScalarsTest.java b/src/test/java/org/opentripplanner/apis/gtfs/GraphQLScalarsTest.java similarity index 96% rename from src/ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi/GraphQLScalarsTest.java rename to src/test/java/org/opentripplanner/apis/gtfs/GraphQLScalarsTest.java index dbeebdfeded..90b35a31b9f 100644 --- a/src/ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi/GraphQLScalarsTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/GraphQLScalarsTest.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi; +package org.opentripplanner.apis.gtfs; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi/TestRoutingService.java b/src/test/java/org/opentripplanner/apis/gtfs/TestRoutingService.java similarity index 96% rename from src/ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi/TestRoutingService.java rename to src/test/java/org/opentripplanner/apis/gtfs/TestRoutingService.java index 714bfdbcdb8..57c965eb3f1 100644 --- a/src/ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi/TestRoutingService.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/TestRoutingService.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi; +package org.opentripplanner.apis.gtfs; import java.time.Instant; import java.time.OffsetDateTime; diff --git a/src/ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/QueryTypeImplTest.java b/src/test/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImplTest.java similarity index 97% rename from src/ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/QueryTypeImplTest.java rename to src/test/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImplTest.java index 9bec527bc78..e223b53844a 100644 --- a/src/ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi/datafetchers/QueryTypeImplTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImplTest.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.datafetchers; +package org.opentripplanner.apis.gtfs.datafetchers; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -6,7 +6,7 @@ import java.util.Map; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.routing.alertpatch.AlertCause; import org.opentripplanner.routing.alertpatch.AlertEffect; diff --git a/src/ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/DirectionMapperTest.java b/src/test/java/org/opentripplanner/apis/gtfs/mapping/DirectionMapperTest.java similarity index 92% rename from src/ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/DirectionMapperTest.java rename to src/test/java/org/opentripplanner/apis/gtfs/mapping/DirectionMapperTest.java index f97a5e69772..2c69f3dca46 100644 --- a/src/ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/DirectionMapperTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/mapping/DirectionMapperTest.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.mapping; +package org.opentripplanner.apis.gtfs.mapping; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/RouteRequestMapperTest.java b/src/test/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapperTest.java similarity index 97% rename from src/ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/RouteRequestMapperTest.java rename to src/test/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapperTest.java index c9ea4666b06..c6e69cecf4f 100644 --- a/src/ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/RouteRequestMapperTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapperTest.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.mapping; +package org.opentripplanner.apis.gtfs.mapping; import static graphql.execution.ExecutionContextBuilder.newExecutionContextBuilder; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -19,9 +19,9 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.opentripplanner._support.time.ZoneIds; +import org.opentripplanner.apis.gtfs.GraphQLRequestContext; +import org.opentripplanner.apis.gtfs.TestRoutingService; import org.opentripplanner.ext.fares.impl.DefaultFareService; -import org.opentripplanner.ext.gtfsgraphqlapi.GraphQLRequestContext; -import org.opentripplanner.ext.gtfsgraphqlapi.TestRoutingService; import org.opentripplanner.model.plan.PlanTestConstants; import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.routing.api.request.preference.TimeSlopeSafetyTriangle; diff --git a/src/ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/StreetNoteMapperTest.java b/src/test/java/org/opentripplanner/apis/gtfs/mapping/StreetNoteMapperTest.java similarity index 97% rename from src/ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/StreetNoteMapperTest.java rename to src/test/java/org/opentripplanner/apis/gtfs/mapping/StreetNoteMapperTest.java index 976db86fbe5..a970a68bcb2 100644 --- a/src/ext-test/java/org/opentripplanner/ext/gtfsgraphqlapi/mapping/StreetNoteMapperTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/mapping/StreetNoteMapperTest.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.mapping; +package org.opentripplanner.apis.gtfs.mapping; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; diff --git a/src/ext-test/resources/gtfsgraphqlapi/expectations/patterns.json b/src/test/resources/gtfsgraphqlapi/expectations/patterns.json similarity index 100% rename from src/ext-test/resources/gtfsgraphqlapi/expectations/patterns.json rename to src/test/resources/gtfsgraphqlapi/expectations/patterns.json diff --git a/src/ext-test/resources/gtfsgraphqlapi/expectations/plan-extended.json b/src/test/resources/gtfsgraphqlapi/expectations/plan-extended.json similarity index 100% rename from src/ext-test/resources/gtfsgraphqlapi/expectations/plan-extended.json rename to src/test/resources/gtfsgraphqlapi/expectations/plan-extended.json diff --git a/src/ext-test/resources/gtfsgraphqlapi/expectations/plan-fares.json b/src/test/resources/gtfsgraphqlapi/expectations/plan-fares.json similarity index 100% rename from src/ext-test/resources/gtfsgraphqlapi/expectations/plan-fares.json rename to src/test/resources/gtfsgraphqlapi/expectations/plan-fares.json diff --git a/src/ext-test/resources/gtfsgraphqlapi/expectations/plan-stop-positions.json b/src/test/resources/gtfsgraphqlapi/expectations/plan-stop-positions.json similarity index 100% rename from src/ext-test/resources/gtfsgraphqlapi/expectations/plan-stop-positions.json rename to src/test/resources/gtfsgraphqlapi/expectations/plan-stop-positions.json diff --git a/src/ext-test/resources/gtfsgraphqlapi/expectations/plan.json b/src/test/resources/gtfsgraphqlapi/expectations/plan.json similarity index 100% rename from src/ext-test/resources/gtfsgraphqlapi/expectations/plan.json rename to src/test/resources/gtfsgraphqlapi/expectations/plan.json diff --git a/src/ext-test/resources/gtfsgraphqlapi/expectations/routes.json b/src/test/resources/gtfsgraphqlapi/expectations/routes.json similarity index 100% rename from src/ext-test/resources/gtfsgraphqlapi/expectations/routes.json rename to src/test/resources/gtfsgraphqlapi/expectations/routes.json diff --git a/src/ext-test/resources/gtfsgraphqlapi/expectations/stops.json b/src/test/resources/gtfsgraphqlapi/expectations/stops.json similarity index 100% rename from src/ext-test/resources/gtfsgraphqlapi/expectations/stops.json rename to src/test/resources/gtfsgraphqlapi/expectations/stops.json diff --git a/src/ext-test/resources/gtfsgraphqlapi/expectations/vehicle-parking.json b/src/test/resources/gtfsgraphqlapi/expectations/vehicle-parking.json similarity index 100% rename from src/ext-test/resources/gtfsgraphqlapi/expectations/vehicle-parking.json rename to src/test/resources/gtfsgraphqlapi/expectations/vehicle-parking.json diff --git a/src/ext-test/resources/gtfsgraphqlapi/expectations/walk-steps.json b/src/test/resources/gtfsgraphqlapi/expectations/walk-steps.json similarity index 100% rename from src/ext-test/resources/gtfsgraphqlapi/expectations/walk-steps.json rename to src/test/resources/gtfsgraphqlapi/expectations/walk-steps.json diff --git a/src/ext-test/resources/gtfsgraphqlapi/queries/patterns.graphql b/src/test/resources/gtfsgraphqlapi/queries/patterns.graphql similarity index 100% rename from src/ext-test/resources/gtfsgraphqlapi/queries/patterns.graphql rename to src/test/resources/gtfsgraphqlapi/queries/patterns.graphql diff --git a/src/ext-test/resources/gtfsgraphqlapi/queries/plan-extended.graphql b/src/test/resources/gtfsgraphqlapi/queries/plan-extended.graphql similarity index 100% rename from src/ext-test/resources/gtfsgraphqlapi/queries/plan-extended.graphql rename to src/test/resources/gtfsgraphqlapi/queries/plan-extended.graphql diff --git a/src/ext-test/resources/gtfsgraphqlapi/queries/plan-fares.graphql b/src/test/resources/gtfsgraphqlapi/queries/plan-fares.graphql similarity index 100% rename from src/ext-test/resources/gtfsgraphqlapi/queries/plan-fares.graphql rename to src/test/resources/gtfsgraphqlapi/queries/plan-fares.graphql diff --git a/src/ext-test/resources/gtfsgraphqlapi/queries/plan-stop-positions.graphql b/src/test/resources/gtfsgraphqlapi/queries/plan-stop-positions.graphql similarity index 100% rename from src/ext-test/resources/gtfsgraphqlapi/queries/plan-stop-positions.graphql rename to src/test/resources/gtfsgraphqlapi/queries/plan-stop-positions.graphql diff --git a/src/ext-test/resources/gtfsgraphqlapi/queries/plan.graphql b/src/test/resources/gtfsgraphqlapi/queries/plan.graphql similarity index 100% rename from src/ext-test/resources/gtfsgraphqlapi/queries/plan.graphql rename to src/test/resources/gtfsgraphqlapi/queries/plan.graphql diff --git a/src/ext-test/resources/gtfsgraphqlapi/queries/routes.graphql b/src/test/resources/gtfsgraphqlapi/queries/routes.graphql similarity index 100% rename from src/ext-test/resources/gtfsgraphqlapi/queries/routes.graphql rename to src/test/resources/gtfsgraphqlapi/queries/routes.graphql diff --git a/src/ext-test/resources/gtfsgraphqlapi/queries/stops.graphql b/src/test/resources/gtfsgraphqlapi/queries/stops.graphql similarity index 100% rename from src/ext-test/resources/gtfsgraphqlapi/queries/stops.graphql rename to src/test/resources/gtfsgraphqlapi/queries/stops.graphql diff --git a/src/ext-test/resources/gtfsgraphqlapi/queries/vehicle-parking.graphql b/src/test/resources/gtfsgraphqlapi/queries/vehicle-parking.graphql similarity index 100% rename from src/ext-test/resources/gtfsgraphqlapi/queries/vehicle-parking.graphql rename to src/test/resources/gtfsgraphqlapi/queries/vehicle-parking.graphql diff --git a/src/ext-test/resources/gtfsgraphqlapi/queries/walk-steps.graphql b/src/test/resources/gtfsgraphqlapi/queries/walk-steps.graphql similarity index 100% rename from src/ext-test/resources/gtfsgraphqlapi/queries/walk-steps.graphql rename to src/test/resources/gtfsgraphqlapi/queries/walk-steps.graphql From 61b53f894fd4b058cab4a19587206b24d869233e Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 30 Aug 2023 22:00:39 +0200 Subject: [PATCH 14/95] Update documentation --- docs/Basic-Tutorial.md | 2 +- docs/SandboxExtension.md | 2 +- docs/Version-Comparison.md | 2 +- docs/{ => apis}/Apis.md | 10 +-- docs/apis/GTFS-GraphQL-API.md | 44 ++++++++++ docs/{ => apis}/GraphQL-Tutorial.md | 10 +-- docs/sandbox/GtfsGraphQlApi.md | 85 ------------------- docs/sandbox/TransmodelApi.md | 2 +- mkdocs.yml | 8 +- .../apis/gtfs/GtfsGraphQLIndex.java | 3 +- 10 files changed, 63 insertions(+), 105 deletions(-) rename docs/{ => apis}/Apis.md (67%) create mode 100644 docs/apis/GTFS-GraphQL-API.md rename docs/{ => apis}/GraphQL-Tutorial.md (92%) delete mode 100644 docs/sandbox/GtfsGraphQlApi.md diff --git a/docs/Basic-Tutorial.md b/docs/Basic-Tutorial.md index 33473c5d526..0ed634f557f 100644 --- a/docs/Basic-Tutorial.md +++ b/docs/Basic-Tutorial.md @@ -201,4 +201,4 @@ You can run the OTP .jar file with the `--help` option for a full list of comman ## Exploring the API -If you want to learn how to use OTP's API's, check out the [GraphQL tutorial](GraphQL-Tutorial.md). \ No newline at end of file +If you want to learn how to use OTP's API's, check out the [GraphQL tutorial](apis/GraphQL-Tutorial.md). \ No newline at end of file diff --git a/docs/SandboxExtension.md b/docs/SandboxExtension.md index 7914c8abeb0..c14b1afef8b 100644 --- a/docs/SandboxExtension.md +++ b/docs/SandboxExtension.md @@ -14,7 +14,7 @@ provided "as is". - [Geocoder API](sandbox/GeocoderAPI.md) - Adds an API to search for corners, stops and stations. - [Transfer analyser](sandbox/transferanalyzer.md) - Module used for analyzing the transfers between nearby stops generated by routing via OSM data. -- [GTFS GraphQL API](sandbox/GtfsGraphQlApi.md) - HSL's GraphQL API used by the Digitransit project. +- [GTFS GraphQL API](apis/GTFS-GraphQL-API.md) - HSL's GraphQL API used by the Digitransit project. - [Transmodel API](sandbox/TransmodelApi.md) - Enturs GraphQL Transmodel API. - [SIRI Updater](sandbox/SiriUpdater.md) - Update OTP with realtime information from a Transmodel SIRI data source. - [SIRI Azure Updater](sandbox/SiriAzureUpdater.md) - fetch SIRI realtime data through *Azure Service Bus* diff --git a/docs/Version-Comparison.md b/docs/Version-Comparison.md index 86d8d1e3e01..17b38dffb9b 100644 --- a/docs/Version-Comparison.md +++ b/docs/Version-Comparison.md @@ -136,7 +136,7 @@ GraphQL API, the only supported way of sending requests to the OTP routing engin Details of those two APIs are available at the following pages: -- [GTFS GraphQL API](sandbox/GtfsGraphQlApi.md) - HSL's GraphQL API used by the Digitransit +- [GTFS GraphQL API](apis/GTFS-GraphQL-API.md) - HSL's GraphQL API used by the Digitransit project. - [Transmodel API](sandbox/TransmodelApi.md) - Entur´s Transmodel API diff --git a/docs/Apis.md b/docs/apis/Apis.md similarity index 67% rename from docs/Apis.md rename to docs/apis/Apis.md index 1bb89c1cc2a..4d6c7f46694 100644 --- a/docs/Apis.md +++ b/docs/apis/Apis.md @@ -2,23 +2,23 @@ Several services are built upon OTP's routing and transit data indexing engines. They expose these APIs: -The [GTFS GraphQL API](sandbox/GtfsGraphQlApi.md) has been used by the Digitransit and otp-react-redux +The [GTFS GraphQL API](GTFS-GraphQL-API.md) has been used by the Digitransit and otp-react-redux projects as a general purpose routing and transit data API in production for many years. If your input data is mostly GTFS then this is probably the best choice as it uses the same vocabulary. -The [Transmodel GraphQL API](sandbox/TransmodelApi.md) is used at +The [Transmodel GraphQL API](../sandbox/TransmodelApi.md) is used at Entur in production since 2020. Like the GTFS GraphQL API it is also a general purpose API. If your input data is mostly NeTeX then you might want to investigate this API as it uses the [Transmodel vocabulary](https://en.wikipedia.org/wiki/Transmodel) to describe its entities. -The [Vector tiles API](sandbox/MapboxVectorTilesApi.md) is a special purpose API for displaying +The [Vector tiles API](../sandbox/MapboxVectorTilesApi.md) is a special purpose API for displaying entities on a vector map. -The [Actuator API](sandbox/ActuatorAPI.md) provides endpoints for checking the health status of the +The [Actuator API](../sandbox/ActuatorAPI.md) provides endpoints for checking the health status of the OTP instance and reading live application metrics. -The [Geocoder API](sandbox/GeocoderAPI.md) allows you to geocode street corners and stop names. +The [Geocoder API](../sandbox/GeocoderAPI.md) allows you to geocode street corners and stop names. ## Legacy APIs (to be removed) diff --git a/docs/apis/GTFS-GraphQL-API.md b/docs/apis/GTFS-GraphQL-API.md new file mode 100644 index 00000000000..f31be0872c5 --- /dev/null +++ b/docs/apis/GTFS-GraphQL-API.md @@ -0,0 +1,44 @@ +# GTFS GraphQL API + +The GTFS GraphQL API was created for the Digitransit project and is used heavily by +[digitransit-ui](https://github.com/HSLdevcom/digitransit-ui). + +[otp-react-redux](https://github.com/opentripplanner/otp-react-redux) has also migrated to this API +in 2023. + +## URLs + - GraphQL endpoint: `http://localhost:8080/otp/routers/default/index/graphql` + - HTML schema documentation: [https://docs.opentripplanner.org/api/dev-2.x/graphql-gtfs/](https://docs.opentripplanner.org/api/dev-2.x/graphql-gtfs/) + - Built-in visual GraphQL client: [http://localhost:8080/graphiql](http://localhost:8080/graphiql) + +## Built-in API client + +A browser based GraphQL API client is available at [http://localhost:8080/graphiql](http://localhost:8080/graphiql) + +![GraphiQL](../images/graphiql.png) + +**`curl` example** + +A complete example that fetches the list of all stops from OTP is: + +``` +curl --request POST \ + --url http://localhost:8080/otp/routers/default/index/graphql \ + --header 'Content-Type: application/json' \ + --header 'OTPTimeout: 180000' \ + --data '{"query":"query stops {\n stops {\n gtfsId\n name\n }\n}\n","operationName":"stops"}' +``` +## Configuration + +The API is enabled by default. + +If you want to disable it, do it in `otp-config.json`: + +```json +// otp-config.json +{ + "otpFeatures" : { + "GtfsGraphQlApi": false + } +} +``` \ No newline at end of file diff --git a/docs/GraphQL-Tutorial.md b/docs/apis/GraphQL-Tutorial.md similarity index 92% rename from docs/GraphQL-Tutorial.md rename to docs/apis/GraphQL-Tutorial.md index 8f0ace9c8e3..3c12a7c39bf 100644 --- a/docs/GraphQL-Tutorial.md +++ b/docs/apis/GraphQL-Tutorial.md @@ -8,10 +8,10 @@ # GraphQL tutorial This document will give you a quick start tutorial on how to get started with OTP's GraphQL APIs. For -this tutorial we will be using the [GTFS GraphQL API](sandbox/GtfsGraphQlApi.md) as this is the most commonly used one. +this tutorial we will be using the [GTFS GraphQL API](GTFS-GraphQL-API.md) as this is the most commonly used one. First of all, make sure that you've loaded street and transit data into your instance by following -the [basic tutorial](Basic-Tutorial.md) +the [basic tutorial](../Basic-Tutorial.md) ## Visual GraphQL API client @@ -20,7 +20,7 @@ started OTP, visit [http://localhost:8080/graphiql](http://localhost:8080/graphi It should look like this: -![GraphiQL](images/graphiql.png) +![GraphiQL](../images/graphiql.png) ## Sending your first query @@ -53,12 +53,12 @@ side panel. Now would be a good time to explore the auto-complete capabilities of the tool by moving the cursor into the query panel and hitting Ctrl-Space to see what other query parameters are possible. -![GraphiQL](images/graphiql-autocomplete.png) +![GraphiQL](../images/graphiql-autocomplete.png) The explorer also has documentation built into it. If you hover your pointer over a property on the left hand side you can see its documentation. -![GraphiQL](images/graphiql-documentation.png) +![GraphiQL](../images/graphiql-documentation.png) ## A more advanced query diff --git a/docs/sandbox/GtfsGraphQlApi.md b/docs/sandbox/GtfsGraphQlApi.md deleted file mode 100644 index 8175e65a0d5..00000000000 --- a/docs/sandbox/GtfsGraphQlApi.md +++ /dev/null @@ -1,85 +0,0 @@ -# GTFS GraphQL API - -The GTFS GraphQL API was created for the Digitransit project and is used heavily by -[digitransit-ui](https://github.com/HSLdevcom/digitransit-ui). - -As of 2023 [otp-rr](https://github.com/opentripplanner/otp-react-redux) is in the process of -migrating to this API. - -## Contact Info - -- Digitransit team, HSL, Helsinki, Finland -- Kyyti, Helsinki, Finland -- IBI, USA - -## URLs - - GraphQL endpoint: `http://localhost:8080/otp/routers/default/index/graphql` - - HTML schema documentation: [https://docs.opentripplanner.org/api/dev-2.x/graphql-gtfs/](https://docs.opentripplanner.org/api/dev-2.x/graphql-gtfs/) - - Built-in visual GraphQL client: [http://localhost:8080/graphiql](http://localhost:8080/graphiql) - -## Built-in API client - -A browser based GraphQL API client is available at [http://localhost:8080/graphiql](http://localhost:8080/graphiql) - -![GraphiQL](../images/graphiql.png) - -**`curl` example** - -A complete example that fetches the list of all stops from OTP is: - -``` -curl --request POST \ - --url http://localhost:8080/otp/routers/default/index/graphql \ - --header 'Content-Type: application/json' \ - --header 'OTPTimeout: 180000' \ - --data '{"query":"query stops {\n stops {\n gtfsId\n name\n }\n}\n","operationName":"stops"}' -``` -## Configuration - -The API is enabled by default. - -If you want to disable it, do it in `otp-config.json`: - -```json -// otp-config.json -{ - "otpFeatures" : { - "GtfsGraphQlApi": false - } -} -``` - -## OTP2 Official GraphQL API (Not available) - -We **plan** to make a new offical OTP2 API, replacing the REST API. The plan is to base the new API -on this API and the [Transmodel GraphQL API](TransmodelApi.md). The new API will most likely have two -"translations": A GTFS version and a Transmodel version, we will try to keep the semantics the same. - -## Changelog - -- Initial version of GTFS Graph QL API (September 2020) -- Added ids parameter to bikeRentalStations query (May 2021, [#3450](https://github.com/opentripplanner/OpenTripPlanner/pull/3450)) -- Added capacity and allowOverloading fields to bike rental stations (not yet properly implemented) (May 2021, [#3450](https://github.com/opentripplanner/OpenTripPlanner/pull/3450)) -- Updated documentation and process for generating Java code from GraphQL schema definition (May 2021, [#3450](https://github.com/opentripplanner/OpenTripPlanner/pull/3450)) -- Implemented modeWeight and added debugItineraryFilter to plan query. Added systemNotices to itineraries (May 2021, [#3503](https://github.com/opentripplanner/OpenTripPlanner/pull/3503)) -- Updated to ignore modes which are not valid in OTP2 (June 2021, [#3464](https://github.com/opentripplanner/OpenTripPlanner/pull/3464)) -- Add Leg#walkingBike (June 2021, [#3550](https://github.com/opentripplanner/OpenTripPlanner/pull/3550)) -- Add GBFS bike rental URIs to bike rental stations (June 2021, [#3543](https://github.com/opentripplanner/OpenTripPlanner/pull/3543)) -- Properly implement all bike rental station fields and add allowPickup, allowPickupNow, allowDropoffNow and operative fields (October 2021, [#3632](https://github.com/opentripplanner/OpenTripPlanner/pull/3632)) -- Create RentalVehicle, VehicleRentalStation and VehicleRentalUris types. Deprecate BikeRentalStation and BikeRentalStationUris types (October 2021, [#3632](https://github.com/opentripplanner/OpenTripPlanner/pull/3632)) -- Create VehicleParking type. Deprecate BikePark and CarPark types (November 2021, [#3480](https://github.com/opentripplanner/OpenTripPlanner/pull/3480)) -- Update and implement Alert type and alerts query. Add ACCESSIBILITY_ISSUE to AlertEffectType enum (November 2021, [#3747](https://github.com/opentripplanner/OpenTripPlanner/pull/3747)) -- Add geometries for stops (December 2021, [#3757](https://github.com/opentripplanner/OpenTripPlanner/pull/3757)) -- Add RouteType and Unknown entities and implement alerts fields (add add alerts field to Feed) (December 2021, [#3780](https://github.com/opentripplanner/OpenTripPlanner/pull/3780)) -- Take free-floating vehicles into account when computing state (February 2022, [#3857](https://github.com/opentripplanner/OpenTripPlanner/pull/3857)) -- Fix issue with GraphQL code generator (February 2022, [#3881](https://github.com/opentripplanner/OpenTripPlanner/pull/3881)) -- Add GBFS form factors for `rentalVehicle` (April 2022, [#4062](https://github.com/opentripplanner/OpenTripPlanner/pull/4062)) -- Implement allowedBikeRentalNetworks while deprecating it and add allowedVehicleRentalNetworks and bannedVehicleRentalNetworks. (July 2022, [#4279](https://github.com/opentripplanner/OpenTripPlanner/pull/4279)) -- Filters place types in GTFS GraphQL API so that a bike park type is not returned if a vehicle parking has no bicycle spaces and car park type is not returned if a parking has no car spaces. (July 2022, [#4296](https://github.com/opentripplanner/OpenTripPlanner/pull/4296)) -- Include departures with skipped stops in the Stop type's stopTimesForPattern query. (July 2022, [#4299](https://github.com/opentripplanner/OpenTripPlanner/pull/4299)) -- Add built-in GraphQL client. (October 2022, [#4499](https://github.com/opentripplanner/OpenTripPlanner/pull/4499)) -- Implement support for omitCanceled parameter in some stop's stoptime queries (October 2022, [#4504]([#4504](https://github.com/opentripplanner/OpenTripPlanner/pull/4504))) -- Rename unpreferredRouteCost to unpreferredCost (October 2022, [#4543](https://github.com/opentripplanner/OpenTripPlanner/pull/4543)) -- Make plan fetcher async (December 2022, [#4676](https://github.com/opentripplanner/OpenTripPlanner/pull/4676)) -- Fix alerts query severity, effect and cause filters (February 2023, [#4909](https://github.com/opentripplanner/OpenTripPlanner/pull/4909)) -- Use accept-language header instead of the default route request locale in the plan query (March 2023, [#4971](https://github.com/opentripplanner/OpenTripPlanner/pull/4971)) diff --git a/docs/sandbox/TransmodelApi.md b/docs/sandbox/TransmodelApi.md index bfde88c886c..5069966bdbd 100644 --- a/docs/sandbox/TransmodelApi.md +++ b/docs/sandbox/TransmodelApi.md @@ -73,7 +73,7 @@ at: `http://localhost:8080/otp/routers/default/transmodel/index/graphql` ### OTP2 Official GraphQL API (Not available) We **plan** to make a new offical OTP2 API, replacing the REST API. The plan is to base the new API -on this API and the [GTFS GraphQL API](GtfsGraphQlApi.md). The new API will most likely have two +on this API and the [GTFS GraphQL API](../apis/GTFS-GraphQL-API.md). The new API will most likely have two "translations": A GTFS version and a Transmodel version, we will try to keep the semantics the same. ### Configuration diff --git a/mkdocs.yml b/mkdocs.yml index 417e81c6c8d..eec8a5cb04d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -16,7 +16,7 @@ plugins: # remove this after OTP 2.4.0 has been released - redirects: redirect_maps: - 'sandbox/LegacyGraphQLApi.md': 'sandbox/GtfsGraphQlApi.md' + 'sandbox/LegacyGraphQLApi.md': 'apis/GTFS-GraphQL-API.md' theme: name: material @@ -68,8 +68,9 @@ nav: - Troubleshooting: 'Troubleshooting-Routing.md' - Comparing OTP2 to OTP1: 'Version-Comparison.md' - APIs: - - Introduction: 'Apis.md' - - GraphQL Tutorial: 'GraphQL-Tutorial.md' + - Introduction: 'apis/Apis.md' + - GraphQL Tutorial: 'apis/GraphQL-Tutorial.md' + - GTFS GraphQL API: 'apis/GTFS-GraphQL-API.md' - Configuration: - Introduction: 'Configuration.md' - Build: 'BuildConfiguration.md' @@ -96,7 +97,6 @@ nav: - Actuator API: 'sandbox/ActuatorAPI.md' - Direct Transfer Analyzer: 'sandbox/transferanalyzer.md' - Google Cloud Storage: 'sandbox/GoogleCloudStorage.md' - - GTFS GraphQL API: 'sandbox/GtfsGraphQlApi.md' - Transmodel(NeTEx) GraphQL API: 'sandbox/TransmodelApi.md' - SIRI Updaters: 'sandbox/SiriUpdater.md' - SIRI Updater (Azure): 'sandbox/SiriAzureUpdater.md' diff --git a/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java index 877e7c7259f..7a1aff65080 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java @@ -104,8 +104,7 @@ class GtfsGraphQLIndex { protected static GraphQLSchema buildSchema() { try { URL url = Resources.getResource("gtfsgraphqlapi/schema.graphqls"); - String sdl = Resources.toString(url, StandardCharsets.UTF_8); - TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(sdl); + TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(url.openStream()); IntrospectionTypeWiring typeWiring = new IntrospectionTypeWiring(typeRegistry); RuntimeWiring runtimeWiring = RuntimeWiring .newRuntimeWiring() From 69ad1531c0a366d91f748090c8bc387d889ae293 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 30 Aug 2023 22:06:09 +0200 Subject: [PATCH 15/95] Move schema file --- src/{ext => main}/resources/gtfsgraphqlapi/schema.graphqls | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{ext => main}/resources/gtfsgraphqlapi/schema.graphqls (100%) diff --git a/src/ext/resources/gtfsgraphqlapi/schema.graphqls b/src/main/resources/gtfsgraphqlapi/schema.graphqls similarity index 100% rename from src/ext/resources/gtfsgraphqlapi/schema.graphqls rename to src/main/resources/gtfsgraphqlapi/schema.graphqls From 98020be43fe02cc8d0549f8e43121920be695f9f Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 31 Aug 2023 09:43:05 +0200 Subject: [PATCH 16/95] Fix doc and code generation --- doc-templates/GraphQL-Tutorial.md | 10 +++--- .../gtfs/generated/GraphQLDataFetchers.java | 32 ++++++++++++------- .../generate/doc/GraphQLTutorialDocTest.java | 2 +- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/doc-templates/GraphQL-Tutorial.md b/doc-templates/GraphQL-Tutorial.md index d35199667a4..c91197743c5 100644 --- a/doc-templates/GraphQL-Tutorial.md +++ b/doc-templates/GraphQL-Tutorial.md @@ -8,10 +8,10 @@ # GraphQL tutorial This document will give you a quick start tutorial on how to get started with OTP's GraphQL APIs. For -this tutorial we will be using the [GTFS GraphQL API](sandbox/GtfsGraphQlApi.md) as this is the most commonly used one. +this tutorial we will be using the [GTFS GraphQL API](GTFS-GraphQL-API.md) as this is the most commonly used one. First of all, make sure that you've loaded street and transit data into your instance by following -the [basic tutorial](Basic-Tutorial.md) +the [basic tutorial](../Basic-Tutorial.md) ## Visual GraphQL API client @@ -20,7 +20,7 @@ started OTP, visit [http://localhost:8080/graphiql](http://localhost:8080/graphi It should look like this: -![GraphiQL](images/graphiql.png) +![GraphiQL](../images/graphiql.png) ## Sending your first query @@ -35,12 +35,12 @@ side panel. Now would be a good time to explore the auto-complete capabilities of the tool by moving the cursor into the query panel and hitting Ctrl-Space to see what other query parameters are possible. -![GraphiQL](images/graphiql-autocomplete.png) +![GraphiQL](../images/graphiql-autocomplete.png) The explorer also has documentation built into it. If you hover your pointer over a property on the left hand side you can see its documentation. -![GraphiQL](images/graphiql-documentation.png) +![GraphiQL](../images/graphiql-documentation.png) ## A more advanced query diff --git a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java index 68d1f84827d..1e33d4907df 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -10,6 +10,14 @@ import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; import org.opentripplanner.api.resource.DebugOutput; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAbsoluteDirection; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAlertCauseType; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAlertEffectType; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAlertSeverityLevelType; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLInputField; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRelativeDirection; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRoutingErrorCode; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLTransitMode; import org.opentripplanner.apis.gtfs.model.RideHailingProvider; import org.opentripplanner.apis.gtfs.model.StopPosition; import org.opentripplanner.ext.fares.model.FareRuleSet; @@ -77,13 +85,13 @@ public interface GraphQLAgency { public interface GraphQLAlert { public DataFetcher agency(); - public DataFetcher alertCause(); + public DataFetcher alertCause(); public DataFetcher alertDescriptionText(); public DataFetcher>> alertDescriptionTextTranslations(); - public DataFetcher alertEffect(); + public DataFetcher alertEffect(); public DataFetcher alertHash(); @@ -91,7 +99,7 @@ public interface GraphQLAlert { public DataFetcher>> alertHeaderTextTranslations(); - public DataFetcher alertSeverityLevel(); + public DataFetcher alertSeverityLevel(); public DataFetcher alertUrl(); @@ -740,9 +748,9 @@ public interface GraphQLRentalVehicle { } public interface GraphQLRentalVehicleType { - public DataFetcher formFactor(); + public DataFetcher formFactor(); - public DataFetcher propulsionType(); + public DataFetcher propulsionType(); } /** An estimate for a ride on a hailed vehicle, like an Uber car. */ @@ -793,7 +801,7 @@ public interface GraphQLRoute { public DataFetcher longName(); - public DataFetcher mode(); + public DataFetcher mode(); public DataFetcher> patterns(); @@ -824,11 +832,11 @@ public interface GraphQLRouteType { /** Description of the reason, why the planner did not return any results */ public interface GraphQLRoutingError { - public DataFetcher code(); + public DataFetcher code(); public DataFetcher description(); - public DataFetcher inputField(); + public DataFetcher inputField(); } /** @@ -889,7 +897,7 @@ public interface GraphQLStop { public DataFetcher vehicleType(); - public DataFetcher wheelchairBoarding(); + public DataFetcher wheelchairBoarding(); public DataFetcher zoneId(); } @@ -1045,7 +1053,7 @@ public interface GraphQLTrip { public DataFetcher tripShortName(); - public DataFetcher wheelchairAccessible(); + public DataFetcher wheelchairAccessible(); } /** This is used for alert entities that we don't explicitly handle or they are missing. */ @@ -1236,7 +1244,7 @@ public interface GraphQLServiceTimeRange { } public interface GraphQLStep { - public DataFetcher absoluteDirection(); + public DataFetcher absoluteDirection(); public DataFetcher> alerts(); @@ -1254,7 +1262,7 @@ public interface GraphQLStep { public DataFetcher lon(); - public DataFetcher relativeDirection(); + public DataFetcher relativeDirection(); public DataFetcher stayOn(); diff --git a/src/test/java/org/opentripplanner/generate/doc/GraphQLTutorialDocTest.java b/src/test/java/org/opentripplanner/generate/doc/GraphQLTutorialDocTest.java index caba9a0e009..4589a4d8c1b 100644 --- a/src/test/java/org/opentripplanner/generate/doc/GraphQLTutorialDocTest.java +++ b/src/test/java/org/opentripplanner/generate/doc/GraphQLTutorialDocTest.java @@ -22,7 +22,7 @@ public class GraphQLTutorialDocTest { private static final File TEMPLATE = new File(TEMPLATE_ROOT, "GraphQL-Tutorial.md"); - private static final File OUT_FILE = new File(DOCS_ROOT, "GraphQL-Tutorial.md"); + private static final File OUT_FILE = new File(DOCS_ROOT + "/apis", "GraphQL-Tutorial.md"); /** * NOTE! This test updates the {@code docs/GraphQlTutorial.md} document based on the latest From 6de1cf2e98b32a278aef805bb1f8a89ed0cbd398 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 31 Aug 2023 09:47:11 +0200 Subject: [PATCH 17/95] Use static imports --- .../apis/gtfs/mapping/AlertCauseMapper.java | 29 +++++----- .../apis/gtfs/mapping/AlertEffectMapper.java | 27 +++++----- .../apis/gtfs/mapping/DirectionMapper.java | 54 ++++++++++--------- .../apis/gtfs/mapping/SeverityMapper.java | 13 ++--- 4 files changed, 64 insertions(+), 59 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/AlertCauseMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/AlertCauseMapper.java index 860b4cf7726..eff6479e93c 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/mapping/AlertCauseMapper.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/AlertCauseMapper.java @@ -1,6 +1,7 @@ package org.opentripplanner.apis.gtfs.mapping; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAlertCauseType; import org.opentripplanner.routing.alertpatch.AlertCause; /** @@ -12,23 +13,23 @@ public class AlertCauseMapper { * Returns GraphQL API string counter part for internal {@link AlertCause} enum. Defaults to * returning UNKNOWN_CAUSE. */ - public static GraphQLTypes.GraphQLAlertCauseType getGraphQLCause(AlertCause cause) { + public static GraphQLAlertCauseType getGraphQLCause(AlertCause cause) { if (cause == null) { - return GraphQLTypes.GraphQLAlertCauseType.UNKNOWN_CAUSE; + return GraphQLAlertCauseType.UNKNOWN_CAUSE; } return switch (cause) { - case UNKNOWN_CAUSE -> GraphQLTypes.GraphQLAlertCauseType.UNKNOWN_CAUSE; - case OTHER_CAUSE -> GraphQLTypes.GraphQLAlertCauseType.OTHER_CAUSE; - case TECHNICAL_PROBLEM -> GraphQLTypes.GraphQLAlertCauseType.TECHNICAL_PROBLEM; - case STRIKE -> GraphQLTypes.GraphQLAlertCauseType.STRIKE; - case DEMONSTRATION -> GraphQLTypes.GraphQLAlertCauseType.DEMONSTRATION; - case ACCIDENT -> GraphQLTypes.GraphQLAlertCauseType.ACCIDENT; - case HOLIDAY -> GraphQLTypes.GraphQLAlertCauseType.HOLIDAY; - case WEATHER -> GraphQLTypes.GraphQLAlertCauseType.WEATHER; - case MAINTENANCE -> GraphQLTypes.GraphQLAlertCauseType.MAINTENANCE; - case CONSTRUCTION -> GraphQLTypes.GraphQLAlertCauseType.CONSTRUCTION; - case POLICE_ACTIVITY -> GraphQLTypes.GraphQLAlertCauseType.POLICE_ACTIVITY; - case MEDICAL_EMERGENCY -> GraphQLTypes.GraphQLAlertCauseType.MEDICAL_EMERGENCY; + case UNKNOWN_CAUSE -> GraphQLAlertCauseType.UNKNOWN_CAUSE; + case OTHER_CAUSE -> GraphQLAlertCauseType.OTHER_CAUSE; + case TECHNICAL_PROBLEM -> GraphQLAlertCauseType.TECHNICAL_PROBLEM; + case STRIKE -> GraphQLAlertCauseType.STRIKE; + case DEMONSTRATION -> GraphQLAlertCauseType.DEMONSTRATION; + case ACCIDENT -> GraphQLAlertCauseType.ACCIDENT; + case HOLIDAY -> GraphQLAlertCauseType.HOLIDAY; + case WEATHER -> GraphQLAlertCauseType.WEATHER; + case MAINTENANCE -> GraphQLAlertCauseType.MAINTENANCE; + case CONSTRUCTION -> GraphQLAlertCauseType.CONSTRUCTION; + case POLICE_ACTIVITY -> GraphQLAlertCauseType.POLICE_ACTIVITY; + case MEDICAL_EMERGENCY -> GraphQLAlertCauseType.MEDICAL_EMERGENCY; }; } } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/AlertEffectMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/AlertEffectMapper.java index 555d89adf7d..022dd6cde26 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/mapping/AlertEffectMapper.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/AlertEffectMapper.java @@ -1,6 +1,7 @@ package org.opentripplanner.apis.gtfs.mapping; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAlertEffectType; import org.opentripplanner.routing.alertpatch.AlertEffect; /** @@ -12,22 +13,22 @@ public class AlertEffectMapper { * Returns GraphQL API string counter part for internal {@link AlertEffect} enum. Defaults * to returning UNKNOWN_Effect. */ - public static GraphQLTypes.GraphQLAlertEffectType getGraphQLEffect(AlertEffect effect) { + public static GraphQLAlertEffectType getGraphQLEffect(AlertEffect effect) { if (effect == null) { - return GraphQLTypes.GraphQLAlertEffectType.UNKNOWN_EFFECT; + return GraphQLAlertEffectType.UNKNOWN_EFFECT; } return switch (effect) { - case NO_SERVICE -> GraphQLTypes.GraphQLAlertEffectType.NO_SERVICE; - case REDUCED_SERVICE -> GraphQLTypes.GraphQLAlertEffectType.REDUCED_SERVICE; - case SIGNIFICANT_DELAYS -> GraphQLTypes.GraphQLAlertEffectType.SIGNIFICANT_DELAYS; - case DETOUR -> GraphQLTypes.GraphQLAlertEffectType.DETOUR; - case ADDITIONAL_SERVICE -> GraphQLTypes.GraphQLAlertEffectType.ADDITIONAL_SERVICE; - case MODIFIED_SERVICE -> GraphQLTypes.GraphQLAlertEffectType.MODIFIED_SERVICE; - case OTHER_EFFECT -> GraphQLTypes.GraphQLAlertEffectType.OTHER_EFFECT; - case UNKNOWN_EFFECT -> GraphQLTypes.GraphQLAlertEffectType.UNKNOWN_EFFECT; - case STOP_MOVED -> GraphQLTypes.GraphQLAlertEffectType.STOP_MOVED; - case NO_EFFECT -> GraphQLTypes.GraphQLAlertEffectType.NO_EFFECT; - case ACCESSIBILITY_ISSUE -> GraphQLTypes.GraphQLAlertEffectType.ACCESSIBILITY_ISSUE; + case NO_SERVICE -> GraphQLAlertEffectType.NO_SERVICE; + case REDUCED_SERVICE -> GraphQLAlertEffectType.REDUCED_SERVICE; + case SIGNIFICANT_DELAYS -> GraphQLAlertEffectType.SIGNIFICANT_DELAYS; + case DETOUR -> GraphQLAlertEffectType.DETOUR; + case ADDITIONAL_SERVICE -> GraphQLAlertEffectType.ADDITIONAL_SERVICE; + case MODIFIED_SERVICE -> GraphQLAlertEffectType.MODIFIED_SERVICE; + case OTHER_EFFECT -> GraphQLAlertEffectType.OTHER_EFFECT; + case UNKNOWN_EFFECT -> GraphQLAlertEffectType.UNKNOWN_EFFECT; + case STOP_MOVED -> GraphQLAlertEffectType.STOP_MOVED; + case NO_EFFECT -> GraphQLAlertEffectType.NO_EFFECT; + case ACCESSIBILITY_ISSUE -> GraphQLAlertEffectType.ACCESSIBILITY_ISSUE; }; } } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/DirectionMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/DirectionMapper.java index 5de932b2753..1deb6e3bb0e 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/mapping/DirectionMapper.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/DirectionMapper.java @@ -2,44 +2,46 @@ import javax.annotation.Nonnull; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAbsoluteDirection; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRelativeDirection; import org.opentripplanner.model.plan.AbsoluteDirection; import org.opentripplanner.model.plan.RelativeDirection; public final class DirectionMapper { @Nonnull - public static GraphQLTypes.GraphQLAbsoluteDirection map(AbsoluteDirection dir) { + public static GraphQLAbsoluteDirection map(AbsoluteDirection dir) { return switch (dir) { - case NORTH -> GraphQLTypes.GraphQLAbsoluteDirection.NORTH; - case NORTHEAST -> GraphQLTypes.GraphQLAbsoluteDirection.NORTHEAST; - case EAST -> GraphQLTypes.GraphQLAbsoluteDirection.EAST; - case SOUTHEAST -> GraphQLTypes.GraphQLAbsoluteDirection.SOUTHEAST; - case SOUTH -> GraphQLTypes.GraphQLAbsoluteDirection.SOUTH; - case SOUTHWEST -> GraphQLTypes.GraphQLAbsoluteDirection.SOUTHWEST; - case WEST -> GraphQLTypes.GraphQLAbsoluteDirection.WEST; - case NORTHWEST -> GraphQLTypes.GraphQLAbsoluteDirection.NORTHWEST; + case NORTH -> GraphQLAbsoluteDirection.NORTH; + case NORTHEAST -> GraphQLAbsoluteDirection.NORTHEAST; + case EAST -> GraphQLAbsoluteDirection.EAST; + case SOUTHEAST -> GraphQLAbsoluteDirection.SOUTHEAST; + case SOUTH -> GraphQLAbsoluteDirection.SOUTH; + case SOUTHWEST -> GraphQLAbsoluteDirection.SOUTHWEST; + case WEST -> GraphQLAbsoluteDirection.WEST; + case NORTHWEST -> GraphQLAbsoluteDirection.NORTHWEST; }; } @Nonnull - public static GraphQLTypes.GraphQLRelativeDirection map(RelativeDirection relativeDirection) { + public static GraphQLRelativeDirection map(RelativeDirection relativeDirection) { return switch (relativeDirection) { - case DEPART -> GraphQLTypes.GraphQLRelativeDirection.DEPART; - case HARD_LEFT -> GraphQLTypes.GraphQLRelativeDirection.HARD_LEFT; - case LEFT -> GraphQLTypes.GraphQLRelativeDirection.LEFT; - case SLIGHTLY_LEFT -> GraphQLTypes.GraphQLRelativeDirection.SLIGHTLY_LEFT; - case CONTINUE -> GraphQLTypes.GraphQLRelativeDirection.CONTINUE; - case SLIGHTLY_RIGHT -> GraphQLTypes.GraphQLRelativeDirection.SLIGHTLY_RIGHT; - case RIGHT -> GraphQLTypes.GraphQLRelativeDirection.RIGHT; - case HARD_RIGHT -> GraphQLTypes.GraphQLRelativeDirection.HARD_RIGHT; - case CIRCLE_CLOCKWISE -> GraphQLTypes.GraphQLRelativeDirection.CIRCLE_CLOCKWISE; - case CIRCLE_COUNTERCLOCKWISE -> GraphQLTypes.GraphQLRelativeDirection.CIRCLE_COUNTERCLOCKWISE; - case ELEVATOR -> GraphQLTypes.GraphQLRelativeDirection.ELEVATOR; - case UTURN_LEFT -> GraphQLTypes.GraphQLRelativeDirection.UTURN_LEFT; - case UTURN_RIGHT -> GraphQLTypes.GraphQLRelativeDirection.UTURN_RIGHT; - case ENTER_STATION -> GraphQLTypes.GraphQLRelativeDirection.ENTER_STATION; - case EXIT_STATION -> GraphQLTypes.GraphQLRelativeDirection.EXIT_STATION; - case FOLLOW_SIGNS -> GraphQLTypes.GraphQLRelativeDirection.FOLLOW_SIGNS; + case DEPART -> GraphQLRelativeDirection.DEPART; + case HARD_LEFT -> GraphQLRelativeDirection.HARD_LEFT; + case LEFT -> GraphQLRelativeDirection.LEFT; + case SLIGHTLY_LEFT -> GraphQLRelativeDirection.SLIGHTLY_LEFT; + case CONTINUE -> GraphQLRelativeDirection.CONTINUE; + case SLIGHTLY_RIGHT -> GraphQLRelativeDirection.SLIGHTLY_RIGHT; + case RIGHT -> GraphQLRelativeDirection.RIGHT; + case HARD_RIGHT -> GraphQLRelativeDirection.HARD_RIGHT; + case CIRCLE_CLOCKWISE -> GraphQLRelativeDirection.CIRCLE_CLOCKWISE; + case CIRCLE_COUNTERCLOCKWISE -> GraphQLRelativeDirection.CIRCLE_COUNTERCLOCKWISE; + case ELEVATOR -> GraphQLRelativeDirection.ELEVATOR; + case UTURN_LEFT -> GraphQLRelativeDirection.UTURN_LEFT; + case UTURN_RIGHT -> GraphQLRelativeDirection.UTURN_RIGHT; + case ENTER_STATION -> GraphQLRelativeDirection.ENTER_STATION; + case EXIT_STATION -> GraphQLRelativeDirection.EXIT_STATION; + case FOLLOW_SIGNS -> GraphQLRelativeDirection.FOLLOW_SIGNS; }; } } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/SeverityMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/SeverityMapper.java index 2286205e52b..e6c3b366b45 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/mapping/SeverityMapper.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/SeverityMapper.java @@ -1,6 +1,7 @@ package org.opentripplanner.apis.gtfs.mapping; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAlertSeverityLevelType; import org.opentripplanner.routing.alertpatch.AlertSeverity; /** @@ -12,18 +13,18 @@ public class SeverityMapper { * Returns GraphQL API string counter part for internal {@link AlertSeverity} enum. Defaults * to returning UNKNOWN_SEVERITY. */ - public static GraphQLTypes.GraphQLAlertSeverityLevelType getGraphQLSeverity( + public static GraphQLAlertSeverityLevelType getGraphQLSeverity( AlertSeverity severity ) { if (severity == null) { - return GraphQLTypes.GraphQLAlertSeverityLevelType.UNKNOWN_SEVERITY; + return GraphQLAlertSeverityLevelType.UNKNOWN_SEVERITY; } return switch (severity) { - case INFO -> GraphQLTypes.GraphQLAlertSeverityLevelType.INFO; - case VERY_SLIGHT, SLIGHT, WARNING -> GraphQLTypes.GraphQLAlertSeverityLevelType.WARNING; - case VERY_SEVERE, SEVERE -> GraphQLTypes.GraphQLAlertSeverityLevelType.SEVERE; + case INFO -> GraphQLAlertSeverityLevelType.INFO; + case VERY_SLIGHT, SLIGHT, WARNING -> GraphQLAlertSeverityLevelType.WARNING; + case VERY_SEVERE, SEVERE -> GraphQLAlertSeverityLevelType.SEVERE; case UNDEFINED, - UNKNOWN_SEVERITY -> GraphQLTypes.GraphQLAlertSeverityLevelType.UNKNOWN_SEVERITY; + UNKNOWN_SEVERITY -> GraphQLAlertSeverityLevelType.UNKNOWN_SEVERITY; }; } } From 6f02515490a0ae4734e5d1cb52d2c12ddfadfc61 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 31 Aug 2023 09:49:49 +0200 Subject: [PATCH 18/95] Remove from sandbox documents --- docs/SandboxExtension.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/SandboxExtension.md b/docs/SandboxExtension.md index c14b1afef8b..55ee214979a 100644 --- a/docs/SandboxExtension.md +++ b/docs/SandboxExtension.md @@ -14,7 +14,6 @@ provided "as is". - [Geocoder API](sandbox/GeocoderAPI.md) - Adds an API to search for corners, stops and stations. - [Transfer analyser](sandbox/transferanalyzer.md) - Module used for analyzing the transfers between nearby stops generated by routing via OSM data. -- [GTFS GraphQL API](apis/GTFS-GraphQL-API.md) - HSL's GraphQL API used by the Digitransit project. - [Transmodel API](sandbox/TransmodelApi.md) - Enturs GraphQL Transmodel API. - [SIRI Updater](sandbox/SiriUpdater.md) - Update OTP with realtime information from a Transmodel SIRI data source. - [SIRI Azure Updater](sandbox/SiriAzureUpdater.md) - fetch SIRI realtime data through *Azure Service Bus* From 0ba31e57a9764b9531ac4180e983b2dbd5a30cb6 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 31 Aug 2023 10:03:18 +0200 Subject: [PATCH 19/95] Change path of schema file --- magidoc.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/magidoc.mjs b/magidoc.mjs index 814ca3e8435..40a6360a716 100644 --- a/magidoc.mjs +++ b/magidoc.mjs @@ -1,7 +1,7 @@ export default { introspection: { type: 'sdl', - paths: ['src/ext/resources/gtfsgraphqlapi/schema.graphqls'], + paths: ['src/main/resources/gtfsgraphqlapi/schema.graphqls'], }, website: { template: 'carbon-multi-page', From b4be6c80c255589795360954e260c8c81a456df0 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 31 Aug 2023 10:07:19 +0200 Subject: [PATCH 20/95] Reformat code --- .../opentripplanner/apis/gtfs/mapping/SeverityMapper.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/SeverityMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/SeverityMapper.java index e6c3b366b45..f8abe861992 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/mapping/SeverityMapper.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/SeverityMapper.java @@ -13,9 +13,7 @@ public class SeverityMapper { * Returns GraphQL API string counter part for internal {@link AlertSeverity} enum. Defaults * to returning UNKNOWN_SEVERITY. */ - public static GraphQLAlertSeverityLevelType getGraphQLSeverity( - AlertSeverity severity - ) { + public static GraphQLAlertSeverityLevelType getGraphQLSeverity(AlertSeverity severity) { if (severity == null) { return GraphQLAlertSeverityLevelType.UNKNOWN_SEVERITY; } @@ -23,8 +21,7 @@ public static GraphQLAlertSeverityLevelType getGraphQLSeverity( case INFO -> GraphQLAlertSeverityLevelType.INFO; case VERY_SLIGHT, SLIGHT, WARNING -> GraphQLAlertSeverityLevelType.WARNING; case VERY_SEVERE, SEVERE -> GraphQLAlertSeverityLevelType.SEVERE; - case UNDEFINED, - UNKNOWN_SEVERITY -> GraphQLAlertSeverityLevelType.UNKNOWN_SEVERITY; + case UNDEFINED, UNKNOWN_SEVERITY -> GraphQLAlertSeverityLevelType.UNKNOWN_SEVERITY; }; } } From ae183914167dab8828eadfe6b4c9677479c00086 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 31 Aug 2023 14:20:32 +0200 Subject: [PATCH 21/95] Update link in documentation --- magidoc.mjs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/magidoc.mjs b/magidoc.mjs index 40a6360a716..ac73845e33a 100644 --- a/magidoc.mjs +++ b/magidoc.mjs @@ -24,12 +24,12 @@ at http://localhost:8080/graphiql ![GraphiQL screenshot](https://docs.opentripplanner.org/en/dev-2.x/images/graphiql.png) -## Activation +## Configuration -This API used to be off by default but since June '23 that has changed. +This API is activated by default. To learn how to deactivate it, read the -[documentation](https://docs.opentripplanner.org/en/dev-2.x/sandbox/GtfsGraphQlApi/). +[documentation](https://docs.opentripplanner.org/en/dev-2.x/apis/GTFS-GraphQ-API/). `, }], appTitle: 'OTP GTFS GraphQL API', From 68a267dad01b94ce6c822fcdb0cbfca70c70a9a6 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Fri, 15 Sep 2023 11:55:08 +0200 Subject: [PATCH 22/95] doc: Add checklist item for GitHub maintenance in release checklist --- docs/ReleaseChecklist.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/ReleaseChecklist.md b/docs/ReleaseChecklist.md index 6a46c0074cb..88fb20c0580 100644 --- a/docs/ReleaseChecklist.md +++ b/docs/ReleaseChecklist.md @@ -87,6 +87,20 @@ manually is more tedious, but keeps eyes on each step and is less prone to failu * Mention the new version number. * Provide links to the new developer documentation. * Provide links to the artifacts directory on Maven Central. +* Prepare for the next release in GitHub by renaming the released millstone and creating a new + millstone for the next release. Then make sure all issues and PRs are tagged with the correct + millstone. + * Close open PRs older than 2 years, make sure the millstone is set to `Rejected`. + * Rename the old millstone from `x.y (Next Release)` to `x.y`. All issues and PRs assigned to + this millstone is updated. + * Create a new millstone: `x.y+1 (Next Release)` + * All PullRequests SHOULD have a millstone (except some very old ones) + * Assign all *open* PRs to this new millstone `x.y+1 (Next Release)`. + * Assign all *closed* PRs without a millstone in the release to the released millstone + `x.y`. Make sure NOT to include very old PRs or PRs merged after the release(if any). + * Some issues have a millstone, but not all. + * Move all open issues with the released millstone `x.y` to the next release + `x.y+1 (Next Release)`. ## Artifact Signing From 7b48c4d664a55790e106b6c1028fd69d04d584b6 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 15 Sep 2023 16:19:29 +0200 Subject: [PATCH 23/95] Move resource file into org/opentripplanner/... folder structure --- .github/workflows/cibuild.yml | 2 +- magidoc.mjs | 2 +- .../java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java | 5 ++--- .../opentripplanner/apis/gtfs/generated/graphql-codegen.yml | 2 +- .../opentripplanner/apis/gtfs}/schema.graphqls | 0 5 files changed, 5 insertions(+), 6 deletions(-) rename src/main/resources/{gtfsgraphqlapi => org/opentripplanner/apis/gtfs}/schema.graphqls (100%) diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml index d326106e553..4d020b8871f 100644 --- a/.github/workflows/cibuild.yml +++ b/.github/workflows/cibuild.yml @@ -152,7 +152,7 @@ jobs: # schema hasn't changed. # example commit: https://github.com/opentripplanner/docs/commit/45e6ddf8e4a4 - SCHEMA_FILE_MODIFIED=`git log -n 1 --pretty=format:%ct src/main/resources/gtfsgraphqlapi/schema.graphqls` + SCHEMA_FILE_MODIFIED=`git log -n 1 --pretty=format:%ct src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls` echo "schema modified at ${SCHEMA_FILE_MODIFIED}" git checkout $LOCAL_BRANCH DOCS_MODIFIED=`git log -n 1 --pretty=format:%ct api/dev-2.x/graphql-gtfs/introduction.html` diff --git a/magidoc.mjs b/magidoc.mjs index ac73845e33a..44997e3fc9d 100644 --- a/magidoc.mjs +++ b/magidoc.mjs @@ -1,7 +1,7 @@ export default { introspection: { type: 'sdl', - paths: ['src/main/resources/gtfsgraphqlapi/schema.graphqls'], + paths: ['src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls'], }, website: { template: 'carbon-multi-page', diff --git a/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java index 7a1aff65080..a8c31766b6f 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java @@ -1,6 +1,5 @@ package org.opentripplanner.apis.gtfs; -import com.google.common.io.Resources; import graphql.ExecutionInput; import graphql.ExecutionResult; import graphql.GraphQL; @@ -17,11 +16,11 @@ import io.micrometer.core.instrument.Metrics; import jakarta.ws.rs.core.Response; import java.net.URL; -import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -103,7 +102,7 @@ class GtfsGraphQLIndex { protected static GraphQLSchema buildSchema() { try { - URL url = Resources.getResource("gtfsgraphqlapi/schema.graphqls"); + URL url = Objects.requireNonNull(GtfsGraphQLIndex.class.getResource("schema.graphqls")); TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(url.openStream()); IntrospectionTypeWiring typeWiring = new IntrospectionTypeWiring(typeRegistry); RuntimeWiring runtimeWiring = RuntimeWiring diff --git a/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml b/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml index 2f068befe90..97840be0292 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml @@ -1,4 +1,4 @@ -schema: ../../../../../../resources/gtfsgraphqlapi/schema.graphqls +schema: ../../../../../../resources/org/opentripplanner/apis/gtfs/schema.graphqls generates: GraphQLTypes.java: diff --git a/src/main/resources/gtfsgraphqlapi/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls similarity index 100% rename from src/main/resources/gtfsgraphqlapi/schema.graphqls rename to src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls From 621b802d93a2eb595b645b77be21a36da7314842 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 15 Sep 2023 16:32:03 +0200 Subject: [PATCH 24/95] Also move test assertions into correct folder --- doc-templates/GraphQL-Tutorial.md | 2 +- .../org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java | 2 +- .../opentripplanner/apis/gtfs}/expectations/alerts.json | 0 .../opentripplanner/apis/gtfs}/expectations/patterns.json | 0 .../opentripplanner/apis/gtfs}/expectations/plan-extended.json | 0 .../opentripplanner/apis/gtfs}/expectations/plan-fares.json | 0 .../apis/gtfs}/expectations/plan-stop-positions.json | 0 .../opentripplanner/apis/gtfs}/expectations/plan.json | 0 .../opentripplanner/apis/gtfs}/expectations/routes.json | 0 .../opentripplanner/apis/gtfs}/expectations/stops.json | 0 .../apis/gtfs}/expectations/vehicle-parking.json | 0 .../opentripplanner/apis/gtfs}/expectations/walk-steps.json | 0 .../opentripplanner/apis/gtfs}/queries/alerts.graphql | 0 .../opentripplanner/apis/gtfs}/queries/patterns.graphql | 0 .../opentripplanner/apis/gtfs}/queries/plan-extended.graphql | 0 .../opentripplanner/apis/gtfs}/queries/plan-fares.graphql | 0 .../apis/gtfs}/queries/plan-stop-positions.graphql | 0 .../opentripplanner/apis/gtfs}/queries/plan.graphql | 0 .../opentripplanner/apis/gtfs}/queries/routes.graphql | 0 .../opentripplanner/apis/gtfs}/queries/stops.graphql | 0 .../opentripplanner/apis/gtfs}/queries/vehicle-parking.graphql | 0 .../opentripplanner/apis/gtfs}/queries/walk-steps.graphql | 0 22 files changed, 2 insertions(+), 2 deletions(-) rename src/test/resources/{gtfsgraphqlapi => org/opentripplanner/apis/gtfs}/expectations/alerts.json (100%) rename src/test/resources/{gtfsgraphqlapi => org/opentripplanner/apis/gtfs}/expectations/patterns.json (100%) rename src/test/resources/{gtfsgraphqlapi => org/opentripplanner/apis/gtfs}/expectations/plan-extended.json (100%) rename src/test/resources/{gtfsgraphqlapi => org/opentripplanner/apis/gtfs}/expectations/plan-fares.json (100%) rename src/test/resources/{gtfsgraphqlapi => org/opentripplanner/apis/gtfs}/expectations/plan-stop-positions.json (100%) rename src/test/resources/{gtfsgraphqlapi => org/opentripplanner/apis/gtfs}/expectations/plan.json (100%) rename src/test/resources/{gtfsgraphqlapi => org/opentripplanner/apis/gtfs}/expectations/routes.json (100%) rename src/test/resources/{gtfsgraphqlapi => org/opentripplanner/apis/gtfs}/expectations/stops.json (100%) rename src/test/resources/{gtfsgraphqlapi => org/opentripplanner/apis/gtfs}/expectations/vehicle-parking.json (100%) rename src/test/resources/{gtfsgraphqlapi => org/opentripplanner/apis/gtfs}/expectations/walk-steps.json (100%) rename src/test/resources/{gtfsgraphqlapi => org/opentripplanner/apis/gtfs}/queries/alerts.graphql (100%) rename src/test/resources/{gtfsgraphqlapi => org/opentripplanner/apis/gtfs}/queries/patterns.graphql (100%) rename src/test/resources/{gtfsgraphqlapi => org/opentripplanner/apis/gtfs}/queries/plan-extended.graphql (100%) rename src/test/resources/{gtfsgraphqlapi => org/opentripplanner/apis/gtfs}/queries/plan-fares.graphql (100%) rename src/test/resources/{gtfsgraphqlapi => org/opentripplanner/apis/gtfs}/queries/plan-stop-positions.graphql (100%) rename src/test/resources/{gtfsgraphqlapi => org/opentripplanner/apis/gtfs}/queries/plan.graphql (100%) rename src/test/resources/{gtfsgraphqlapi => org/opentripplanner/apis/gtfs}/queries/routes.graphql (100%) rename src/test/resources/{gtfsgraphqlapi => org/opentripplanner/apis/gtfs}/queries/stops.graphql (100%) rename src/test/resources/{gtfsgraphqlapi => org/opentripplanner/apis/gtfs}/queries/vehicle-parking.graphql (100%) rename src/test/resources/{gtfsgraphqlapi => org/opentripplanner/apis/gtfs}/queries/walk-steps.graphql (100%) diff --git a/doc-templates/GraphQL-Tutorial.md b/doc-templates/GraphQL-Tutorial.md index c91197743c5..51f0ef7880a 100644 --- a/doc-templates/GraphQL-Tutorial.md +++ b/doc-templates/GraphQL-Tutorial.md @@ -51,4 +51,4 @@ Most people want to get routing results out of OTP, so lets see the query for th Again, please use the autocomplete and documentation viewers to figure out what each input parameter and property means. -More examples for a variety of queries can also be found [in the test code](https://github.com/opentripplanner/OpenTripPlanner/tree/dev-2.x/src/test/resources/gtfsgraphqlapi/queries). \ No newline at end of file +More examples for a variety of queries can also be found [in the test code](https://github.com/opentripplanner/OpenTripPlanner/tree/dev-2.x/src/test/resources/org/opentripplanner/apis/gtfs/queries). \ No newline at end of file diff --git a/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java b/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java index da9f594ce6e..191c6e93abe 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java @@ -228,7 +228,7 @@ public TransitAlertService getTransitAlertService() { ); } - @FilePatternSource(pattern = "src/test/resources/gtfsgraphqlapi/queries/*.graphql") + @FilePatternSource(pattern = "src/test/resources/org/opentripplanner/apis/gtfs/queries/*.graphql") @ParameterizedTest(name = "Check GraphQL query in {0}") void graphQL(Path path) throws IOException { var query = Files.readString(path); diff --git a/src/test/resources/gtfsgraphqlapi/expectations/alerts.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/alerts.json similarity index 100% rename from src/test/resources/gtfsgraphqlapi/expectations/alerts.json rename to src/test/resources/org/opentripplanner/apis/gtfs/expectations/alerts.json diff --git a/src/test/resources/gtfsgraphqlapi/expectations/patterns.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/patterns.json similarity index 100% rename from src/test/resources/gtfsgraphqlapi/expectations/patterns.json rename to src/test/resources/org/opentripplanner/apis/gtfs/expectations/patterns.json diff --git a/src/test/resources/gtfsgraphqlapi/expectations/plan-extended.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan-extended.json similarity index 100% rename from src/test/resources/gtfsgraphqlapi/expectations/plan-extended.json rename to src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan-extended.json diff --git a/src/test/resources/gtfsgraphqlapi/expectations/plan-fares.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan-fares.json similarity index 100% rename from src/test/resources/gtfsgraphqlapi/expectations/plan-fares.json rename to src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan-fares.json diff --git a/src/test/resources/gtfsgraphqlapi/expectations/plan-stop-positions.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan-stop-positions.json similarity index 100% rename from src/test/resources/gtfsgraphqlapi/expectations/plan-stop-positions.json rename to src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan-stop-positions.json diff --git a/src/test/resources/gtfsgraphqlapi/expectations/plan.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan.json similarity index 100% rename from src/test/resources/gtfsgraphqlapi/expectations/plan.json rename to src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan.json diff --git a/src/test/resources/gtfsgraphqlapi/expectations/routes.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/routes.json similarity index 100% rename from src/test/resources/gtfsgraphqlapi/expectations/routes.json rename to src/test/resources/org/opentripplanner/apis/gtfs/expectations/routes.json diff --git a/src/test/resources/gtfsgraphqlapi/expectations/stops.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/stops.json similarity index 100% rename from src/test/resources/gtfsgraphqlapi/expectations/stops.json rename to src/test/resources/org/opentripplanner/apis/gtfs/expectations/stops.json diff --git a/src/test/resources/gtfsgraphqlapi/expectations/vehicle-parking.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/vehicle-parking.json similarity index 100% rename from src/test/resources/gtfsgraphqlapi/expectations/vehicle-parking.json rename to src/test/resources/org/opentripplanner/apis/gtfs/expectations/vehicle-parking.json diff --git a/src/test/resources/gtfsgraphqlapi/expectations/walk-steps.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json similarity index 100% rename from src/test/resources/gtfsgraphqlapi/expectations/walk-steps.json rename to src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json diff --git a/src/test/resources/gtfsgraphqlapi/queries/alerts.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/alerts.graphql similarity index 100% rename from src/test/resources/gtfsgraphqlapi/queries/alerts.graphql rename to src/test/resources/org/opentripplanner/apis/gtfs/queries/alerts.graphql diff --git a/src/test/resources/gtfsgraphqlapi/queries/patterns.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/patterns.graphql similarity index 100% rename from src/test/resources/gtfsgraphqlapi/queries/patterns.graphql rename to src/test/resources/org/opentripplanner/apis/gtfs/queries/patterns.graphql diff --git a/src/test/resources/gtfsgraphqlapi/queries/plan-extended.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/plan-extended.graphql similarity index 100% rename from src/test/resources/gtfsgraphqlapi/queries/plan-extended.graphql rename to src/test/resources/org/opentripplanner/apis/gtfs/queries/plan-extended.graphql diff --git a/src/test/resources/gtfsgraphqlapi/queries/plan-fares.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/plan-fares.graphql similarity index 100% rename from src/test/resources/gtfsgraphqlapi/queries/plan-fares.graphql rename to src/test/resources/org/opentripplanner/apis/gtfs/queries/plan-fares.graphql diff --git a/src/test/resources/gtfsgraphqlapi/queries/plan-stop-positions.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/plan-stop-positions.graphql similarity index 100% rename from src/test/resources/gtfsgraphqlapi/queries/plan-stop-positions.graphql rename to src/test/resources/org/opentripplanner/apis/gtfs/queries/plan-stop-positions.graphql diff --git a/src/test/resources/gtfsgraphqlapi/queries/plan.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/plan.graphql similarity index 100% rename from src/test/resources/gtfsgraphqlapi/queries/plan.graphql rename to src/test/resources/org/opentripplanner/apis/gtfs/queries/plan.graphql diff --git a/src/test/resources/gtfsgraphqlapi/queries/routes.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/routes.graphql similarity index 100% rename from src/test/resources/gtfsgraphqlapi/queries/routes.graphql rename to src/test/resources/org/opentripplanner/apis/gtfs/queries/routes.graphql diff --git a/src/test/resources/gtfsgraphqlapi/queries/stops.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/stops.graphql similarity index 100% rename from src/test/resources/gtfsgraphqlapi/queries/stops.graphql rename to src/test/resources/org/opentripplanner/apis/gtfs/queries/stops.graphql diff --git a/src/test/resources/gtfsgraphqlapi/queries/vehicle-parking.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/vehicle-parking.graphql similarity index 100% rename from src/test/resources/gtfsgraphqlapi/queries/vehicle-parking.graphql rename to src/test/resources/org/opentripplanner/apis/gtfs/queries/vehicle-parking.graphql diff --git a/src/test/resources/gtfsgraphqlapi/queries/walk-steps.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql similarity index 100% rename from src/test/resources/gtfsgraphqlapi/queries/walk-steps.graphql rename to src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql From 89be97910db29655e8cc655d52f2ae14285daad0 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 15 Sep 2023 16:43:03 +0200 Subject: [PATCH 25/95] Update docs test --- docs/apis/GraphQL-Tutorial.md | 2 +- .../generate/doc/GraphQLTutorialDocTest.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/apis/GraphQL-Tutorial.md b/docs/apis/GraphQL-Tutorial.md index 3c12a7c39bf..cdac61c47e2 100644 --- a/docs/apis/GraphQL-Tutorial.md +++ b/docs/apis/GraphQL-Tutorial.md @@ -126,4 +126,4 @@ Most people want to get routing results out of OTP, so lets see the query for th Again, please use the autocomplete and documentation viewers to figure out what each input parameter and property means. -More examples for a variety of queries can also be found [in the test code](https://github.com/opentripplanner/OpenTripPlanner/tree/dev-2.x/src/test/resources/gtfsgraphqlapi/queries). \ No newline at end of file +More examples for a variety of queries can also be found [in the test code](https://github.com/opentripplanner/OpenTripPlanner/tree/dev-2.x/src/test/resources/org/opentripplanner/apis/gtfs/queries). \ No newline at end of file diff --git a/src/test/java/org/opentripplanner/generate/doc/GraphQLTutorialDocTest.java b/src/test/java/org/opentripplanner/generate/doc/GraphQLTutorialDocTest.java index 4589a4d8c1b..bf2b092e092 100644 --- a/src/test/java/org/opentripplanner/generate/doc/GraphQLTutorialDocTest.java +++ b/src/test/java/org/opentripplanner/generate/doc/GraphQLTutorialDocTest.java @@ -37,8 +37,8 @@ public void updateTutorialDoc() throws IOException { String doc = readFile(TEMPLATE); String original = readFile(OUT_FILE); - var routeQuery = getGraphQlQuery("gtfsgraphqlapi/queries/routes.graphql"); - var planQuery = getGraphQlQuery("gtfsgraphqlapi/queries/plan.graphql"); + var routeQuery = getGraphQlQuery("routes.graphql"); + var planQuery = getGraphQlQuery("plan.graphql"); doc = replaceSection(doc, "route-query", routeQuery); doc = replaceSection(doc, "plan-query", planQuery); @@ -49,7 +49,7 @@ public void updateTutorialDoc() throws IOException { @Nonnull private static String getGraphQlQuery(String resourceName) throws IOException { - var url = Resources.getResource(resourceName); + var url = Resources.getResource("org/opentripplanner/apis/gtfs/queries/" + resourceName); var query = TemplateUtil.graphQlExample(Resources.toString(url, StandardCharsets.UTF_8)); assertNotNull(query); return query; From aa8a6d73a7fd8378b0af84e6072f110bfe32e6a4 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 15 Sep 2023 16:48:04 +0200 Subject: [PATCH 26/95] Make URL a link --- docs/apis/GTFS-GraphQL-API.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/apis/GTFS-GraphQL-API.md b/docs/apis/GTFS-GraphQL-API.md index f31be0872c5..a9937afc6b2 100644 --- a/docs/apis/GTFS-GraphQL-API.md +++ b/docs/apis/GTFS-GraphQL-API.md @@ -1,13 +1,12 @@ # GTFS GraphQL API -The GTFS GraphQL API was created for the Digitransit project and is used heavily by -[digitransit-ui](https://github.com/HSLdevcom/digitransit-ui). +The GTFS GraphQL API is a general purpose API which was created for the Digitransit project and is +used heavily by [digitransit-ui](https://github.com/HSLdevcom/digitransit-ui). -[otp-react-redux](https://github.com/opentripplanner/otp-react-redux) has also migrated to this API -in 2023. +[otp-react-redux](https://github.com/opentripplanner/otp-react-redux) has also migrated to this API in 2023. ## URLs - - GraphQL endpoint: `http://localhost:8080/otp/routers/default/index/graphql` + - GraphQL endpoint: [`http://localhost:8080/otp/routers/default/index/graphql`](http://localhost:8080/otp/routers/default/index/graphql) - HTML schema documentation: [https://docs.opentripplanner.org/api/dev-2.x/graphql-gtfs/](https://docs.opentripplanner.org/api/dev-2.x/graphql-gtfs/) - Built-in visual GraphQL client: [http://localhost:8080/graphiql](http://localhost:8080/graphiql) From 5521f3e6a58307ca94c565955464f3991c2961bb Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Fri, 15 Sep 2023 17:01:39 +0200 Subject: [PATCH 27/95] Apply suggestions from code review Co-authored-by: Leonard Ehrenfried --- docs/ReleaseChecklist.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/ReleaseChecklist.md b/docs/ReleaseChecklist.md index 88fb20c0580..65ce8eb383e 100644 --- a/docs/ReleaseChecklist.md +++ b/docs/ReleaseChecklist.md @@ -87,19 +87,19 @@ manually is more tedious, but keeps eyes on each step and is less prone to failu * Mention the new version number. * Provide links to the new developer documentation. * Provide links to the artifacts directory on Maven Central. -* Prepare for the next release in GitHub by renaming the released millstone and creating a new - millstone for the next release. Then make sure all issues and PRs are tagged with the correct - millstone. - * Close open PRs older than 2 years, make sure the millstone is set to `Rejected`. - * Rename the old millstone from `x.y (Next Release)` to `x.y`. All issues and PRs assigned to - this millstone is updated. - * Create a new millstone: `x.y+1 (Next Release)` - * All PullRequests SHOULD have a millstone (except some very old ones) - * Assign all *open* PRs to this new millstone `x.y+1 (Next Release)`. - * Assign all *closed* PRs without a millstone in the release to the released millstone +* Prepare for the next release in GitHub by renaming the released milestone and creating a new + milestone for the next release. Then make sure all issues and PRs are tagged with the correct + milestone. + * Close open PRs older than 2 years, make sure the milestone is set to `Rejected`. + * Rename the old milestone from `x.y (Next Release)` to `x.y`. All issues and PRs assigned to + this milestone is updated. + * Create a new milestone: `x.y+1 (Next Release)` + * All PullRequests SHOULD have a milestone (except some very old ones) + * Assign all *open* PRs to this new milestone `x.y+1 (Next Release)`. + * Assign all *closed* PRs without a milestone in the release to the released milestone `x.y`. Make sure NOT to include very old PRs or PRs merged after the release(if any). - * Some issues have a millstone, but not all. - * Move all open issues with the released millstone `x.y` to the next release + * Some issues have a milestone, but not all. + * Move all open issues with the released milestone `x.y` to the next release `x.y+1 (Next Release)`. ## Artifact Signing From 76cfb122f8b8f13c59134c164d8c0032de0507dd Mon Sep 17 00:00:00 2001 From: Vesa Meskanen Date: Sun, 17 Sep 2023 10:31:19 +0300 Subject: [PATCH 28/95] Implement sensible barrier vertex logic --- .../openstreetmap/model/OSMNode.java | 13 +++++++++++++ .../street/model/vertex/BarrierVertex.java | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMNode.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMNode.java index 53a47298dae..2a007807b83 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMNode.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMNode.java @@ -1,6 +1,7 @@ package org.opentripplanner.openstreetmap.model; import org.locationtech.jts.geom.Coordinate; +import org.opentripplanner.street.model.StreetTraversalPermission; public class OSMNode extends OSMWithTags { @@ -59,6 +60,18 @@ public boolean isBarrier() { ); } + /** + * Consider barrier tag in permissions. Leave the rest for the super class. + */ + @Override + public StreetTraversalPermission overridePermissions(StreetTraversalPermission def) { + StreetTraversalPermission permission = def; + if (isBollard()) { + permission = permission.remove(StreetTraversalPermission.CAR); + } + return super.overridePermissions(permission); + } + @Override public String url() { return String.format("https://www.openstreetmap.org/node/%d", getId()); diff --git a/src/main/java/org/opentripplanner/street/model/vertex/BarrierVertex.java b/src/main/java/org/opentripplanner/street/model/vertex/BarrierVertex.java index 13b7335fda5..27200898c65 100644 --- a/src/main/java/org/opentripplanner/street/model/vertex/BarrierVertex.java +++ b/src/main/java/org/opentripplanner/street/model/vertex/BarrierVertex.java @@ -18,7 +18,7 @@ public class BarrierVertex extends OsmVertex { //According to OSM default permissions are access=no, foot=yes, bicycle=yes public static final StreetTraversalPermission defaultBarrierPermissions = - StreetTraversalPermission.PEDESTRIAN_AND_BICYCLE; + StreetTraversalPermission.ALL; private StreetTraversalPermission barrierPermissions; public BarrierVertex(double x, double y, long nodeId) { From 4db1d6e00d7ec7de5e93bcc5bfe79ae719539105 Mon Sep 17 00:00:00 2001 From: Vesa Meskanen Date: Sun, 17 Sep 2023 11:00:54 +0300 Subject: [PATCH 29/95] Uodate bollard tests --- .../model/vertex/BarrierVertexTest.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/test/java/org/opentripplanner/street/model/vertex/BarrierVertexTest.java b/src/test/java/org/opentripplanner/street/model/vertex/BarrierVertexTest.java index 127f34e17ca..4e2b9872625 100644 --- a/src/test/java/org/opentripplanner/street/model/vertex/BarrierVertexTest.java +++ b/src/test/java/org/opentripplanner/street/model/vertex/BarrierVertexTest.java @@ -94,8 +94,8 @@ public void testBarrierPermissions() { @Test public void testStreetsWithBollard() { Graph graph = new Graph(); - //default permissions are PEDESTRIAND and BICYCLE BarrierVertex bv = new BarrierVertex(2.0, 2.0, 0); + bv.setBarrierPermissions(StreetTraversalPermission.PEDESTRIAN_AND_BICYCLE); StreetVertex endVertex = StreetModelForTest.intersectionVertex("end_vertex", 1.0, 2.0); @@ -139,18 +139,18 @@ public void testStreetsWithBollard() { assertTrue(endVertex_to_bv_forward.canTraverse(TraverseMode.BICYCLE)); assertTrue(endVertex_to_bv_forward.canTraverse(TraverseMode.WALK)); - //tests bollard which doesn't allow cycling - BarrierVertex noBicycleBollard = new BarrierVertex(1.5, 1, 0); - noBicycleBollard.setBarrierPermissions(StreetTraversalPermission.PEDESTRIAN); - StreetEdge no_bike_to_endVertex = edge(noBicycleBollard, endVertex, 100, false); + //tests bollard which allows only walking + BarrierVertex onlyWalkBollard = new BarrierVertex(1.5, 1, 0); + onlyWalkBollard.setBarrierPermissions(StreetTraversalPermission.PEDESTRIAN); + StreetEdge edge = edge(onlyWalkBollard, endVertex, 100, false); - assertTrue(no_bike_to_endVertex.canTraverse(new TraverseModeSet(TraverseMode.CAR))); - assertTrue(no_bike_to_endVertex.canTraverse(new TraverseModeSet(TraverseMode.BICYCLE))); - assertTrue(no_bike_to_endVertex.canTraverse(new TraverseModeSet(TraverseMode.WALK))); + assertTrue(edge.canTraverse(new TraverseModeSet(TraverseMode.CAR))); + assertTrue(edge.canTraverse(new TraverseModeSet(TraverseMode.BICYCLE))); + assertTrue(edge.canTraverse(new TraverseModeSet(TraverseMode.WALK))); - assertFalse(no_bike_to_endVertex.canTraverse(TraverseMode.CAR)); - assertFalse(no_bike_to_endVertex.canTraverse(TraverseMode.BICYCLE)); - assertTrue(no_bike_to_endVertex.canTraverse(TraverseMode.WALK)); + assertFalse(edge.canTraverse(TraverseMode.CAR)); + assertFalse(edge.canTraverse(TraverseMode.BICYCLE)); + assertTrue(edge.canTraverse(TraverseMode.WALK)); } /** From efc72cd8d27414adf3eb0baad0a88774926b684f Mon Sep 17 00:00:00 2001 From: Vesa Meskanen Date: Sun, 17 Sep 2023 11:18:27 +0300 Subject: [PATCH 30/95] Move comment to a place where the logic is --- .../java/org/opentripplanner/openstreetmap/model/OSMNode.java | 1 + .../org/opentripplanner/street/model/vertex/BarrierVertex.java | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMNode.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMNode.java index 2a007807b83..42a590d1e24 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMNode.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMNode.java @@ -67,6 +67,7 @@ public boolean isBarrier() { public StreetTraversalPermission overridePermissions(StreetTraversalPermission def) { StreetTraversalPermission permission = def; if (isBollard()) { + //According to OSM default permissions are access=no, foot=yes, bicycle=yes permission = permission.remove(StreetTraversalPermission.CAR); } return super.overridePermissions(permission); diff --git a/src/main/java/org/opentripplanner/street/model/vertex/BarrierVertex.java b/src/main/java/org/opentripplanner/street/model/vertex/BarrierVertex.java index 27200898c65..c923e8d14d4 100644 --- a/src/main/java/org/opentripplanner/street/model/vertex/BarrierVertex.java +++ b/src/main/java/org/opentripplanner/street/model/vertex/BarrierVertex.java @@ -16,7 +16,6 @@ */ public class BarrierVertex extends OsmVertex { - //According to OSM default permissions are access=no, foot=yes, bicycle=yes public static final StreetTraversalPermission defaultBarrierPermissions = StreetTraversalPermission.ALL; private StreetTraversalPermission barrierPermissions; From 9ae2f40fa5660b635e1dd83ad2d758f7ffded13c Mon Sep 17 00:00:00 2001 From: Vesa Meskanen Date: Mon, 18 Sep 2023 15:02:40 +0300 Subject: [PATCH 31/95] Repair dead end barriers, which break routing Osm contains nodes tagged as access=no and which represent an end point of a way. For example, a doorway leading to a locked gate is sometimes modeled like that. If routing request snaps to such end point, routing fails. Barrier end vertices do not add any kind of useful functionality but just cause trouble. --- .../graph_builder/module/osm/OsmModule.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 1f54820a912..20c1b5ebb93 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -30,6 +30,7 @@ import org.opentripplanner.street.model.StreetTraversalPermission; import org.opentripplanner.street.model.edge.StreetEdge; import org.opentripplanner.street.model.edge.StreetEdgeBuilder; +import org.opentripplanner.street.model.vertex.BarrierVertex; import org.opentripplanner.street.model.vertex.IntersectionVertex; import org.opentripplanner.street.model.vertex.Vertex; import org.slf4j.Logger; @@ -143,6 +144,7 @@ private void build() { buildBasicGraph(); buildWalkableAreas(!params.areaVisibility()); + validateBarriers(); if (params.staticParkAndRide()) { List areaGroups = groupAreas(osmdb.getParkAndRideAreas()); @@ -400,6 +402,36 @@ private void buildBasicGraph() { LOG.info(progress.completeMessage()); } + /* + * Barrier vertex at the end of a way does not make sense, because + * it creates discontunuity of routing in a single point. + * Remove all traversal limitations from such vertices. + */ + private void validateBarriers() { + List vertices = graph.getVerticesOfType(BarrierVertex.class); + + for (BarrierVertex bv : vertices) { + var edgeCount = bv.getDegreeOut() + bv.getDegreeIn(); + var needsFix = false; + if (edgeCount == 1) { + needsFix = true; + } else if (edgeCount == 2) { + var out = bv.getOutgoing(); + var in = bv.getIncoming(); + if ( + out.isEmpty() || + in.isEmpty() || + out.iterator().next().getToVertex() == in.iterator().next().getFromVertex() + ) { + needsFix = true; + } + } + if (needsFix) { + bv.setBarrierPermissions(StreetTraversalPermission.ALL); + } + } + } + private void setWayName(OSMWithTags way) { if (!way.hasTag("name")) { I18NString creativeName = way.getOsmProvider().getWayPropertySet().getCreativeNameForWay(way); From c70b80cf8b7f339bfb1341b415937fb470fed449 Mon Sep 17 00:00:00 2001 From: Vesa Meskanen Date: Tue, 19 Sep 2023 15:30:50 +0300 Subject: [PATCH 32/95] Add test for barrier vertex pruning --- .../module/osm/OsmModuleTest.java | 35 ++++++++++++++++++ .../module/osm/accessno-at-end.pbf | Bin 0 -> 349 bytes 2 files changed, 35 insertions(+) create mode 100644 src/test/resources/org/opentripplanner/graph_builder/module/osm/accessno-at-end.pbf diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java index c0c7a68cc37..14511258ae3 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java @@ -303,6 +303,41 @@ void createArtificalEntrancesToUnlikedParkingLots() { }); } + /** + * Test that a barrier vertex created when street ends to an access restriction + * will not prevent routing to that vertex + */ + @Test + private void testBarrierAtEnd() { + var deduplicator = new Deduplicator(); + var graph = new Graph(deduplicator); + + File file = new File( + URLDecoder.decode( + getClass().getResource("noaccess-at-end.pbf").getFile(), + StandardCharsets.UTF_8 + ) + ); + OsmProvider provider = new OsmProvider(file, false); + OsmModule loader = OsmModule.of(provider, graph).build(); + loader.buildGraph(); + + RouteRequest request = new RouteRequest(); + + // Route along a simple 3 vertex highway which ends to a 'access=none' node + Vertex start = graph.getVertex(VertexLabel.osm(1)); + Vertex end = graph.getVertex(VertexLabel.osm(3)); + + GraphPathFinder graphPathFinder = new GraphPathFinder(null); + List> pathList = graphPathFinder.graphPathFinderEntryPoint( + request, + Set.of(start), + Set.of(end) + ); + assertNotNull(pathList); + assertFalse(pathList.isEmpty()); + } + @Nonnull private Graph buildParkingLots() { var graph = new Graph(); diff --git a/src/test/resources/org/opentripplanner/graph_builder/module/osm/accessno-at-end.pbf b/src/test/resources/org/opentripplanner/graph_builder/module/osm/accessno-at-end.pbf new file mode 100644 index 0000000000000000000000000000000000000000..03b93ab30d20381d3e30e4bd371e2ee653ef0ab0 GIT binary patch literal 349 zcmZQzVBq88^bhv+NKH&hEs~hVDA2(uHG#2W&f`fBGj{w>zn16L*d~5I?S0ymjEG%d z9yzA9wyk^noX<%6<4^JDZ0hMto~gZRlYB8#vvB8W!dQei+R3Oz_-aN2g{ z$&)LOlJu7CsZp#}jMht2Q&ZFR)Jt=7b5l%HV`I}yV`F1e6X&J6fdC7%q(oo%%Hhm;0&rowgSddmLQ8B0xkO1bLLCWrz2i^*Cs8x nv?oU~Q`3`AOwmnE(=AO^kI#+KjgyU)jn_>b Date: Thu, 21 Sep 2023 19:08:20 +0000 Subject: [PATCH 33/95] Update dependency com.google.cloud.tools:jib-maven-plugin to v3.4.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 66e8f9dfedb..c4836db4c38 100644 --- a/pom.xml +++ b/pom.xml @@ -445,7 +445,7 @@ com.google.cloud.tools jib-maven-plugin - 3.3.2 + 3.4.0 org.opentripplanner.standalone.OTPMain From 005b780ef917b59144daa3daa2c1ebdea75ea7e3 Mon Sep 17 00:00:00 2001 From: Vesa Meskanen Date: Fri, 22 Sep 2023 11:45:58 +0300 Subject: [PATCH 34/95] Move barrier vertex specific logic to the class itself --- .../graph_builder/module/osm/OsmModule.java | 22 +------------------ .../street/model/vertex/BarrierVertex.java | 21 ++++++++++++++++++ 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 20c1b5ebb93..54797bc1e92 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -409,27 +409,7 @@ private void buildBasicGraph() { */ private void validateBarriers() { List vertices = graph.getVerticesOfType(BarrierVertex.class); - - for (BarrierVertex bv : vertices) { - var edgeCount = bv.getDegreeOut() + bv.getDegreeIn(); - var needsFix = false; - if (edgeCount == 1) { - needsFix = true; - } else if (edgeCount == 2) { - var out = bv.getOutgoing(); - var in = bv.getIncoming(); - if ( - out.isEmpty() || - in.isEmpty() || - out.iterator().next().getToVertex() == in.iterator().next().getFromVertex() - ) { - needsFix = true; - } - } - if (needsFix) { - bv.setBarrierPermissions(StreetTraversalPermission.ALL); - } - } + vertices.forEach(bv -> bv.makeBarrierAtEndReachable()); } private void setWayName(OSMWithTags way) { diff --git a/src/main/java/org/opentripplanner/street/model/vertex/BarrierVertex.java b/src/main/java/org/opentripplanner/street/model/vertex/BarrierVertex.java index c923e8d14d4..0ee155f800b 100644 --- a/src/main/java/org/opentripplanner/street/model/vertex/BarrierVertex.java +++ b/src/main/java/org/opentripplanner/street/model/vertex/BarrierVertex.java @@ -32,4 +32,25 @@ public StreetTraversalPermission getBarrierPermissions() { public void setBarrierPermissions(StreetTraversalPermission barrierPermissions) { this.barrierPermissions = barrierPermissions; } + + public void makeBarrierAtEndReachable() { + var edgeCount = this.getDegreeOut() + this.getDegreeIn(); + var needsFix = false; + if (edgeCount == 1) { + needsFix = true; + } else if (edgeCount == 2) { + var out = this.getOutgoing(); + var in = this.getIncoming(); + if ( + out.isEmpty() || + in.isEmpty() || + out.iterator().next().getToVertex() == in.iterator().next().getFromVertex() + ) { + needsFix = true; + } + } + if (needsFix) { + this.barrierPermissions = StreetTraversalPermission.ALL; + } + } } From a14a883b05c30d8f220a5be07722d70d96dd4929 Mon Sep 17 00:00:00 2001 From: Vesa Meskanen Date: Fri, 22 Sep 2023 13:14:22 +0300 Subject: [PATCH 35/95] Improve docs --- .../graph_builder/module/osm/OsmModule.java | 5 ----- .../opentripplanner/street/model/vertex/BarrierVertex.java | 7 +++++++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 54797bc1e92..9cc15d191fc 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -402,11 +402,6 @@ private void buildBasicGraph() { LOG.info(progress.completeMessage()); } - /* - * Barrier vertex at the end of a way does not make sense, because - * it creates discontunuity of routing in a single point. - * Remove all traversal limitations from such vertices. - */ private void validateBarriers() { List vertices = graph.getVerticesOfType(BarrierVertex.class); vertices.forEach(bv -> bv.makeBarrierAtEndReachable()); diff --git a/src/main/java/org/opentripplanner/street/model/vertex/BarrierVertex.java b/src/main/java/org/opentripplanner/street/model/vertex/BarrierVertex.java index 0ee155f800b..9132bc13291 100644 --- a/src/main/java/org/opentripplanner/street/model/vertex/BarrierVertex.java +++ b/src/main/java/org/opentripplanner/street/model/vertex/BarrierVertex.java @@ -33,6 +33,13 @@ public void setBarrierPermissions(StreetTraversalPermission barrierPermissions) this.barrierPermissions = barrierPermissions; } + /* + * Barrier vertex at the end of a way does not make sense, because + * it creates discontinuity of routing in a single point. + * This method examines if traversal limitations can be removed. + * The logic examines edges referring to the vertex, so it should be + * applied only after the vertex has been linked to the graph. + */ public void makeBarrierAtEndReachable() { var edgeCount = this.getDegreeOut() + this.getDegreeIn(); var needsFix = false; From 05ec1f3673325885dc56128f0a4945aecf7794e8 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Fri, 22 Sep 2023 14:29:20 +0200 Subject: [PATCH 36/95] Use bulk operation on collections --- .../graph_builder/module/islandpruning/PruneIslands.java | 4 +--- .../java/org/opentripplanner/astar/model/BinHeapTest.java | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/islandpruning/PruneIslands.java b/src/main/java/org/opentripplanner/graph_builder/module/islandpruning/PruneIslands.java index 6f35bc59ab6..d3024a144f5 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/islandpruning/PruneIslands.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/islandpruning/PruneIslands.java @@ -119,9 +119,7 @@ public void buildGraph() { areas.add(ae.getArea()); } for (AreaEdgeList a : areas) { - for (Vertex v : a.visibilityVertices()) { - visibilityVertices.add(v); - } + visibilityVertices.addAll(a.visibilityVertices()); } int removed = 0; diff --git a/src/test/java/org/opentripplanner/astar/model/BinHeapTest.java b/src/test/java/org/opentripplanner/astar/model/BinHeapTest.java index f050928ba92..1205179d28b 100644 --- a/src/test/java/org/opentripplanner/astar/model/BinHeapTest.java +++ b/src/test/java/org/opentripplanner/astar/model/BinHeapTest.java @@ -73,9 +73,7 @@ public void testCompareHeaps() throws InterruptedException { // First determine the expected results using a plain old PriorityQueue expected = new ArrayList<>(N); PriorityQueue q = new PriorityQueue<>(N); - for (Integer j : input) { - q.add(j); - } + q.addAll(input); while (!q.isEmpty()) { expected.add(q.remove()); } From 431f8e07bea85d7d98b4a8bf97ec5a1884a0d6c6 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Fri, 22 Sep 2023 14:31:04 +0200 Subject: [PATCH 37/95] Fix size of List.toArray() parameter --- .../org/opentripplanner/framework/geometry/GeometryUtils.java | 2 +- .../opentripplanner/graph_builder/module/osm/AreaGroup.java | 4 +--- .../org/opentripplanner/graph_builder/module/osm/Ring.java | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/opentripplanner/framework/geometry/GeometryUtils.java b/src/main/java/org/opentripplanner/framework/geometry/GeometryUtils.java index b440ac32979..27725f5ddf4 100644 --- a/src/main/java/org/opentripplanner/framework/geometry/GeometryUtils.java +++ b/src/main/java/org/opentripplanner/framework/geometry/GeometryUtils.java @@ -249,7 +249,7 @@ public static Geometry convertGeoJsonToJtsGeometry(GeoJsonObject geoJsonGeom) int i = 0; for (List geoJsonPath : geoJsonMultiLineString.getCoordinates()) { org.geojson.LineString geoJsonLineString = new org.geojson.LineString( - geoJsonPath.toArray(new LngLatAlt[geoJsonPath.size()]) + geoJsonPath.toArray(new LngLatAlt[0]) ); jtsLineStrings[i++] = (LineString) convertGeoJsonToJtsGeometry(geoJsonLineString); } diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/AreaGroup.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/AreaGroup.java index fd3f697a8e1..a7bcc9ccb94 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/AreaGroup.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/AreaGroup.java @@ -66,9 +66,7 @@ public AreaGroup(Collection areas) { } } GeometryFactory geometryFactory = GeometryUtils.getGeometryFactory(); - Geometry allPolygons = geometryFactory.createMultiPolygon( - allRings.toArray(new Polygon[allRings.size()]) - ); + Geometry allPolygons = geometryFactory.createMultiPolygon(allRings.toArray(new Polygon[0])); this.union = allPolygons.union(); if (this.union instanceof GeometryCollection coll) { diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/Ring.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/Ring.java index 1c126809501..9804c2d646f 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/Ring.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/Ring.java @@ -115,7 +115,7 @@ private Polygon calculateJtsPolygon() { lrholelist.add(ring); } } - LinearRing[] lrholes = lrholelist.toArray(new LinearRing[lrholelist.size()]); + LinearRing[] lrholes = lrholelist.toArray(new LinearRing[0]); return factory.createPolygon(shell, lrholes); } From 754c51ddee50f8aa76527d2467bac0cca61c6522 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Fri, 22 Sep 2023 14:49:53 +0200 Subject: [PATCH 38/95] Use String.isEmpty() to test empty String --- .../ext/vehicleparking/hslpark/HslFacilitiesDownloader.java | 2 +- .../hslpark/HslHubToVehicleParkingGroupMapper.java | 2 +- .../ext/vehicleparking/hslpark/HslHubsDownloader.java | 2 +- .../vehicleparking/hslpark/HslParkToVehicleParkingMapper.java | 2 +- .../opentripplanner/framework/io/JsonDataListDownloader.java | 2 +- .../graph_builder/module/geometry/GeometryProcessor.java | 2 +- .../inspector/raster/NoThruTrafficEdgeRenderer.java | 2 +- .../openstreetmap/wayproperty/WayPropertySet.java | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslFacilitiesDownloader.java b/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslFacilitiesDownloader.java index 55a325d7431..31664170a04 100644 --- a/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslFacilitiesDownloader.java +++ b/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslFacilitiesDownloader.java @@ -86,7 +86,7 @@ private List parseJSON( JsonNode rootNode = mapper.readTree(facilitiesString); - if (!jsonParsePath.equals("")) { + if (!jsonParsePath.isEmpty()) { String delimiter = "/"; String[] parseElement = jsonParsePath.split(delimiter); for (String s : parseElement) { diff --git a/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslHubToVehicleParkingGroupMapper.java b/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslHubToVehicleParkingGroupMapper.java index 959523247e4..1ef0ef3597b 100644 --- a/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslHubToVehicleParkingGroupMapper.java +++ b/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslHubToVehicleParkingGroupMapper.java @@ -46,7 +46,7 @@ public Map parseHub(JsonNode jsonNode) { .fieldNames() .forEachRemaining(lang -> { String name = nameNode.path(lang).asText(); - if (!name.equals("")) { + if (!name.isEmpty()) { translations.put(lang, nameNode.path(lang).asText()); } }); diff --git a/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslHubsDownloader.java b/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslHubsDownloader.java index 06a275de9ae..c5712e2067e 100644 --- a/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslHubsDownloader.java +++ b/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslHubsDownloader.java @@ -79,7 +79,7 @@ private Map parseJSON(InputStream dataStream) JsonNode rootNode = mapper.readTree(hubsString); - if (!jsonParsePath.equals("")) { + if (!jsonParsePath.isEmpty()) { String delimiter = "/"; String[] parseElement = jsonParsePath.split(delimiter); for (String s : parseElement) { diff --git a/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslParkToVehicleParkingMapper.java b/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslParkToVehicleParkingMapper.java index 0e41f0ed09d..1f69458dd1f 100644 --- a/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslParkToVehicleParkingMapper.java +++ b/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslParkToVehicleParkingMapper.java @@ -84,7 +84,7 @@ public VehicleParking parsePark( .fieldNames() .forEachRemaining(lang -> { String name = nameNode.path(lang).asText(); - if (!name.equals("")) { + if (!name.isEmpty()) { translations.put(lang, nameNode.path(lang).asText()); } }); diff --git a/src/main/java/org/opentripplanner/framework/io/JsonDataListDownloader.java b/src/main/java/org/opentripplanner/framework/io/JsonDataListDownloader.java index 867c9de3e2e..13857cd005a 100644 --- a/src/main/java/org/opentripplanner/framework/io/JsonDataListDownloader.java +++ b/src/main/java/org/opentripplanner/framework/io/JsonDataListDownloader.java @@ -87,7 +87,7 @@ private List parseJSON(InputStream dataStream) throws IllegalArgumentExceptio ObjectMapper mapper = new ObjectMapper(); JsonNode rootNode = mapper.readTree(rentalString); - if (!jsonParsePath.equals("")) { + if (!jsonParsePath.isEmpty()) { String delimiter = "/"; String[] parseElement = jsonParsePath.split(delimiter); for (String s : parseElement) { diff --git a/src/main/java/org/opentripplanner/graph_builder/module/geometry/GeometryProcessor.java b/src/main/java/org/opentripplanner/graph_builder/module/geometry/GeometryProcessor.java index 360ad9f5865..17b0f9d5367 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/geometry/GeometryProcessor.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/geometry/GeometryProcessor.java @@ -80,7 +80,7 @@ public List createHopGeometries(Trip trip) { if ( trip.getShapeId() == null || trip.getShapeId().getId() == null || - trip.getShapeId().getId().equals("") + trip.getShapeId().getId().isEmpty() ) { return null; } diff --git a/src/main/java/org/opentripplanner/inspector/raster/NoThruTrafficEdgeRenderer.java b/src/main/java/org/opentripplanner/inspector/raster/NoThruTrafficEdgeRenderer.java index 206cfbd3f13..6d5e635d59c 100644 --- a/src/main/java/org/opentripplanner/inspector/raster/NoThruTrafficEdgeRenderer.java +++ b/src/main/java/org/opentripplanner/inspector/raster/NoThruTrafficEdgeRenderer.java @@ -47,7 +47,7 @@ public Optional renderEdge(Edge e) { label += " car"; colorIndex += 4; } - if (!label.equals("")) { + if (!label.isEmpty()) { label = "No" + label + " thru traffic"; } diff --git a/src/main/java/org/opentripplanner/openstreetmap/wayproperty/WayPropertySet.java b/src/main/java/org/opentripplanner/openstreetmap/wayproperty/WayPropertySet.java index 06d5f895777..376614b1093 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/wayproperty/WayPropertySet.java +++ b/src/main/java/org/opentripplanner/openstreetmap/wayproperty/WayPropertySet.java @@ -336,7 +336,7 @@ public Float getMetersSecondFromSpeed(String speed) { } String units = m.group(2); - if (units == null || units.equals("")) units = "kmh"; + if (units == null || units.isEmpty()) units = "kmh"; // we'll be doing quite a few string comparisons here units = units.intern(); From 820b3d74361d9ffadb555a2120c1a2c2adfe390e Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Fri, 22 Sep 2023 15:03:39 +0200 Subject: [PATCH 39/95] Replace wrapper with primitive --- .../java/org/opentripplanner/openstreetmap/model/OSMLevel.java | 2 +- .../standalone/config/CommandLineParameters.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMLevel.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMLevel.java index c8c87f383d0..9aef9e4bd01 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMLevel.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMLevel.java @@ -69,7 +69,7 @@ public static OSMLevel fromString( /* get short and long level names by splitting on = character */ String shortName = ""; String longName = ""; - Integer indexEquals = spec.indexOf('='); + int indexEquals = spec.indexOf('='); if (indexEquals >= 1) { shortName = spec.substring(0, indexEquals); longName = spec.substring(indexEquals + 1); diff --git a/src/main/java/org/opentripplanner/standalone/config/CommandLineParameters.java b/src/main/java/org/opentripplanner/standalone/config/CommandLineParameters.java index 6f85f9d8598..cf02d127c52 100644 --- a/src/main/java/org/opentripplanner/standalone/config/CommandLineParameters.java +++ b/src/main/java/org/opentripplanner/standalone/config/CommandLineParameters.java @@ -327,7 +327,7 @@ public static class PositiveInteger implements IParameterValidator { @Override public void validate(String name, String value) throws ParameterException { - Integer i = Integer.parseInt(value); + int i = Integer.parseInt(value); if (i <= 0) { String msg = String.format("%s must be a positive integer.", name); throw new ParameterException(msg); From ae46416a2bf3a422e8ae7681d4578016633c3d36 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Fri, 22 Sep 2023 15:32:59 +0200 Subject: [PATCH 40/95] Use short-circuit boolean operator --- .../org/opentripplanner/ext/siri/SiriTripPatternCache.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/siri/SiriTripPatternCache.java b/src/ext/java/org/opentripplanner/ext/siri/SiriTripPatternCache.java index 83b7d04c34f..8b33d71ab96 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/SiriTripPatternCache.java +++ b/src/ext/java/org/opentripplanner/ext/siri/SiriTripPatternCache.java @@ -190,7 +190,7 @@ public boolean equals(Object thatObject) { return false; } StopPatternServiceDateKey that = (StopPatternServiceDateKey) thatObject; - return (this.stopPattern.equals(that.stopPattern) & this.serviceDate.equals(that.serviceDate)); + return (this.stopPattern.equals(that.stopPattern) && this.serviceDate.equals(that.serviceDate)); } } @@ -215,6 +215,6 @@ public boolean equals(Object thatObject) { return false; } TripServiceDateKey that = (TripServiceDateKey) thatObject; - return (this.trip.equals(that.trip) & this.serviceDate.equals(that.serviceDate)); + return (this.trip.equals(that.trip) && this.serviceDate.equals(that.serviceDate)); } } From acda2b86189f2079d812b54fa7a2665d402b68d0 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Mon, 25 Sep 2023 10:17:19 +0200 Subject: [PATCH 41/95] Apply review suggestions --- .../internal/DefaultVehicleRentalService.java | 18 +++++++++--------- .../DefaultVehicleRentalServiceTest.java | 18 ++++++++++-------- .../TestFreeFloatingRentalVehicleBuilder.java | 19 +++++++++++++++++-- .../TestVehicleRentalStationBuilder.java | 19 +++++++++++++++++-- 4 files changed, 53 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/opentripplanner/service/vehiclerental/internal/DefaultVehicleRentalService.java b/src/main/java/org/opentripplanner/service/vehiclerental/internal/DefaultVehicleRentalService.java index ab8bc3d780c..996f4cbcc9d 100644 --- a/src/main/java/org/opentripplanner/service/vehiclerental/internal/DefaultVehicleRentalService.java +++ b/src/main/java/org/opentripplanner/service/vehiclerental/internal/DefaultVehicleRentalService.java @@ -6,6 +6,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Stream; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Envelope; import org.opentripplanner.service.vehiclerental.VehicleRentalRepository; @@ -54,12 +55,7 @@ public VehicleRentalVehicle getVehicleRentalVehicle(FeedScopedId id) { @Override public List getVehicleRentalStations() { - return rentalPlaces - .values() - .stream() - .filter(VehicleRentalStation.class::isInstance) - .map(VehicleRentalStation.class::cast) - .toList(); + return getVehicleRentalStationsAsStream().toList(); } @Override @@ -111,12 +107,16 @@ public List getVehicleRentalStationForEnvelope( new Coordinate(maxLon, maxLat) ); + return getVehicleRentalStationsAsStream() + .filter(b -> envelope.contains(new Coordinate(b.getLongitude(), b.getLatitude()))) + .toList(); + } + + private Stream getVehicleRentalStationsAsStream() { return rentalPlaces .values() .stream() .filter(VehicleRentalStation.class::isInstance) - .filter(b -> envelope.contains(new Coordinate(b.getLongitude(), b.getLatitude()))) - .map(VehicleRentalStation.class::cast) - .toList(); + .map(VehicleRentalStation.class::cast); } } diff --git a/src/test/java/org/opentripplanner/service/vehiclerental/internal/DefaultVehicleRentalServiceTest.java b/src/test/java/org/opentripplanner/service/vehiclerental/internal/DefaultVehicleRentalServiceTest.java index 7f9d882fdeb..f193b65d986 100644 --- a/src/test/java/org/opentripplanner/service/vehiclerental/internal/DefaultVehicleRentalServiceTest.java +++ b/src/test/java/org/opentripplanner/service/vehiclerental/internal/DefaultVehicleRentalServiceTest.java @@ -4,6 +4,8 @@ import java.util.List; import org.junit.jupiter.api.Test; +import org.opentripplanner.service.vehiclerental.model.TestFreeFloatingRentalVehicleBuilder; +import org.opentripplanner.service.vehiclerental.model.TestVehicleRentalStationBuilder; import org.opentripplanner.service.vehiclerental.model.VehicleRentalStation; import org.opentripplanner.service.vehiclerental.model.VehicleRentalVehicle; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -14,16 +16,16 @@ class DefaultVehicleRentalServiceTest { void getVehicleRentalStationForEnvelopeShouldExcludeVehicleRentalVehicle() { DefaultVehicleRentalService defaultVehicleRentalService = new DefaultVehicleRentalService(); - VehicleRentalStation vehicleRentalStation = new VehicleRentalStation(); - vehicleRentalStation.id = new FeedScopedId("Feed1", "VehicleRentalStation1"); - vehicleRentalStation.latitude = 1; - vehicleRentalStation.longitude = 1; + VehicleRentalStation vehicleRentalStation = new TestVehicleRentalStationBuilder() + .withLatitude(1) + .withLongitude(1) + .build(); defaultVehicleRentalService.addVehicleRentalStation(vehicleRentalStation); - VehicleRentalVehicle vehicleRentalVehicle = new VehicleRentalVehicle(); - vehicleRentalVehicle.id = new FeedScopedId("Feed1", "VehicleRentalVehicle1"); - vehicleRentalVehicle.latitude = 2; - vehicleRentalVehicle.longitude = 2; + VehicleRentalVehicle vehicleRentalVehicle = new TestFreeFloatingRentalVehicleBuilder() + .withLatitude(2) + .withLongitude(2) + .build(); defaultVehicleRentalService.addVehicleRentalStation(vehicleRentalVehicle); List vehicleRentalStationForEnvelope = defaultVehicleRentalService.getVehicleRentalStationForEnvelope( diff --git a/src/test/java/org/opentripplanner/service/vehiclerental/model/TestFreeFloatingRentalVehicleBuilder.java b/src/test/java/org/opentripplanner/service/vehiclerental/model/TestFreeFloatingRentalVehicleBuilder.java index a6fe3da2f4a..4864fab5e43 100644 --- a/src/test/java/org/opentripplanner/service/vehiclerental/model/TestFreeFloatingRentalVehicleBuilder.java +++ b/src/test/java/org/opentripplanner/service/vehiclerental/model/TestFreeFloatingRentalVehicleBuilder.java @@ -8,6 +8,11 @@ public class TestFreeFloatingRentalVehicleBuilder { public static final String NETWORK_1 = "Network-1"; + public static final double DEFAULT_LATITUDE = 47.520; + public static final double DEFAULT_LONGITUDE = 19.01; + + private double latitude = DEFAULT_LATITUDE; + private double longitude = DEFAULT_LONGITUDE; private RentalVehicleType vehicleType = RentalVehicleType.getDefaultType(NETWORK_1); @@ -15,6 +20,16 @@ public static TestFreeFloatingRentalVehicleBuilder of() { return new TestFreeFloatingRentalVehicleBuilder(); } + public TestFreeFloatingRentalVehicleBuilder withLatitude(double latitude) { + this.latitude = latitude; + return this; + } + + public TestFreeFloatingRentalVehicleBuilder withLongitude(double longitude) { + this.longitude = longitude; + return this; + } + public TestFreeFloatingRentalVehicleBuilder withVehicleScooter() { return buildVehicleType(RentalFormFactor.SCOOTER); } @@ -45,8 +60,8 @@ public VehicleRentalVehicle build() { var stationName = "free-floating-" + vehicleType.formFactor.name().toLowerCase(); vehicle.id = new FeedScopedId(NETWORK_1, stationName); vehicle.name = new NonLocalizedString(stationName); - vehicle.latitude = 47.510; - vehicle.longitude = 18.99; + vehicle.latitude = latitude; + vehicle.longitude = longitude; vehicle.vehicleType = vehicleType; return vehicle; } diff --git a/src/test/java/org/opentripplanner/service/vehiclerental/model/TestVehicleRentalStationBuilder.java b/src/test/java/org/opentripplanner/service/vehiclerental/model/TestVehicleRentalStationBuilder.java index 6e3d302c219..4ed26d4869b 100644 --- a/src/test/java/org/opentripplanner/service/vehiclerental/model/TestVehicleRentalStationBuilder.java +++ b/src/test/java/org/opentripplanner/service/vehiclerental/model/TestVehicleRentalStationBuilder.java @@ -5,11 +5,16 @@ import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.street.model.RentalFormFactor; import org.opentripplanner.transit.model.framework.FeedScopedId; +import org.rutebanken.netex.model.LocationStructure; public class TestVehicleRentalStationBuilder { public static final String NETWORK_1 = "Network-1"; + public static final double DEFAULT_LATITUDE = 47.510; + public static final double DEFAULT_LONGITUDE = 18.99; + private double latitude = DEFAULT_LATITUDE; + private double longitude = DEFAULT_LONGITUDE; private int vehicles = 10; private int spaces = 10; private boolean overloadingAllowed = false; @@ -20,6 +25,16 @@ public static TestVehicleRentalStationBuilder of() { return new TestVehicleRentalStationBuilder(); } + public TestVehicleRentalStationBuilder withLatitude(double latitude) { + this.latitude = latitude; + return this; + } + + public TestVehicleRentalStationBuilder withLongitude(double longitude) { + this.longitude = longitude; + return this; + } + public TestVehicleRentalStationBuilder withVehicles(int vehicles) { this.vehicles = vehicles; return this; @@ -66,8 +81,8 @@ public VehicleRentalStation build() { var stationName = "FooStation"; station.id = new FeedScopedId(NETWORK_1, stationName); station.name = new NonLocalizedString(stationName); - station.latitude = 47.510; - station.longitude = 18.99; + station.latitude = latitude; + station.longitude = longitude; station.vehiclesAvailable = vehicles; station.spacesAvailable = spaces; station.vehicleTypesAvailable = Map.of(vehicleType, vehicles); From 8049fd3759d525523e476d4b0eec7be6323657a2 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 25 Sep 2023 10:52:53 +0200 Subject: [PATCH 42/95] Make ResourceLoader package-dependent --- .../ext/fares/impl/FaresIntegrationTest.java | 3 +- .../opentripplanner/ext/flex/FlexTest.java | 16 ++++---- .../opentripplanner/ConstantsForTests.java | 37 +++++++++--------- .../graph_builder/module/FakeGraph.java | 7 +++- .../graph_builder/module/GtfsModuleTest.java | 2 +- .../module/osm/OpenStreetMapParserTest.java | 2 +- .../module/osm/OsmModuleTest.java | 10 +++-- .../module/osm/PlatformLinkerTest.java | 2 +- .../module/osm/TriangleInequalityTest.java | 2 +- .../module/osm/UnroutableTest.java | 2 +- .../module/osm/WalkableAreaBuilderTest.java | 2 +- .../model/BicycleNetworkRelationsTest.java | 4 +- .../test/support/ResourceLoader.java | 27 ++++++++++--- .../GtfsRealtimeTripUpdateSourceTest.java | 3 +- .../VehiclePositionParsingTest.java | 2 +- .../graph_builder/module}/osm/map.osm.pbf | Bin 16 files changed, 72 insertions(+), 49 deletions(-) rename src/test/resources/{ => org/opentripplanner/graph/graph_builder/module}/osm/map.osm.pbf (100%) diff --git a/src/ext-test/java/org/opentripplanner/ext/fares/impl/FaresIntegrationTest.java b/src/ext-test/java/org/opentripplanner/ext/fares/impl/FaresIntegrationTest.java index 64c78e64f32..0f6430e32ac 100644 --- a/src/ext-test/java/org/opentripplanner/ext/fares/impl/FaresIntegrationTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/fares/impl/FaresIntegrationTest.java @@ -22,6 +22,7 @@ import org.opentripplanner.routing.core.FareType; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.standalone.api.OtpServerRequestContext; +import org.opentripplanner.test.support.ResourceLoader; import org.opentripplanner.transit.model.basic.Money; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.service.TransitModel; @@ -107,7 +108,7 @@ public void testPortland() { @Test public void testFareComponent() { - TestOtpModel model = ConstantsForTests.buildGtfsGraph(ConstantsForTests.FARE_COMPONENT_GTFS); + TestOtpModel model = ConstantsForTests.buildGtfsGraph(ResourceLoader.of(this).file("/gtfs/farecomponents.gtfs.zip")); Graph graph = model.graph(); TransitModel transitModel = model.transitModel(); String feedId = transitModel.getFeedIds().iterator().next(); diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/FlexTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/FlexTest.java index e8cd77d3ff6..d9ab0d91ca0 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/FlexTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/FlexTest.java @@ -1,7 +1,6 @@ package org.opentripplanner.ext.flex; import static graphql.Assert.assertTrue; -import static org.opentripplanner.test.support.ResourceLoader.file; import gnu.trove.set.hash.TIntHashSet; import java.io.File; @@ -16,20 +15,23 @@ import org.opentripplanner.gtfs.graphbuilder.GtfsModule; import org.opentripplanner.model.calendar.ServiceDateInterval; import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.test.support.ResourceLoader; import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.service.StopModel; import org.opentripplanner.transit.service.TransitModel; public abstract class FlexTest { - protected static final File ASPEN_GTFS = file("/flex/aspen-flex-on-demand.gtfs.zip"); - protected static final File COBB_FLEX_GTFS = file( + private static final ResourceLoader RES = ResourceLoader.of(FlexTest.class); + + protected static final File ASPEN_GTFS = RES.file("/flex/aspen-flex-on-demand.gtfs.zip"); + protected static final File COBB_FLEX_GTFS = RES.file( "/flex/cobblinc-scheduled-deviated-flex.gtfs.zip" ); - protected static final File COBB_BUS_30_GTFS = file("/flex/cobblinc-bus-30-only.gtfs.zip"); - protected static final File MARTA_BUS_856_GTFS = file("/flex/marta-bus-856-only.gtfs.zip"); - protected static final File LINCOLN_COUNTY_GBFS = file("/flex/lincoln-county-flex.gtfs.zip"); - protected static final File COBB_OSM = file("/flex/cobb-county.filtered.osm.pbf"); + protected static final File COBB_BUS_30_GTFS = RES.file("/flex/cobblinc-bus-30-only.gtfs.zip"); + protected static final File MARTA_BUS_856_GTFS = RES.file("/flex/marta-bus-856-only.gtfs.zip"); + protected static final File LINCOLN_COUNTY_GBFS = RES.file("/flex/lincoln-county-flex.gtfs.zip"); + protected static final File COBB_OSM = RES.file("/flex/cobb-county.filtered.osm.pbf"); protected static final DirectFlexPathCalculator calculator = new DirectFlexPathCalculator(); protected static final LocalDate serviceDate = LocalDate.of(2021, 4, 11); diff --git a/src/test/java/org/opentripplanner/ConstantsForTests.java b/src/test/java/org/opentripplanner/ConstantsForTests.java index 463ea63f365..9debf270272 100644 --- a/src/test/java/org/opentripplanner/ConstantsForTests.java +++ b/src/test/java/org/opentripplanner/ConstantsForTests.java @@ -1,7 +1,5 @@ package org.opentripplanner; -import static org.opentripplanner.test.support.ResourceLoader.file; -import static org.opentripplanner.test.support.ResourceLoader.osmFile; import com.csvreader.CsvReader; import java.io.File; @@ -42,6 +40,7 @@ import org.opentripplanner.standalone.config.OtpConfigLoader; import org.opentripplanner.street.search.TraverseMode; import org.opentripplanner.street.search.TraverseModeSet; +import org.opentripplanner.test.support.ResourceLoader; import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.service.StopModel; @@ -49,11 +48,13 @@ public class ConstantsForTests { - public static final File CALTRAIN_GTFS = file("/gtfs/caltrain_gtfs.zip"); + private final static ResourceLoader RES = ResourceLoader.of(ConstantsForTests.class); - public static final File PORTLAND_GTFS = file("/portland/portland.gtfs.zip"); + public static final File CALTRAIN_GTFS = RES.file("/gtfs/caltrain_gtfs.zip"); - public static final File PORTLAND_CENTRAL_OSM = file( + public static final File PORTLAND_GTFS = RES.file("/portland/portland.gtfs.zip"); + + public static final File PORTLAND_CENTRAL_OSM = RES.file( "/portland/portland-central-filtered.osm.pbf" ); @@ -65,13 +66,11 @@ public class ConstantsForTests { private static final String OSLO_EAST_OSM = "src/test/resources/osm/oslo-east-filtered.osm.pbf"; - public static final File KCM_GTFS = file("/gtfs/kcm_gtfs.zip"); - - public static final File SIMPLE_GTFS = file("/gtfs/simple/"); + public static final File KCM_GTFS = RES.file("/gtfs/kcm_gtfs.zip"); - public static final File FARE_COMPONENT_GTFS = file("/gtfs/farecomponents.gtfs.zip"); + public static final File SIMPLE_GTFS = RES.file("/gtfs/simple/"); - public static final File SHAPE_DIST_GTFS = file("/gtfs/shape_dist_traveled/"); + public static final File SHAPE_DIST_GTFS = RES.file("/gtfs/shape_dist_traveled/"); private static final String NETEX_NORDIC_DIR = "src/test/resources/netex/nordic"; @@ -79,19 +78,19 @@ public class ConstantsForTests { private static final String NETEX_EPIP_DIR = "src/test/resources/netex/epip/"; private static final String NETEX_EPIP_DATA_DIR = NETEX_EPIP_DIR + "netex_epip_minimal/"; /* Stuttgart area, Germany */ - public static final File DEUFRINGEN_OSM = osmFile("deufringen-minimal.osm.pbf"); - public static final File BOEBLINGEN_OSM = osmFile("boeblingen-minimal.osm.pbf"); - public static final File VVS_BUS_764_ONLY = file("/gtfs/vvs-bus-764-only.gtfs.zip"); - public static final File VVS_BUS_751_ONLY = file("/gtfs/vvs-bus-751-only.gtfs.zip"); - public static final File HERRENBERG_HINDENBURG_STR_UNDER_CONSTRUCTION_OSM = osmFile( + public static final File DEUFRINGEN_OSM = RES.osmFile("deufringen-minimal.osm.pbf"); + public static final File BOEBLINGEN_OSM = RES.osmFile("boeblingen-minimal.osm.pbf"); + public static final File VVS_BUS_764_ONLY = RES.file("/gtfs/vvs-bus-764-only.gtfs.zip"); + public static final File VVS_BUS_751_ONLY = RES.file("/gtfs/vvs-bus-751-only.gtfs.zip"); + public static final File HERRENBERG_HINDENBURG_STR_UNDER_CONSTRUCTION_OSM = RES.osmFile( "herrenberg-hindenburgstr-under-construction.osm.pbf" ); - public static final File HERRENBERG_BARRIER_GATES_OSM = osmFile( + public static final File HERRENBERG_BARRIER_GATES_OSM = RES.osmFile( "herrenberg-barrier-gates.osm.pbf" ); - public static final File HERRENBERG_OSM = osmFile("herrenberg-minimal.osm.pbf"); - public static final File ISLAND_PRUNE_OSM = osmFile("herrenberg-island-prune-nothru.osm.pbf"); - public static final File ADAPTIVE_PRUNE_OSM = osmFile("isoiiluoto.pbf"); + public static final File HERRENBERG_OSM = RES.osmFile("herrenberg-minimal.osm.pbf"); + public static final File ISLAND_PRUNE_OSM = RES.osmFile("herrenberg-island-prune-nothru.osm.pbf"); + public static final File ADAPTIVE_PRUNE_OSM = RES.osmFile("isoiiluoto.pbf"); /* filenames encoded with cp437 and utf8 */ public static final String UMLAUT_CP437_ZIP = "src/test/resources/umlaut-cp437.zip"; diff --git a/src/test/java/org/opentripplanner/graph_builder/module/FakeGraph.java b/src/test/java/org/opentripplanner/graph_builder/module/FakeGraph.java index 3e6d9c5358e..6dc14e583e1 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/FakeGraph.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/FakeGraph.java @@ -4,6 +4,7 @@ import java.util.List; import org.opentripplanner.TestOtpModel; import org.opentripplanner.graph_builder.module.osm.OsmModule; +import org.opentripplanner.graph_builder.module.osm.OsmModuleTest; import org.opentripplanner.gtfs.graphbuilder.GtfsBundle; import org.opentripplanner.gtfs.graphbuilder.GtfsModule; import org.opentripplanner.model.calendar.ServiceDateInterval; @@ -29,6 +30,8 @@ */ public class FakeGraph { + private final static ResourceLoader RES = ResourceLoader.of(OsmModuleTest.class); + /** Build a graph in Columbus, OH with no transit */ public static TestOtpModel buildGraphNoTransit() { var deduplicator = new Deduplicator(); @@ -36,7 +39,7 @@ public static TestOtpModel buildGraphNoTransit() { var gg = new Graph(deduplicator); var transitModel = new TransitModel(stopModel, deduplicator); - File file = ResourceLoader.osmFile("columbus.osm.pbf"); + File file = RES.file("columbus.osm.pbf"); OsmProvider provider = new OsmProvider(file, false); OsmModule osmModule = OsmModule.of(provider, gg).build(); @@ -49,7 +52,7 @@ public static TestOtpModel buildGraphNoTransit() { * This introduces a 1MB test resource but is only used by TestIntermediatePlaces. */ public static void addPerpendicularRoutes(Graph graph, TransitModel transitModel) { - var input = List.of(new GtfsBundle(ResourceLoader.file("addPerpendicularRoutes.gtfs.zip"))); + var input = List.of(new GtfsBundle(RES.file("addPerpendicularRoutes.gtfs.zip"))); new GtfsModule(input, transitModel, graph, ServiceDateInterval.unbounded()).buildGraph(); } diff --git a/src/test/java/org/opentripplanner/graph_builder/module/GtfsModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/GtfsModuleTest.java index d058993a527..c628a14ad43 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/GtfsModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/GtfsModuleTest.java @@ -67,7 +67,7 @@ record TestModels(Graph graph, TransitModel transitModel) {} class Interlining { static GtfsBundle bundle(String feedId) { - var b = new GtfsBundle(ResourceLoader.file("/gtfs/interlining")); + var b = new GtfsBundle(ResourceLoader.of(GtfsModuleTest.class).file("/gtfs/interlining")); b.setFeedId(new GtfsFeedId.Builder().id(feedId).build()); return b; } diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/OpenStreetMapParserTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/OpenStreetMapParserTest.java index 2fcac407d95..b0073475699 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/OpenStreetMapParserTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/OpenStreetMapParserTest.java @@ -16,7 +16,7 @@ public class OpenStreetMapParserTest { @Test public void testBinaryParser() { - File osmFile = ResourceLoader.osmFile("map.osm.pbf"); + File osmFile = ResourceLoader.of(this).file("map.osm.pbf"); OsmProvider pr = new OsmProvider(osmFile, true); OsmDatabase osmdb = new OsmDatabase(DataImportIssueStore.NOOP); diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java index 6536f5cb7a5..ac762c66ff3 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java @@ -45,12 +45,14 @@ public class OsmModuleTest { + private final static ResourceLoader RES = ResourceLoader.of(OsmModuleTest.class); + @Test public void testGraphBuilder() { var deduplicator = new Deduplicator(); var gg = new Graph(deduplicator); - File file = ResourceLoader.osmFile("map.osm.pbf"); + File file = RES.file("map.osm.pbf"); OsmProvider provider = new OsmProvider(file, true); @@ -108,7 +110,7 @@ public void testBuildGraphDetailed() { var deduplicator = new Deduplicator(); var gg = new Graph(deduplicator); - File file = ResourceLoader.osmFile("NYC_small.osm.pbf"); + File file = RES.file("NYC_small.osm.pbf"); OsmProvider provider = new OsmProvider(file, true); OsmModule osmModule = OsmModule.of(provider, gg).withAreaVisibility(true).build(); @@ -300,7 +302,7 @@ private Graph buildParkingLots() { var graph = new Graph(); var providers = Stream .of("B+R.osm.pbf", "P+R.osm.pbf") - .map(ResourceLoader::osmFile) + .map(RES::file) .map(f -> new OsmProvider(f, false)) .toList(); var module = OsmModule @@ -325,7 +327,7 @@ private void testBuildingAreas(boolean skipVisibility) { var deduplicator = new Deduplicator(); var graph = new Graph(deduplicator); - File file = ResourceLoader.osmFile("usf_area.osm.pbf"); + File file = RES.file("usf_area.osm.pbf"); OsmProvider provider = new OsmProvider(file, false); OsmModule loader = OsmModule.of(provider, graph).withAreaVisibility(!skipVisibility).build(); diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/PlatformLinkerTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/PlatformLinkerTest.java index fc64c93e3e2..9d3ea5a1d81 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/PlatformLinkerTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/PlatformLinkerTest.java @@ -25,7 +25,7 @@ public void testLinkEntriesToPlatforms() { var deduplicator = new Deduplicator(); var gg = new Graph(deduplicator); - File file = ResourceLoader.osmFile("skoyen.osm.pbf"); + File file = ResourceLoader.of(this).file("skoyen.osm.pbf"); OsmProvider provider = new OsmProvider(file, false); diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/TriangleInequalityTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/TriangleInequalityTest.java index 3f665faa245..4cdada95e4b 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/TriangleInequalityTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/TriangleInequalityTest.java @@ -48,7 +48,7 @@ public class TriangleInequalityTest { public static void onlyOnce() { graph = new Graph(new Deduplicator()); - File file = ResourceLoader.osmFile("NYC_small.osm.pbf"); + File file = ResourceLoader.of(TriangleInequalityTest.class).file("NYC_small.osm.pbf"); OsmProvider provider = new OsmProvider(file, true); OsmModule osmModule = OsmModule.of(provider, graph).withAreaVisibility(true).build(); osmModule.buildGraph(); diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/UnroutableTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/UnroutableTest.java index 2ccdad60b32..49729bbace6 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/UnroutableTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/UnroutableTest.java @@ -35,7 +35,7 @@ public void setUp() throws Exception { var deduplicator = new Deduplicator(); graph = new Graph(deduplicator); - var osmDataFile = ResourceLoader.osmFile("bridge_construction.osm.pbf"); + var osmDataFile = ResourceLoader.of(UnroutableTest.class).osmFile("bridge_construction.osm.pbf"); OsmProvider provider = new OsmProvider(osmDataFile, true); OsmModule osmBuilder = OsmModule.of(provider, graph).withAreaVisibility(true).build(); osmBuilder.buildGraph(); diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilderTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilderTest.java index 1756c842048..4906bce930d 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilderTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilderTest.java @@ -40,7 +40,7 @@ public Graph buildGraph(final TestInfo testInfo) { final Set boardingAreaRefTags = Set.of(); final OsmDatabase osmdb = new OsmDatabase(DataImportIssueStore.NOOP); - final File file = ResourceLoader.osmFile(osmFile); + final File file = ResourceLoader.of(WalkableAreaBuilderTest.class).file(osmFile); assertTrue(file.exists()); new OsmProvider(file, false).readOSM(osmdb); osmdb.postLoad(); diff --git a/src/test/java/org/opentripplanner/openstreetmap/model/BicycleNetworkRelationsTest.java b/src/test/java/org/opentripplanner/openstreetmap/model/BicycleNetworkRelationsTest.java index 15934413f0b..3b80f0f74a4 100644 --- a/src/test/java/org/opentripplanner/openstreetmap/model/BicycleNetworkRelationsTest.java +++ b/src/test/java/org/opentripplanner/openstreetmap/model/BicycleNetworkRelationsTest.java @@ -2,12 +2,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.opentripplanner.test.support.ResourceLoader.osmFile; import org.junit.jupiter.api.Test; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.graph_builder.module.osm.OsmDatabase; import org.opentripplanner.openstreetmap.OsmProvider; +import org.opentripplanner.test.support.ResourceLoader; public class BicycleNetworkRelationsTest { @@ -19,7 +19,7 @@ public class BicycleNetworkRelationsTest { public void testBicycleRouteRelations() { var issueStore = DataImportIssueStore.NOOP; var osmdb = new OsmDatabase(issueStore); - var provider = new OsmProvider(osmFile("ehningen-minimal.osm.pbf"), true); + var provider = new OsmProvider(ResourceLoader.of(this).file("ehningen-minimal.osm.pbf"), true); provider.readOSM(osmdb); osmdb.postLoad(); diff --git a/src/test/java/org/opentripplanner/test/support/ResourceLoader.java b/src/test/java/org/opentripplanner/test/support/ResourceLoader.java index d79cf2add45..a7392722ee9 100644 --- a/src/test/java/org/opentripplanner/test/support/ResourceLoader.java +++ b/src/test/java/org/opentripplanner/test/support/ResourceLoader.java @@ -13,10 +13,25 @@ */ public class ResourceLoader { + + final Class clazz; + + private ResourceLoader(Class clazz) { + this.clazz = clazz; + } + + public static ResourceLoader of(Class clazz) { + return new ResourceLoader(clazz); + } + + public static ResourceLoader of(Object object) { + return new ResourceLoader(object.getClass()); + } + /** * Return a File instance for the given path. */ - public static File file(String path) { + public File file(String path) { URL resource = url(path); var file = new File(resource.getFile()); assertTrue(file.exists(), "File %s not found on file system".formatted(file.getAbsolutePath())); @@ -26,16 +41,16 @@ public static File file(String path) { /** * Return a File instance for the given name from the /osm subfolder. */ - public static File osmFile(String osmFile) { + public File osmFile(String osmFile) { return file("/osm/" + osmFile); } /** * Return a URL for the given resource. */ - public static URL url(String name) { - var resource = ResourceLoader.class.getResource(name); - var msg = "Resource %s not found on file system".formatted(resource); + public URL url(String name) { + var resource = clazz.getResource(name); + var msg = "Resource '%s' not found on in package '%s'".formatted(name, clazz.getPackageName()); assertNotNull(resource, msg); return resource; } @@ -43,7 +58,7 @@ public static URL url(String name) { /** * Return a URI for the given resource. */ - public static URI uri(String s) { + public URI uri(String s) { try { return url(s).toURI(); } catch (URISyntaxException e) { diff --git a/src/test/java/org/opentripplanner/updater/trip/GtfsRealtimeTripUpdateSourceTest.java b/src/test/java/org/opentripplanner/updater/trip/GtfsRealtimeTripUpdateSourceTest.java index cd5e7ba4a53..f971f476d7f 100644 --- a/src/test/java/org/opentripplanner/updater/trip/GtfsRealtimeTripUpdateSourceTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/GtfsRealtimeTripUpdateSourceTest.java @@ -19,7 +19,8 @@ public void parseFeed() { false, BackwardsDelayPropagationType.ALWAYS, "rt", - ResourceLoader.url("/gtfs-rt/trip-updates/septa.pbf").toString(), + ResourceLoader.of(this).url("/gtfs-rt/trip-updates/septa.pbf").toString(), + HttpHeaders.empty() ) ); diff --git a/src/test/java/org/opentripplanner/updater/vehicle_position/VehiclePositionParsingTest.java b/src/test/java/org/opentripplanner/updater/vehicle_position/VehiclePositionParsingTest.java index 5b4a38833fa..4aae26fbfa4 100644 --- a/src/test/java/org/opentripplanner/updater/vehicle_position/VehiclePositionParsingTest.java +++ b/src/test/java/org/opentripplanner/updater/vehicle_position/VehiclePositionParsingTest.java @@ -37,7 +37,7 @@ public void parseSecondPositionsFeed() { private GtfsRealtimeHttpVehiclePositionSource getVehiclePositionSource(String filename) { return new GtfsRealtimeHttpVehiclePositionSource( - ResourceLoader.uri("/gtfs-rt/vehicle-positions/" + filename), + ResourceLoader.of(this).uri("/gtfs-rt/vehicle-positions/" + filename), HttpHeaders.empty() ); } diff --git a/src/test/resources/osm/map.osm.pbf b/src/test/resources/org/opentripplanner/graph/graph_builder/module/osm/map.osm.pbf similarity index 100% rename from src/test/resources/osm/map.osm.pbf rename to src/test/resources/org/opentripplanner/graph/graph_builder/module/osm/map.osm.pbf From 61e2753b5b316b24f9aff95edfb5324c4419869c Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Mon, 25 Sep 2023 11:21:18 +0200 Subject: [PATCH 43/95] Apply review suggestions --- .../vehiclerental/model/TestVehicleRentalStationBuilder.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/org/opentripplanner/service/vehiclerental/model/TestVehicleRentalStationBuilder.java b/src/test/java/org/opentripplanner/service/vehiclerental/model/TestVehicleRentalStationBuilder.java index 4ed26d4869b..4557be813d5 100644 --- a/src/test/java/org/opentripplanner/service/vehiclerental/model/TestVehicleRentalStationBuilder.java +++ b/src/test/java/org/opentripplanner/service/vehiclerental/model/TestVehicleRentalStationBuilder.java @@ -5,7 +5,6 @@ import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.street.model.RentalFormFactor; import org.opentripplanner.transit.model.framework.FeedScopedId; -import org.rutebanken.netex.model.LocationStructure; public class TestVehicleRentalStationBuilder { From d492a72128454c7d0ec1cbbd88f2a24fbbeb5eb1 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Mon, 25 Sep 2023 11:31:07 +0200 Subject: [PATCH 44/95] Apply review suggestions --- .../routing/algorithm/mapping/RaptorPathToItineraryMapper.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapper.java index 7bd8b474529..5f946b1d775 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapper.java @@ -348,6 +348,8 @@ private ZonedDateTime createZonedDateTime(int timeInSeconds) { * Include transfer leg in itinerary if the path is a "physical" path-leg between two stops, like * walk or bicycle. Do NOT include it if it represents a stay-seated transfer. See more details in * https://github.com/opentripplanner/OpenTripPlanner/issues/5086. + * TODO: the logic should be revisited when adding support for transfer between on-board flex + * access and transit. */ private boolean includeTransferInItinerary(Leg transitLegBeforeTransfer) { return ( From 68dcfcbfb3629f8d410bbf0996484c4affcc4ab2 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 25 Sep 2023 12:16:29 +0200 Subject: [PATCH 45/95] Use resource loader throughout --- .../ext/fares/impl/FaresIntegrationTest.java | 4 +- .../opentripplanner/ConstantsForTests.java | 17 +- .../graph_builder/module/FakeGraph.java | 18 +- .../graph_builder/module/GtfsModuleTest.java | 2 +- .../OsmBoardingLocationsModuleTest.java | 4 +- .../islandpruning/AdaptivePruningTest.java | 4 +- .../islandpruning/PruneNoThruIslandsTest.java | 4 +- .../module/linking/LinkingTest.java | 23 +- .../module/osm/OsmModuleTest.java | 2 +- .../module/osm/TestIntermediatePlaces.java | 372 ------------------ .../module/osm/UnconnectedAreasTest.java | 27 +- .../module/osm/UnroutableTest.java | 2 +- .../integration/BarrierRoutingTest.java | 5 +- .../integration/BicycleRoutingTest.java | 5 +- .../street/integration/CarRoutingTest.java | 8 +- .../SplitEdgeTurnRestrictionsTest.java | 12 +- .../test/support/ResourceLoader.java | 8 - .../GtfsRealtimeTripUpdateSourceTest.java | 3 +- .../VehiclePositionParsingTest.java | 2 +- .../module}/herrenberg-minimal.osm.pbf | Bin .../module}/interlining/agency.txt | 0 .../module}/interlining/calendar_dates.txt | 0 .../module}/interlining/description.txt | 0 .../module}/interlining/routes.txt | 0 .../module}/interlining/stop_times.txt | 0 .../module}/interlining/stops.txt | 0 .../module}/interlining/transfers.txt | 0 .../module}/interlining/trips.txt | 0 .../herrenberg-island-prune-nothru.osm.pbf | Bin .../module/islandpruning}/isoiiluoto.pbf | Bin .../module/linking}/columbus.osm.pbf | Bin .../graph_builder/module}/osm/B+R.osm.pbf | Bin .../module}/osm/NYC_small.osm.pbf | Bin .../graph_builder/module}/osm/P+R.osm.pbf | Bin .../module}/osm/bridge_construction.osm.pbf | Bin .../module}/osm/coincident_pr.osm.pbf | Bin .../module}/osm/coincident_pr_dupl.osm.pbf | Bin .../module}/osm/coincident_pr_overlap.osm.pbf | Bin .../module}/osm/coincident_pr_reverse.osm.pbf | Bin .../module}/osm/hackett_pr.osm.pbf | Bin .../module}/osm/lund-station-sweden.osm.pbf | Bin .../graph_builder/module/osm/map.osm.pbf | Bin .../graph_builder/module}/osm/skoyen.osm.pbf | Bin .../graph_builder/module}/osm/stopareas.pbf | Bin .../module}/osm/usf_area.osm.pbf | Bin .../module}/osm/wendlingen-bahnhof.osm.pbf | Bin .../model}/ehningen-minimal.osm.pbf | Bin .../integration}/boeblingen-minimal.osm.pbf | Bin .../integration}/deufringen-minimal.osm.pbf | Bin .../herrenberg-barrier-gates.osm.pbf | Bin .../integration}/vvs-bus-751-only.gtfs.zip | Bin .../integration}/vvs-bus-764-only.gtfs.zip | Bin .../opentripplanner/updater/trip}/septa.pbf | Bin .../vehicle_position}/king-county-metro-1.pbf | Bin .../vehicle_position}/king-county-metro-2.pbf | Bin 55 files changed, 73 insertions(+), 449 deletions(-) delete mode 100644 src/test/java/org/opentripplanner/graph_builder/module/osm/TestIntermediatePlaces.java rename src/test/resources/{osm => org/opentripplanner/graph_builder/module}/herrenberg-minimal.osm.pbf (100%) rename src/test/resources/{gtfs => org/opentripplanner/graph_builder/module}/interlining/agency.txt (100%) rename src/test/resources/{gtfs => org/opentripplanner/graph_builder/module}/interlining/calendar_dates.txt (100%) rename src/test/resources/{gtfs => org/opentripplanner/graph_builder/module}/interlining/description.txt (100%) rename src/test/resources/{gtfs => org/opentripplanner/graph_builder/module}/interlining/routes.txt (100%) rename src/test/resources/{gtfs => org/opentripplanner/graph_builder/module}/interlining/stop_times.txt (100%) rename src/test/resources/{gtfs => org/opentripplanner/graph_builder/module}/interlining/stops.txt (100%) rename src/test/resources/{gtfs => org/opentripplanner/graph_builder/module}/interlining/transfers.txt (100%) rename src/test/resources/{gtfs => org/opentripplanner/graph_builder/module}/interlining/trips.txt (100%) rename src/test/resources/{osm => org/opentripplanner/graph_builder/module/islandpruning}/herrenberg-island-prune-nothru.osm.pbf (100%) rename src/test/resources/{osm => org/opentripplanner/graph_builder/module/islandpruning}/isoiiluoto.pbf (100%) rename src/test/resources/{osm => org/opentripplanner/graph_builder/module/linking}/columbus.osm.pbf (100%) rename src/test/resources/{ => org/opentripplanner/graph_builder/module}/osm/B+R.osm.pbf (100%) rename src/test/resources/{ => org/opentripplanner/graph_builder/module}/osm/NYC_small.osm.pbf (100%) rename src/test/resources/{ => org/opentripplanner/graph_builder/module}/osm/P+R.osm.pbf (100%) rename src/test/resources/{ => org/opentripplanner/graph_builder/module}/osm/bridge_construction.osm.pbf (100%) rename src/test/resources/{ => org/opentripplanner/graph_builder/module}/osm/coincident_pr.osm.pbf (100%) rename src/test/resources/{ => org/opentripplanner/graph_builder/module}/osm/coincident_pr_dupl.osm.pbf (100%) rename src/test/resources/{ => org/opentripplanner/graph_builder/module}/osm/coincident_pr_overlap.osm.pbf (100%) rename src/test/resources/{ => org/opentripplanner/graph_builder/module}/osm/coincident_pr_reverse.osm.pbf (100%) rename src/test/resources/{ => org/opentripplanner/graph_builder/module}/osm/hackett_pr.osm.pbf (100%) rename src/test/resources/{ => org/opentripplanner/graph_builder/module}/osm/lund-station-sweden.osm.pbf (100%) rename src/test/resources/org/opentripplanner/{graph => }/graph_builder/module/osm/map.osm.pbf (100%) rename src/test/resources/{ => org/opentripplanner/graph_builder/module}/osm/skoyen.osm.pbf (100%) rename src/test/resources/{ => org/opentripplanner/graph_builder/module}/osm/stopareas.pbf (100%) rename src/test/resources/{ => org/opentripplanner/graph_builder/module}/osm/usf_area.osm.pbf (100%) rename src/test/resources/{ => org/opentripplanner/graph_builder/module}/osm/wendlingen-bahnhof.osm.pbf (100%) rename src/test/resources/{osm => org/opentripplanner/openstreetmap/model}/ehningen-minimal.osm.pbf (100%) rename src/test/resources/{osm => org/opentripplanner/street/integration}/boeblingen-minimal.osm.pbf (100%) rename src/test/resources/{osm => org/opentripplanner/street/integration}/deufringen-minimal.osm.pbf (100%) rename src/test/resources/{osm => org/opentripplanner/street/integration}/herrenberg-barrier-gates.osm.pbf (100%) rename src/test/resources/{gtfs => org/opentripplanner/street/integration}/vvs-bus-751-only.gtfs.zip (100%) rename src/test/resources/{gtfs => org/opentripplanner/street/integration}/vvs-bus-764-only.gtfs.zip (100%) rename src/test/resources/{gtfs-rt/trip-updates => org/opentripplanner/updater/trip}/septa.pbf (100%) rename src/test/resources/{gtfs-rt/vehicle-positions => org/opentripplanner/updater/vehicle_position}/king-county-metro-1.pbf (100%) rename src/test/resources/{gtfs-rt/vehicle-positions => org/opentripplanner/updater/vehicle_position}/king-county-metro-2.pbf (100%) diff --git a/src/ext-test/java/org/opentripplanner/ext/fares/impl/FaresIntegrationTest.java b/src/ext-test/java/org/opentripplanner/ext/fares/impl/FaresIntegrationTest.java index 0f6430e32ac..50bee0a510c 100644 --- a/src/ext-test/java/org/opentripplanner/ext/fares/impl/FaresIntegrationTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/fares/impl/FaresIntegrationTest.java @@ -108,7 +108,9 @@ public void testPortland() { @Test public void testFareComponent() { - TestOtpModel model = ConstantsForTests.buildGtfsGraph(ResourceLoader.of(this).file("/gtfs/farecomponents.gtfs.zip")); + TestOtpModel model = ConstantsForTests.buildGtfsGraph( + ResourceLoader.of(this).file("/gtfs/farecomponents.gtfs.zip") + ); Graph graph = model.graph(); TransitModel transitModel = model.transitModel(); String feedId = transitModel.getFeedIds().iterator().next(); diff --git a/src/test/java/org/opentripplanner/ConstantsForTests.java b/src/test/java/org/opentripplanner/ConstantsForTests.java index 9debf270272..17fc03712cb 100644 --- a/src/test/java/org/opentripplanner/ConstantsForTests.java +++ b/src/test/java/org/opentripplanner/ConstantsForTests.java @@ -1,6 +1,5 @@ package org.opentripplanner; - import com.csvreader.CsvReader; import java.io.File; import java.io.IOException; @@ -48,7 +47,7 @@ public class ConstantsForTests { - private final static ResourceLoader RES = ResourceLoader.of(ConstantsForTests.class); + private static final ResourceLoader RES = ResourceLoader.of(ConstantsForTests.class); public static final File CALTRAIN_GTFS = RES.file("/gtfs/caltrain_gtfs.zip"); @@ -77,20 +76,6 @@ public class ConstantsForTests { private static final String NETEX_NORDIC_FILENAME = "netex_minimal.zip"; private static final String NETEX_EPIP_DIR = "src/test/resources/netex/epip/"; private static final String NETEX_EPIP_DATA_DIR = NETEX_EPIP_DIR + "netex_epip_minimal/"; - /* Stuttgart area, Germany */ - public static final File DEUFRINGEN_OSM = RES.osmFile("deufringen-minimal.osm.pbf"); - public static final File BOEBLINGEN_OSM = RES.osmFile("boeblingen-minimal.osm.pbf"); - public static final File VVS_BUS_764_ONLY = RES.file("/gtfs/vvs-bus-764-only.gtfs.zip"); - public static final File VVS_BUS_751_ONLY = RES.file("/gtfs/vvs-bus-751-only.gtfs.zip"); - public static final File HERRENBERG_HINDENBURG_STR_UNDER_CONSTRUCTION_OSM = RES.osmFile( - "herrenberg-hindenburgstr-under-construction.osm.pbf" - ); - public static final File HERRENBERG_BARRIER_GATES_OSM = RES.osmFile( - "herrenberg-barrier-gates.osm.pbf" - ); - public static final File HERRENBERG_OSM = RES.osmFile("herrenberg-minimal.osm.pbf"); - public static final File ISLAND_PRUNE_OSM = RES.osmFile("herrenberg-island-prune-nothru.osm.pbf"); - public static final File ADAPTIVE_PRUNE_OSM = RES.osmFile("isoiiluoto.pbf"); /* filenames encoded with cp437 and utf8 */ public static final String UMLAUT_CP437_ZIP = "src/test/resources/umlaut-cp437.zip"; diff --git a/src/test/java/org/opentripplanner/graph_builder/module/FakeGraph.java b/src/test/java/org/opentripplanner/graph_builder/module/FakeGraph.java index 6dc14e583e1..17ce9e6d5d3 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/FakeGraph.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/FakeGraph.java @@ -30,23 +30,7 @@ */ public class FakeGraph { - private final static ResourceLoader RES = ResourceLoader.of(OsmModuleTest.class); - - /** Build a graph in Columbus, OH with no transit */ - public static TestOtpModel buildGraphNoTransit() { - var deduplicator = new Deduplicator(); - var stopModel = new StopModel(); - var gg = new Graph(deduplicator); - var transitModel = new TransitModel(stopModel, deduplicator); - - File file = RES.file("columbus.osm.pbf"); - OsmProvider provider = new OsmProvider(file, false); - - OsmModule osmModule = OsmModule.of(provider, gg).build(); - - osmModule.buildGraph(); - return new TestOtpModel(gg, transitModel); - } + private static final ResourceLoader RES = ResourceLoader.of(OsmModuleTest.class); /** * This introduces a 1MB test resource but is only used by TestIntermediatePlaces. diff --git a/src/test/java/org/opentripplanner/graph_builder/module/GtfsModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/GtfsModuleTest.java index c628a14ad43..0be8ab258c9 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/GtfsModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/GtfsModuleTest.java @@ -67,7 +67,7 @@ record TestModels(Graph graph, TransitModel transitModel) {} class Interlining { static GtfsBundle bundle(String feedId) { - var b = new GtfsBundle(ResourceLoader.of(GtfsModuleTest.class).file("/gtfs/interlining")); + var b = new GtfsBundle(ResourceLoader.of(GtfsModuleTest.class).file("interlining")); b.setFeedId(new GtfsFeedId.Builder().id(feedId).build()); return b; } diff --git a/src/test/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModuleTest.java index 43a10188a20..fb05619ca62 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModuleTest.java @@ -9,7 +9,6 @@ import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; -import org.opentripplanner.ConstantsForTests; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.graph_builder.module.osm.OsmModule; import org.opentripplanner.openstreetmap.OsmProvider; @@ -23,6 +22,7 @@ import org.opentripplanner.street.model.vertex.Vertex; import org.opentripplanner.street.model.vertex.VertexFactory; import org.opentripplanner.street.model.vertex.VertexLabel; +import org.opentripplanner.test.support.ResourceLoader; import org.opentripplanner.test.support.VariableSource; import org.opentripplanner.transit.model._data.TransitModelForTest; import org.opentripplanner.transit.model.basic.TransitMode; @@ -37,7 +37,7 @@ */ class OsmBoardingLocationsModuleTest { - File file = ConstantsForTests.HERRENBERG_OSM; + File file = ResourceLoader.of(OsmBoardingLocationsModuleTest.class).file("herrenberg-minimal.osm.pbf"); RegularStop platform = TransitModelForTest .stop("de:08115:4512:4:101") .withCoordinate(48.59328, 8.86128) diff --git a/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/AdaptivePruningTest.java b/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/AdaptivePruningTest.java index 8768ffbbcb0..832045b6469 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/AdaptivePruningTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/AdaptivePruningTest.java @@ -5,11 +5,11 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.opentripplanner.ConstantsForTests; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.graph_builder.module.osm.OsmModule; import org.opentripplanner.openstreetmap.OsmProvider; import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.test.support.ResourceLoader; import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.service.StopModel; import org.opentripplanner.transit.service.TransitModel; @@ -26,7 +26,7 @@ public class AdaptivePruningTest { @BeforeAll static void setup() { - graph = buildOsmGraph(ConstantsForTests.ADAPTIVE_PRUNE_OSM); + graph = buildOsmGraph(ResourceLoader.of(AdaptivePruningTest.class).file("isoiiluoto.pbf")); } @Test diff --git a/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/PruneNoThruIslandsTest.java b/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/PruneNoThruIslandsTest.java index d143a184299..29c74d07264 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/PruneNoThruIslandsTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/PruneNoThruIslandsTest.java @@ -6,12 +6,12 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.opentripplanner.ConstantsForTests; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.graph_builder.module.osm.OsmModule; import org.opentripplanner.openstreetmap.OsmProvider; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.street.model.edge.StreetEdge; +import org.opentripplanner.test.support.ResourceLoader; import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.service.StopModel; import org.opentripplanner.transit.service.TransitModel; @@ -22,7 +22,7 @@ public class PruneNoThruIslandsTest { @BeforeAll static void setup() { - graph = buildOsmGraph(ConstantsForTests.ISLAND_PRUNE_OSM); + graph = buildOsmGraph(ResourceLoader.of(PruneNoThruIslandsTest.class).file("herrenberg-island-prune-nothru.osm.pbf")); } @Test diff --git a/src/test/java/org/opentripplanner/graph_builder/module/linking/LinkingTest.java b/src/test/java/org/opentripplanner/graph_builder/module/linking/LinkingTest.java index 2c2f4092278..c4576f0e9ca 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/linking/LinkingTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/linking/LinkingTest.java @@ -5,9 +5,9 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.opentripplanner.graph_builder.module.FakeGraph.addExtraStops; import static org.opentripplanner.graph_builder.module.FakeGraph.addRegularStopGrid; -import static org.opentripplanner.graph_builder.module.FakeGraph.buildGraphNoTransit; import static org.opentripplanner.graph_builder.module.FakeGraph.link; +import java.io.File; import java.net.URISyntaxException; import java.util.Comparator; import java.util.List; @@ -19,6 +19,8 @@ import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.framework.geometry.SphericalDistanceLibrary; import org.opentripplanner.framework.i18n.NonLocalizedString; +import org.opentripplanner.graph_builder.module.osm.OsmModule; +import org.opentripplanner.openstreetmap.OsmProvider; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.street.model.StreetTraversalPermission; import org.opentripplanner.street.model._data.StreetModelForTest; @@ -29,6 +31,9 @@ import org.opentripplanner.street.model.vertex.StreetVertex; import org.opentripplanner.street.model.vertex.TransitStopVertex; import org.opentripplanner.street.model.vertex.Vertex; +import org.opentripplanner.test.support.ResourceLoader; +import org.opentripplanner.transit.model.framework.Deduplicator; +import org.opentripplanner.transit.service.StopModel; import org.opentripplanner.transit.service.TransitModel; public class LinkingTest { @@ -143,6 +148,22 @@ public void testStopsLinkedIdentically() throws URISyntaxException { } } + /** Build a graph in Columbus, OH with no transit */ + public static TestOtpModel buildGraphNoTransit() { + var deduplicator = new Deduplicator(); + var stopModel = new StopModel(); + var gg = new Graph(deduplicator); + var transitModel = new TransitModel(stopModel, deduplicator); + + File file = ResourceLoader.of(LinkingTest.class).file("columbus.osm.pbf"); + OsmProvider provider = new OsmProvider(file, false); + + OsmModule osmModule = OsmModule.of(provider, gg).build(); + + osmModule.buildGraph(); + return new TestOtpModel(gg, transitModel); + } + private static List outgoingStls(final TransitStopVertex tsv) { return tsv .getOutgoing() diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java index ac762c66ff3..1ed2d699f90 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java @@ -45,7 +45,7 @@ public class OsmModuleTest { - private final static ResourceLoader RES = ResourceLoader.of(OsmModuleTest.class); + private static final ResourceLoader RES = ResourceLoader.of(OsmModuleTest.class); @Test public void testGraphBuilder() { diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/TestIntermediatePlaces.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/TestIntermediatePlaces.java deleted file mode 100644 index e1cffe6cd85..00000000000 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/TestIntermediatePlaces.java +++ /dev/null @@ -1,372 +0,0 @@ -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.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.opentripplanner.routing.api.request.StreetMode.CAR; -import static org.opentripplanner.routing.api.request.StreetMode.NOT_SET; - -import java.time.Instant; -import java.time.ZoneId; -import java.util.List; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.opentripplanner.TestOtpModel; -import org.opentripplanner.astar.model.GraphPath; -import org.opentripplanner.graph_builder.module.FakeGraph; -import org.opentripplanner.model.GenericLocation; -import org.opentripplanner.model.modes.ExcludeAllTransitFilter; -import org.opentripplanner.model.plan.Itinerary; -import org.opentripplanner.model.plan.Leg; -import org.opentripplanner.model.plan.Place; -import org.opentripplanner.model.plan.TripPlan; -import org.opentripplanner.routing.algorithm.mapping.GraphPathToItineraryMapper; -import org.opentripplanner.routing.algorithm.mapping.TripPlanMapper; -import org.opentripplanner.routing.api.request.RequestModes; -import org.opentripplanner.routing.api.request.RouteRequest; -import org.opentripplanner.routing.api.request.request.filter.AllowAllTransitFilter; -import org.opentripplanner.routing.api.request.request.filter.TransitFilter; -import org.opentripplanner.routing.graph.Graph; -import org.opentripplanner.routing.impl.GraphPathFinder; -import org.opentripplanner.street.model.edge.Edge; -import org.opentripplanner.street.model.vertex.Vertex; -import org.opentripplanner.street.search.TemporaryVerticesContainer; -import org.opentripplanner.street.search.state.State; -import org.opentripplanner.transit.service.TransitModel; - -/** - * Tests for planning with intermediate places - * TODO OTP2 - Test is too close to the implementation and will need to be reimplemented. - */ -@Disabled -public class TestIntermediatePlaces { - - /** - * The spatial deviation that we allow in degrees - */ - public static final double DELTA = 0.005; - - private static ZoneId timeZone; - - private static GraphPathFinder graphPathFinder; - - private static Graph graph; - - private static GraphPathToItineraryMapper graphPathToItineraryMapper; - - @BeforeAll - public static void setUp() { - try { - TestOtpModel model = FakeGraph.buildGraphNoTransit(); - graph = model.graph(); - TransitModel transitModel = model.transitModel(); - FakeGraph.addPerpendicularRoutes(graph, transitModel); - FakeGraph.link(graph, transitModel); - model.index(); - TestIntermediatePlaces.graphPathFinder = new GraphPathFinder(null); - timeZone = transitModel.getTimeZone(); - - graphPathToItineraryMapper = - new GraphPathToItineraryMapper( - timeZone, - graph.streetNotesService, - graph.ellipsoidToGeoidDifference - ); - } catch (Exception e) { - e.printStackTrace(); - assert false : "Could not add transit data: " + e; - } - } - - @Test - public void testWithoutIntermediatePlaces() { - GenericLocation fromLocation = new GenericLocation(39.93080, -82.98522); - GenericLocation toLocation = new GenericLocation(39.96383, -82.96291); - GenericLocation[] intermediateLocations = {}; - - handleRequest( - fromLocation, - toLocation, - intermediateLocations, - RequestModes.of().build(), - List.of(ExcludeAllTransitFilter.of()), - false - ); - handleRequest( - fromLocation, - toLocation, - intermediateLocations, - RequestModes.of().build(), - List.of(ExcludeAllTransitFilter.of()), - true - ); - } - - @Test - @Disabled - public void testOneIntermediatePlace() { - GenericLocation fromLocation = new GenericLocation(39.93080, -82.98522); - GenericLocation toLocation = new GenericLocation(39.96383, -82.96291); - GenericLocation[] intermediateLocations = { new GenericLocation(39.92099, -82.95570) }; - - handleRequest( - fromLocation, - toLocation, - intermediateLocations, - RequestModes.of().build(), - List.of(ExcludeAllTransitFilter.of()), - false - ); - handleRequest( - fromLocation, - toLocation, - intermediateLocations, - RequestModes.of().build(), - List.of(ExcludeAllTransitFilter.of()), - true - ); - } - - @Test - @Disabled - public void testTwoIntermediatePlaces() { - GenericLocation fromLocation = new GenericLocation(39.93080, -82.98522); - GenericLocation toLocation = new GenericLocation(39.96383, -82.96291); - GenericLocation[] intermediateLocations = new GenericLocation[2]; - intermediateLocations[0] = new GenericLocation(39.92099, -82.95570); - intermediateLocations[1] = new GenericLocation(39.96146, -82.99552); - - handleRequest( - fromLocation, - toLocation, - intermediateLocations, - RequestModes.of().withDirectMode(CAR).build(), - List.of(ExcludeAllTransitFilter.of()), - false - ); - handleRequest( - fromLocation, - toLocation, - intermediateLocations, - RequestModes.of().withDirectMode(CAR).build(), - List.of(ExcludeAllTransitFilter.of()), - true - ); - } - - @Test - public void testTransitWithoutIntermediatePlaces() { - GenericLocation fromLocation = new GenericLocation(39.9308, -83.0118); - GenericLocation toLocation = new GenericLocation(39.9998, -83.0198); - GenericLocation[] intermediateLocations = {}; - - handleRequest( - fromLocation, - toLocation, - intermediateLocations, - RequestModes.defaultRequestModes(), - List.of(AllowAllTransitFilter.of()), - false - ); - handleRequest( - fromLocation, - toLocation, - intermediateLocations, - RequestModes.defaultRequestModes(), - List.of(AllowAllTransitFilter.of()), - true - ); - } - - @Test - public void testThreeBusStopPlaces() { - GenericLocation fromLocation = new GenericLocation(39.9058, -83.1341); - GenericLocation toLocation = new GenericLocation(39.9058, -82.8841); - GenericLocation[] intermediateLocations = { new GenericLocation(39.9058, -82.9841) }; - - handleRequest( - fromLocation, - toLocation, - intermediateLocations, - RequestModes.of().withDirectMode(NOT_SET).build(), - List.of(AllowAllTransitFilter.of()), - false - ); - handleRequest( - fromLocation, - toLocation, - intermediateLocations, - RequestModes.of().withDirectMode(NOT_SET).build(), - List.of(AllowAllTransitFilter.of()), - true - ); - } - - @Test - public void testTransitOneIntermediatePlace() { - GenericLocation fromLocation = new GenericLocation(39.9108, -83.0118); - GenericLocation toLocation = new GenericLocation(39.9698, -83.0198); - GenericLocation[] intermediateLocations = { new GenericLocation(39.9948, -83.0148) }; - - handleRequest( - fromLocation, - toLocation, - intermediateLocations, - RequestModes.defaultRequestModes(), - List.of(AllowAllTransitFilter.of()), - false - ); - handleRequest( - fromLocation, - toLocation, - intermediateLocations, - RequestModes.defaultRequestModes(), - List.of(AllowAllTransitFilter.of()), - true - ); - } - - @Test - public void testTransitTwoIntermediatePlaces() { - GenericLocation fromLocation = new GenericLocation(39.9908, -83.0118); - GenericLocation toLocation = new GenericLocation(39.9998, -83.0198); - GenericLocation[] intermediateLocations = new GenericLocation[2]; - intermediateLocations[0] = new GenericLocation(40.0000, -82.900); - intermediateLocations[1] = new GenericLocation(39.9100, -83.100); - - handleRequest( - fromLocation, - toLocation, - intermediateLocations, - RequestModes.defaultRequestModes(), - List.of(AllowAllTransitFilter.of()), - false - ); - handleRequest( - fromLocation, - toLocation, - intermediateLocations, - RequestModes.defaultRequestModes(), - List.of(AllowAllTransitFilter.of()), - true - ); - } - - private void handleRequest( - GenericLocation from, - GenericLocation to, - GenericLocation[] via, - RequestModes modes, - List filters, - boolean arriveBy - ) { - RouteRequest request = new RouteRequest(); - request.journey().setModes(modes); - request.journey().transit().setFilters(filters); - request.setDateTime("2016-04-20", "13:00", timeZone); - request.setArriveBy(arriveBy); - request.setFrom(from); - request.setTo(to); - for (GenericLocation intermediateLocation : via) { - // TODO VIA - Replace with a RouteViaRequest - //options.addIntermediatePlace(intermediateLocation); - } - try ( - var temporaryVertices = new TemporaryVerticesContainer( - graph, - request, - request.journey().access().mode(), - request.journey().egress().mode() - ) - ) { - List> paths = graphPathFinder.graphPathFinderEntryPoint( - request, - temporaryVertices - ); - - assertNotNull(paths); - assertFalse(paths.isEmpty()); - List itineraries = graphPathToItineraryMapper.mapItineraries(paths); - TripPlan plan = TripPlanMapper.mapTripPlan(request, itineraries); - assertLocationIsVeryCloseToPlace(from, plan.from); - assertLocationIsVeryCloseToPlace(to, plan.to); - assertTrue(1 <= plan.itineraries.size()); - for (Itinerary itinerary : plan.itineraries) { - validateIntermediatePlacesVisited(itinerary, via); - assertTrue(via.length < itinerary.getLegs().size()); - validateLegsTemporally(request, itinerary); - validateLegsSpatially(plan, itinerary); - - // technically this is not 100% right but should work of a test - if (!filters.contains(ExcludeAllTransitFilter.of())) { - assertTrue(itinerary.getTransitDuration().toSeconds() > 0); - } - } - } - } - - // Check that every via location is visited in the right order - private void validateIntermediatePlacesVisited(Itinerary itinerary, GenericLocation[] via) { - int legIndex = 0; - - for (GenericLocation location : via) { - Leg leg; - do { - assertTrue( - legIndex < itinerary.getLegs().size(), - "Intermediate location was not an endpoint of any leg" - ); - leg = itinerary.getLegs().get(legIndex); - legIndex++; - } while ( - Math.abs(leg.getTo().coordinate.latitude() - location.lat) > DELTA || - Math.abs(leg.getTo().coordinate.longitude() - location.lng) > DELTA - ); - } - } - - // Check that the end point of a leg is also the start point of the next leg - private void validateLegsSpatially(TripPlan plan, Itinerary itinerary) { - Place place = plan.from; - for (Leg leg : itinerary.getLegs()) { - assertEquals(place.coordinate, leg.getFrom().coordinate); - place = leg.getTo(); - } - assertEquals(place.coordinate, plan.to.coordinate); - } - - // Check that the start time and end time of each leg are consistent - private void validateLegsTemporally(RouteRequest request, Itinerary itinerary) { - Instant departTime; - Instant arriveTime; - if (request.arriveBy()) { - departTime = itinerary.getLegs().get(0).getStartTime().toInstant(); - arriveTime = request.dateTime(); - } else { - departTime = request.dateTime(); - arriveTime = itinerary.getLegs().get(itinerary.getLegs().size() - 1).getEndTime().toInstant(); - } - long sumOfDuration = 0; - for (Leg leg : itinerary.getLegs()) { - assertFalse(departTime.isAfter(leg.getStartTime().toInstant())); - assertFalse(leg.getStartTime().isAfter(leg.getEndTime())); - - departTime = leg.getEndTime().toInstant(); - sumOfDuration += leg.getDuration().toSeconds(); - } - sumOfDuration += itinerary.getWaitingDuration().toSeconds(); - - assertFalse(departTime.isAfter(arriveTime)); - - // Check the total duration of the legs, - int accuracy = itinerary.getLegs().size(); // allow 1 second per leg for rounding errors - assertEquals(sumOfDuration, itinerary.getDuration().toSeconds(), accuracy); - } - - private void assertLocationIsVeryCloseToPlace(GenericLocation location, Place place) { - assertEquals(location.lat, place.coordinate.latitude(), DELTA); - assertEquals(location.lng, place.coordinate.longitude(), DELTA); - } -} diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/UnconnectedAreasTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/UnconnectedAreasTest.java index 09009ee7d13..e50ddb4f5dc 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/UnconnectedAreasTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/UnconnectedAreasTest.java @@ -3,11 +3,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.io.File; import java.util.HashSet; import java.util.List; import java.util.stream.Collectors; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.graph_builder.issue.service.DefaultDataImportIssueStore; @@ -19,12 +17,17 @@ import org.opentripplanner.street.model.edge.VehicleParkingEdge; import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex; import org.opentripplanner.street.model.vertex.VertexLabel; +import org.opentripplanner.test.support.ResourceLoader; import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.service.StopModel; import org.opentripplanner.transit.service.TransitModel; public class UnconnectedAreasTest { + private static final ResourceLoader RESOURCE_LOADER = ResourceLoader.of( + UnconnectedAreasTest.class + ); + /** * The P+R.osm.pbf file contains 2 park and ride, one a single way area and the other a * multipolygon with a hole. Both are not linked to any street, apart from three roads that @@ -36,7 +39,7 @@ public class UnconnectedAreasTest { @Test public void unconnectedCarParkAndRide() { DefaultDataImportIssueStore issueStore = new DefaultDataImportIssueStore(); - Graph gg = buildOSMGraph("/osm/P+R.osm.pbf", issueStore); + Graph gg = buildOSMGraph("P+R.osm.pbf", issueStore); assertEquals(1, getParkAndRideUnlinkedIssueCount(issueStore)); @@ -53,7 +56,7 @@ public void unconnectedCarParkAndRide() { @Test public void unconnectedBikeParkAndRide() { DefaultDataImportIssueStore issueStore = new DefaultDataImportIssueStore(); - Graph gg = buildOSMGraph("/osm/B+R.osm.pbf", issueStore); + Graph gg = buildOSMGraph("B+R.osm.pbf", issueStore); assertEquals(2, getParkAndRideUnlinkedIssueCount(issueStore)); @@ -76,7 +79,7 @@ public void unconnectedBikeParkAndRide() { @Test public void testCoincidentNodeUnconnectedParkAndRide() { List connections = testGeometricGraphWithClasspathFile( - "/osm/hackett_pr.osm.pbf", + "hackett_pr.osm.pbf", 4, 8 ); @@ -94,7 +97,7 @@ public void testCoincidentNodeUnconnectedParkAndRide() { @Test public void testRoadPassingOverNode() { List connections = testGeometricGraphWithClasspathFile( - "/osm/coincident_pr.osm.pbf", + "coincident_pr.osm.pbf", 1, 2 ); @@ -108,7 +111,7 @@ public void testRoadPassingOverNode() { @Test public void testAreaPassingOverNode() { List connections = testGeometricGraphWithClasspathFile( - "/osm/coincident_pr_reverse.osm.pbf", + "coincident_pr_reverse.osm.pbf", 1, 2 ); @@ -122,7 +125,7 @@ public void testAreaPassingOverNode() { @Test public void testRoadPassingOverDuplicatedNode() { List connections = testGeometricGraphWithClasspathFile( - "/osm/coincident_pr_dupl.osm.pbf", + "coincident_pr_dupl.osm.pbf", 1, 2 ); @@ -143,7 +146,7 @@ public void testRoadPassingOverDuplicatedNode() { @Test public void testRoadPassingOverParkRide() { List connections = testGeometricGraphWithClasspathFile( - "/osm/coincident_pr_overlap.osm.pbf", + "coincident_pr_overlap.osm.pbf", 2, 4 ); @@ -161,11 +164,7 @@ private Graph buildOSMGraph(String osmFileName, DataImportIssueStore issueStore) var stopModel = new StopModel(); var graph = new Graph(deduplicator); var transitModel = new TransitModel(stopModel, deduplicator); - var fileUrl = getClass().getResource(osmFileName); - Assertions.assertNotNull(fileUrl); - File file = new File(fileUrl.getFile()); - - OsmProvider provider = new OsmProvider(file, false); + OsmProvider provider = new OsmProvider(RESOURCE_LOADER.file(osmFileName), false); OsmModule loader = OsmModule .of(provider, graph) .withIssueStore(issueStore) diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/UnroutableTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/UnroutableTest.java index 49729bbace6..f921062e790 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/UnroutableTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/UnroutableTest.java @@ -35,7 +35,7 @@ public void setUp() throws Exception { var deduplicator = new Deduplicator(); graph = new Graph(deduplicator); - var osmDataFile = ResourceLoader.of(UnroutableTest.class).osmFile("bridge_construction.osm.pbf"); + var osmDataFile = ResourceLoader.of(UnroutableTest.class).file("bridge_construction.osm.pbf"); OsmProvider provider = new OsmProvider(osmDataFile, true); OsmModule osmBuilder = OsmModule.of(provider, graph).withAreaVisibility(true).build(); osmBuilder.buildGraph(); diff --git a/src/test/java/org/opentripplanner/street/integration/BarrierRoutingTest.java b/src/test/java/org/opentripplanner/street/integration/BarrierRoutingTest.java index a5f37083d4a..ea662bdc236 100644 --- a/src/test/java/org/opentripplanner/street/integration/BarrierRoutingTest.java +++ b/src/test/java/org/opentripplanner/street/integration/BarrierRoutingTest.java @@ -31,6 +31,7 @@ import org.opentripplanner.routing.impl.GraphPathFinder; import org.opentripplanner.street.search.TemporaryVerticesContainer; import org.opentripplanner.street.search.TraverseMode; +import org.opentripplanner.test.support.ResourceLoader; public class BarrierRoutingTest { @@ -41,7 +42,9 @@ public class BarrierRoutingTest { @BeforeAll public static void createGraph() { TestOtpModel model = ConstantsForTests.buildOsmGraph( - ConstantsForTests.HERRENBERG_BARRIER_GATES_OSM + ResourceLoader.of(BarrierRoutingTest.class).file( + "herrenberg-barrier-gates.osm.pbf" + ) ); graph = model.graph(); graph.index(model.transitModel().getStopModel()); diff --git a/src/test/java/org/opentripplanner/street/integration/BicycleRoutingTest.java b/src/test/java/org/opentripplanner/street/integration/BicycleRoutingTest.java index 9588671cd28..300615269a1 100644 --- a/src/test/java/org/opentripplanner/street/integration/BicycleRoutingTest.java +++ b/src/test/java/org/opentripplanner/street/integration/BicycleRoutingTest.java @@ -21,6 +21,7 @@ import org.opentripplanner.routing.impl.GraphPathFinder; import org.opentripplanner.street.search.TemporaryVerticesContainer; import org.opentripplanner.street.search.TraverseMode; +import org.opentripplanner.test.support.ResourceLoader; public class BicycleRoutingTest { @@ -28,7 +29,9 @@ public class BicycleRoutingTest { private final Graph herrenbergGraph; { - TestOtpModel model = ConstantsForTests.buildOsmGraph(ConstantsForTests.HERRENBERG_OSM); + TestOtpModel model = ConstantsForTests.buildOsmGraph( + ResourceLoader.of(BicycleRoutingTest.class).file("herrenberg-minimal.osm.pbf") + ); herrenbergGraph = model.graph(); model.transitModel().index(); diff --git a/src/test/java/org/opentripplanner/street/integration/CarRoutingTest.java b/src/test/java/org/opentripplanner/street/integration/CarRoutingTest.java index 2ed0ff6febd..471d6bf80ce 100644 --- a/src/test/java/org/opentripplanner/street/integration/CarRoutingTest.java +++ b/src/test/java/org/opentripplanner/street/integration/CarRoutingTest.java @@ -22,16 +22,20 @@ import org.opentripplanner.routing.impl.GraphPathFinder; import org.opentripplanner.street.search.TemporaryVerticesContainer; import org.opentripplanner.street.search.TraverseMode; +import org.opentripplanner.test.support.ResourceLoader; public class CarRoutingTest { static final Instant dateTime = Instant.now(); + private static final ResourceLoader RESOURCE_LOADER = ResourceLoader.of(CarRoutingTest.class); private static Graph herrenbergGraph; @BeforeAll public static void setup() { - TestOtpModel model = ConstantsForTests.buildOsmGraph(ConstantsForTests.HERRENBERG_OSM); + TestOtpModel model = ConstantsForTests.buildOsmGraph( + RESOURCE_LOADER.file("herrenberg-minimal.osm.pbf") + ); herrenbergGraph = model.index().graph(); } @@ -53,7 +57,7 @@ public static void setup() { @DisplayName("car routes can contain loops (traversing the same edge twice)") public void shouldAllowLoopCausedByTurnRestrictions() { TestOtpModel model = ConstantsForTests.buildOsmGraph( - ConstantsForTests.HERRENBERG_HINDENBURG_STR_UNDER_CONSTRUCTION_OSM + RESOURCE_LOADER.file("herrenberg-hindenburgstr-under-construction.osm.pbf") ); var hindenburgStrUnderConstruction = model.index().graph(); diff --git a/src/test/java/org/opentripplanner/street/integration/SplitEdgeTurnRestrictionsTest.java b/src/test/java/org/opentripplanner/street/integration/SplitEdgeTurnRestrictionsTest.java index 3f88182c494..28ab5e91725 100644 --- a/src/test/java/org/opentripplanner/street/integration/SplitEdgeTurnRestrictionsTest.java +++ b/src/test/java/org/opentripplanner/street/integration/SplitEdgeTurnRestrictionsTest.java @@ -21,6 +21,7 @@ import org.opentripplanner.routing.impl.GraphPathFinder; import org.opentripplanner.street.search.TemporaryVerticesContainer; import org.opentripplanner.street.search.TraverseMode; +import org.opentripplanner.test.support.ResourceLoader; /* * When bus stops are added to graph they split an existing edge in two parts so that an artificial @@ -45,12 +46,15 @@ public class SplitEdgeTurnRestrictionsTest { static final GenericLocation parkStrasse = new GenericLocation(48.68358, 9.00826); static final GenericLocation herrenbergerStrasse = new GenericLocation(48.68497, 9.00909); static final GenericLocation steinbeissWeg = new GenericLocation(48.68172, 9.00599); + private static final ResourceLoader RESOURCE_LOADER = ResourceLoader.of( + SplitEdgeTurnRestrictionsTest.class + ); @Test void shouldTakeDeufringenTurnRestrictionsIntoAccount() { TestOtpModel model = ConstantsForTests.buildOsmAndGtfsGraph( - ConstantsForTests.DEUFRINGEN_OSM, - ConstantsForTests.VVS_BUS_764_ONLY + RESOURCE_LOADER.file("deufringen-minimal.osm.pbf"), + RESOURCE_LOADER.file("vvs-bus-764-only.gtfs.zip") ); Graph graph = model.graph(); // https://www.openstreetmap.org/relation/10264251 has a turn restriction so when leaving Hardtheimer Weg @@ -89,8 +93,8 @@ void shouldTakeBoeblingenTurnRestrictionsIntoAccount() { // this tests that the following turn restriction is transferred correctly to the split edges // https://www.openstreetmap.org/relation/299171 TestOtpModel model = ConstantsForTests.buildOsmAndGtfsGraph( - ConstantsForTests.BOEBLINGEN_OSM, - ConstantsForTests.VVS_BUS_751_ONLY + RESOURCE_LOADER.file("boeblingen-minimal.osm.pbf"), + RESOURCE_LOADER.file("vvs-bus-751-only.gtfs.zip") ); var graph = model.graph(); diff --git a/src/test/java/org/opentripplanner/test/support/ResourceLoader.java b/src/test/java/org/opentripplanner/test/support/ResourceLoader.java index a7392722ee9..4412af25bb9 100644 --- a/src/test/java/org/opentripplanner/test/support/ResourceLoader.java +++ b/src/test/java/org/opentripplanner/test/support/ResourceLoader.java @@ -13,7 +13,6 @@ */ public class ResourceLoader { - final Class clazz; private ResourceLoader(Class clazz) { @@ -38,13 +37,6 @@ public File file(String path) { return file; } - /** - * Return a File instance for the given name from the /osm subfolder. - */ - public File osmFile(String osmFile) { - return file("/osm/" + osmFile); - } - /** * Return a URL for the given resource. */ diff --git a/src/test/java/org/opentripplanner/updater/trip/GtfsRealtimeTripUpdateSourceTest.java b/src/test/java/org/opentripplanner/updater/trip/GtfsRealtimeTripUpdateSourceTest.java index f971f476d7f..cffb869ad43 100644 --- a/src/test/java/org/opentripplanner/updater/trip/GtfsRealtimeTripUpdateSourceTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/GtfsRealtimeTripUpdateSourceTest.java @@ -19,8 +19,7 @@ public void parseFeed() { false, BackwardsDelayPropagationType.ALWAYS, "rt", - ResourceLoader.of(this).url("/gtfs-rt/trip-updates/septa.pbf").toString(), - + ResourceLoader.of(this).url("septa.pbf").toString(), HttpHeaders.empty() ) ); diff --git a/src/test/java/org/opentripplanner/updater/vehicle_position/VehiclePositionParsingTest.java b/src/test/java/org/opentripplanner/updater/vehicle_position/VehiclePositionParsingTest.java index 4aae26fbfa4..f077321a0aa 100644 --- a/src/test/java/org/opentripplanner/updater/vehicle_position/VehiclePositionParsingTest.java +++ b/src/test/java/org/opentripplanner/updater/vehicle_position/VehiclePositionParsingTest.java @@ -37,7 +37,7 @@ public void parseSecondPositionsFeed() { private GtfsRealtimeHttpVehiclePositionSource getVehiclePositionSource(String filename) { return new GtfsRealtimeHttpVehiclePositionSource( - ResourceLoader.of(this).uri("/gtfs-rt/vehicle-positions/" + filename), + ResourceLoader.of(this).uri(filename), HttpHeaders.empty() ); } diff --git a/src/test/resources/osm/herrenberg-minimal.osm.pbf b/src/test/resources/org/opentripplanner/graph_builder/module/herrenberg-minimal.osm.pbf similarity index 100% rename from src/test/resources/osm/herrenberg-minimal.osm.pbf rename to src/test/resources/org/opentripplanner/graph_builder/module/herrenberg-minimal.osm.pbf diff --git a/src/test/resources/gtfs/interlining/agency.txt b/src/test/resources/org/opentripplanner/graph_builder/module/interlining/agency.txt similarity index 100% rename from src/test/resources/gtfs/interlining/agency.txt rename to src/test/resources/org/opentripplanner/graph_builder/module/interlining/agency.txt diff --git a/src/test/resources/gtfs/interlining/calendar_dates.txt b/src/test/resources/org/opentripplanner/graph_builder/module/interlining/calendar_dates.txt similarity index 100% rename from src/test/resources/gtfs/interlining/calendar_dates.txt rename to src/test/resources/org/opentripplanner/graph_builder/module/interlining/calendar_dates.txt diff --git a/src/test/resources/gtfs/interlining/description.txt b/src/test/resources/org/opentripplanner/graph_builder/module/interlining/description.txt similarity index 100% rename from src/test/resources/gtfs/interlining/description.txt rename to src/test/resources/org/opentripplanner/graph_builder/module/interlining/description.txt diff --git a/src/test/resources/gtfs/interlining/routes.txt b/src/test/resources/org/opentripplanner/graph_builder/module/interlining/routes.txt similarity index 100% rename from src/test/resources/gtfs/interlining/routes.txt rename to src/test/resources/org/opentripplanner/graph_builder/module/interlining/routes.txt diff --git a/src/test/resources/gtfs/interlining/stop_times.txt b/src/test/resources/org/opentripplanner/graph_builder/module/interlining/stop_times.txt similarity index 100% rename from src/test/resources/gtfs/interlining/stop_times.txt rename to src/test/resources/org/opentripplanner/graph_builder/module/interlining/stop_times.txt diff --git a/src/test/resources/gtfs/interlining/stops.txt b/src/test/resources/org/opentripplanner/graph_builder/module/interlining/stops.txt similarity index 100% rename from src/test/resources/gtfs/interlining/stops.txt rename to src/test/resources/org/opentripplanner/graph_builder/module/interlining/stops.txt diff --git a/src/test/resources/gtfs/interlining/transfers.txt b/src/test/resources/org/opentripplanner/graph_builder/module/interlining/transfers.txt similarity index 100% rename from src/test/resources/gtfs/interlining/transfers.txt rename to src/test/resources/org/opentripplanner/graph_builder/module/interlining/transfers.txt diff --git a/src/test/resources/gtfs/interlining/trips.txt b/src/test/resources/org/opentripplanner/graph_builder/module/interlining/trips.txt similarity index 100% rename from src/test/resources/gtfs/interlining/trips.txt rename to src/test/resources/org/opentripplanner/graph_builder/module/interlining/trips.txt diff --git a/src/test/resources/osm/herrenberg-island-prune-nothru.osm.pbf b/src/test/resources/org/opentripplanner/graph_builder/module/islandpruning/herrenberg-island-prune-nothru.osm.pbf similarity index 100% rename from src/test/resources/osm/herrenberg-island-prune-nothru.osm.pbf rename to src/test/resources/org/opentripplanner/graph_builder/module/islandpruning/herrenberg-island-prune-nothru.osm.pbf diff --git a/src/test/resources/osm/isoiiluoto.pbf b/src/test/resources/org/opentripplanner/graph_builder/module/islandpruning/isoiiluoto.pbf similarity index 100% rename from src/test/resources/osm/isoiiluoto.pbf rename to src/test/resources/org/opentripplanner/graph_builder/module/islandpruning/isoiiluoto.pbf diff --git a/src/test/resources/osm/columbus.osm.pbf b/src/test/resources/org/opentripplanner/graph_builder/module/linking/columbus.osm.pbf similarity index 100% rename from src/test/resources/osm/columbus.osm.pbf rename to src/test/resources/org/opentripplanner/graph_builder/module/linking/columbus.osm.pbf diff --git a/src/test/resources/osm/B+R.osm.pbf b/src/test/resources/org/opentripplanner/graph_builder/module/osm/B+R.osm.pbf similarity index 100% rename from src/test/resources/osm/B+R.osm.pbf rename to src/test/resources/org/opentripplanner/graph_builder/module/osm/B+R.osm.pbf diff --git a/src/test/resources/osm/NYC_small.osm.pbf b/src/test/resources/org/opentripplanner/graph_builder/module/osm/NYC_small.osm.pbf similarity index 100% rename from src/test/resources/osm/NYC_small.osm.pbf rename to src/test/resources/org/opentripplanner/graph_builder/module/osm/NYC_small.osm.pbf diff --git a/src/test/resources/osm/P+R.osm.pbf b/src/test/resources/org/opentripplanner/graph_builder/module/osm/P+R.osm.pbf similarity index 100% rename from src/test/resources/osm/P+R.osm.pbf rename to src/test/resources/org/opentripplanner/graph_builder/module/osm/P+R.osm.pbf diff --git a/src/test/resources/osm/bridge_construction.osm.pbf b/src/test/resources/org/opentripplanner/graph_builder/module/osm/bridge_construction.osm.pbf similarity index 100% rename from src/test/resources/osm/bridge_construction.osm.pbf rename to src/test/resources/org/opentripplanner/graph_builder/module/osm/bridge_construction.osm.pbf diff --git a/src/test/resources/osm/coincident_pr.osm.pbf b/src/test/resources/org/opentripplanner/graph_builder/module/osm/coincident_pr.osm.pbf similarity index 100% rename from src/test/resources/osm/coincident_pr.osm.pbf rename to src/test/resources/org/opentripplanner/graph_builder/module/osm/coincident_pr.osm.pbf diff --git a/src/test/resources/osm/coincident_pr_dupl.osm.pbf b/src/test/resources/org/opentripplanner/graph_builder/module/osm/coincident_pr_dupl.osm.pbf similarity index 100% rename from src/test/resources/osm/coincident_pr_dupl.osm.pbf rename to src/test/resources/org/opentripplanner/graph_builder/module/osm/coincident_pr_dupl.osm.pbf diff --git a/src/test/resources/osm/coincident_pr_overlap.osm.pbf b/src/test/resources/org/opentripplanner/graph_builder/module/osm/coincident_pr_overlap.osm.pbf similarity index 100% rename from src/test/resources/osm/coincident_pr_overlap.osm.pbf rename to src/test/resources/org/opentripplanner/graph_builder/module/osm/coincident_pr_overlap.osm.pbf diff --git a/src/test/resources/osm/coincident_pr_reverse.osm.pbf b/src/test/resources/org/opentripplanner/graph_builder/module/osm/coincident_pr_reverse.osm.pbf similarity index 100% rename from src/test/resources/osm/coincident_pr_reverse.osm.pbf rename to src/test/resources/org/opentripplanner/graph_builder/module/osm/coincident_pr_reverse.osm.pbf diff --git a/src/test/resources/osm/hackett_pr.osm.pbf b/src/test/resources/org/opentripplanner/graph_builder/module/osm/hackett_pr.osm.pbf similarity index 100% rename from src/test/resources/osm/hackett_pr.osm.pbf rename to src/test/resources/org/opentripplanner/graph_builder/module/osm/hackett_pr.osm.pbf diff --git a/src/test/resources/osm/lund-station-sweden.osm.pbf b/src/test/resources/org/opentripplanner/graph_builder/module/osm/lund-station-sweden.osm.pbf similarity index 100% rename from src/test/resources/osm/lund-station-sweden.osm.pbf rename to src/test/resources/org/opentripplanner/graph_builder/module/osm/lund-station-sweden.osm.pbf diff --git a/src/test/resources/org/opentripplanner/graph/graph_builder/module/osm/map.osm.pbf b/src/test/resources/org/opentripplanner/graph_builder/module/osm/map.osm.pbf similarity index 100% rename from src/test/resources/org/opentripplanner/graph/graph_builder/module/osm/map.osm.pbf rename to src/test/resources/org/opentripplanner/graph_builder/module/osm/map.osm.pbf diff --git a/src/test/resources/osm/skoyen.osm.pbf b/src/test/resources/org/opentripplanner/graph_builder/module/osm/skoyen.osm.pbf similarity index 100% rename from src/test/resources/osm/skoyen.osm.pbf rename to src/test/resources/org/opentripplanner/graph_builder/module/osm/skoyen.osm.pbf diff --git a/src/test/resources/osm/stopareas.pbf b/src/test/resources/org/opentripplanner/graph_builder/module/osm/stopareas.pbf similarity index 100% rename from src/test/resources/osm/stopareas.pbf rename to src/test/resources/org/opentripplanner/graph_builder/module/osm/stopareas.pbf diff --git a/src/test/resources/osm/usf_area.osm.pbf b/src/test/resources/org/opentripplanner/graph_builder/module/osm/usf_area.osm.pbf similarity index 100% rename from src/test/resources/osm/usf_area.osm.pbf rename to src/test/resources/org/opentripplanner/graph_builder/module/osm/usf_area.osm.pbf diff --git a/src/test/resources/osm/wendlingen-bahnhof.osm.pbf b/src/test/resources/org/opentripplanner/graph_builder/module/osm/wendlingen-bahnhof.osm.pbf similarity index 100% rename from src/test/resources/osm/wendlingen-bahnhof.osm.pbf rename to src/test/resources/org/opentripplanner/graph_builder/module/osm/wendlingen-bahnhof.osm.pbf diff --git a/src/test/resources/osm/ehningen-minimal.osm.pbf b/src/test/resources/org/opentripplanner/openstreetmap/model/ehningen-minimal.osm.pbf similarity index 100% rename from src/test/resources/osm/ehningen-minimal.osm.pbf rename to src/test/resources/org/opentripplanner/openstreetmap/model/ehningen-minimal.osm.pbf diff --git a/src/test/resources/osm/boeblingen-minimal.osm.pbf b/src/test/resources/org/opentripplanner/street/integration/boeblingen-minimal.osm.pbf similarity index 100% rename from src/test/resources/osm/boeblingen-minimal.osm.pbf rename to src/test/resources/org/opentripplanner/street/integration/boeblingen-minimal.osm.pbf diff --git a/src/test/resources/osm/deufringen-minimal.osm.pbf b/src/test/resources/org/opentripplanner/street/integration/deufringen-minimal.osm.pbf similarity index 100% rename from src/test/resources/osm/deufringen-minimal.osm.pbf rename to src/test/resources/org/opentripplanner/street/integration/deufringen-minimal.osm.pbf diff --git a/src/test/resources/osm/herrenberg-barrier-gates.osm.pbf b/src/test/resources/org/opentripplanner/street/integration/herrenberg-barrier-gates.osm.pbf similarity index 100% rename from src/test/resources/osm/herrenberg-barrier-gates.osm.pbf rename to src/test/resources/org/opentripplanner/street/integration/herrenberg-barrier-gates.osm.pbf diff --git a/src/test/resources/gtfs/vvs-bus-751-only.gtfs.zip b/src/test/resources/org/opentripplanner/street/integration/vvs-bus-751-only.gtfs.zip similarity index 100% rename from src/test/resources/gtfs/vvs-bus-751-only.gtfs.zip rename to src/test/resources/org/opentripplanner/street/integration/vvs-bus-751-only.gtfs.zip diff --git a/src/test/resources/gtfs/vvs-bus-764-only.gtfs.zip b/src/test/resources/org/opentripplanner/street/integration/vvs-bus-764-only.gtfs.zip similarity index 100% rename from src/test/resources/gtfs/vvs-bus-764-only.gtfs.zip rename to src/test/resources/org/opentripplanner/street/integration/vvs-bus-764-only.gtfs.zip diff --git a/src/test/resources/gtfs-rt/trip-updates/septa.pbf b/src/test/resources/org/opentripplanner/updater/trip/septa.pbf similarity index 100% rename from src/test/resources/gtfs-rt/trip-updates/septa.pbf rename to src/test/resources/org/opentripplanner/updater/trip/septa.pbf diff --git a/src/test/resources/gtfs-rt/vehicle-positions/king-county-metro-1.pbf b/src/test/resources/org/opentripplanner/updater/vehicle_position/king-county-metro-1.pbf similarity index 100% rename from src/test/resources/gtfs-rt/vehicle-positions/king-county-metro-1.pbf rename to src/test/resources/org/opentripplanner/updater/vehicle_position/king-county-metro-1.pbf diff --git a/src/test/resources/gtfs-rt/vehicle-positions/king-county-metro-2.pbf b/src/test/resources/org/opentripplanner/updater/vehicle_position/king-county-metro-2.pbf similarity index 100% rename from src/test/resources/gtfs-rt/vehicle-positions/king-county-metro-2.pbf rename to src/test/resources/org/opentripplanner/updater/vehicle_position/king-county-metro-2.pbf From 6fff48660ed7bc9b3d4f82e0c87bd64df2e244db Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 25 Sep 2023 12:31:26 +0200 Subject: [PATCH 46/95] Move more files into proper folder structure --- .../ext/fares/impl/FaresIntegrationTest.java | 2 +- .../ext/fares/impl}/farecomponents.gtfs.zip | Bin .../opentripplanner/ConstantsForTests.java | 9 +++----- .../graph_builder/module/FakeGraph.java | 21 ------------------ .../graph_builder/module/GtfsModuleTest.java | 2 +- .../transit/service/TransitModelTest.java | 6 +++-- .../gtfs/addPerpendicularRoutes.gtfs.zip | Bin 1240947 -> 0 bytes .../module => gtfs}/interlining/agency.txt | 0 .../interlining/calendar_dates.txt | 0 .../interlining/description.txt | 0 .../module => gtfs}/interlining/routes.txt | 0 .../interlining/stop_times.txt | 0 .../module => gtfs}/interlining/stops.txt | 0 .../module => gtfs}/interlining/transfers.txt | 0 .../module => gtfs}/interlining/trips.txt | 0 .../transit/service}/kcm_gtfs.zip | Bin 16 files changed, 9 insertions(+), 31 deletions(-) rename src/{test/resources/gtfs => ext-test/resources/org/opentripplanner/ext/fares/impl}/farecomponents.gtfs.zip (100%) delete mode 100644 src/test/resources/gtfs/addPerpendicularRoutes.gtfs.zip rename src/test/resources/{org/opentripplanner/graph_builder/module => gtfs}/interlining/agency.txt (100%) rename src/test/resources/{org/opentripplanner/graph_builder/module => gtfs}/interlining/calendar_dates.txt (100%) rename src/test/resources/{org/opentripplanner/graph_builder/module => gtfs}/interlining/description.txt (100%) rename src/test/resources/{org/opentripplanner/graph_builder/module => gtfs}/interlining/routes.txt (100%) rename src/test/resources/{org/opentripplanner/graph_builder/module => gtfs}/interlining/stop_times.txt (100%) rename src/test/resources/{org/opentripplanner/graph_builder/module => gtfs}/interlining/stops.txt (100%) rename src/test/resources/{org/opentripplanner/graph_builder/module => gtfs}/interlining/transfers.txt (100%) rename src/test/resources/{org/opentripplanner/graph_builder/module => gtfs}/interlining/trips.txt (100%) rename src/test/resources/{gtfs => org/opentripplanner/transit/service}/kcm_gtfs.zip (100%) diff --git a/src/ext-test/java/org/opentripplanner/ext/fares/impl/FaresIntegrationTest.java b/src/ext-test/java/org/opentripplanner/ext/fares/impl/FaresIntegrationTest.java index 50bee0a510c..939d05cb2c2 100644 --- a/src/ext-test/java/org/opentripplanner/ext/fares/impl/FaresIntegrationTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/fares/impl/FaresIntegrationTest.java @@ -109,7 +109,7 @@ public void testPortland() { @Test public void testFareComponent() { TestOtpModel model = ConstantsForTests.buildGtfsGraph( - ResourceLoader.of(this).file("/gtfs/farecomponents.gtfs.zip") + ResourceLoader.of(this).file("farecomponents.gtfs.zip") ); Graph graph = model.graph(); TransitModel transitModel = model.transitModel(); diff --git a/src/test/resources/gtfs/farecomponents.gtfs.zip b/src/ext-test/resources/org/opentripplanner/ext/fares/impl/farecomponents.gtfs.zip similarity index 100% rename from src/test/resources/gtfs/farecomponents.gtfs.zip rename to src/ext-test/resources/org/opentripplanner/ext/fares/impl/farecomponents.gtfs.zip diff --git a/src/test/java/org/opentripplanner/ConstantsForTests.java b/src/test/java/org/opentripplanner/ConstantsForTests.java index 17fc03712cb..a9a7f4a3e13 100644 --- a/src/test/java/org/opentripplanner/ConstantsForTests.java +++ b/src/test/java/org/opentripplanner/ConstantsForTests.java @@ -51,9 +51,9 @@ public class ConstantsForTests { public static final File CALTRAIN_GTFS = RES.file("/gtfs/caltrain_gtfs.zip"); - public static final File PORTLAND_GTFS = RES.file("/portland/portland.gtfs.zip"); + private static final File PORTLAND_GTFS = RES.file("/portland/portland.gtfs.zip"); - public static final File PORTLAND_CENTRAL_OSM = RES.file( + private static final File PORTLAND_CENTRAL_OSM = RES.file( "/portland/portland-central-filtered.osm.pbf" ); @@ -65,8 +65,6 @@ public class ConstantsForTests { private static final String OSLO_EAST_OSM = "src/test/resources/osm/oslo-east-filtered.osm.pbf"; - public static final File KCM_GTFS = RES.file("/gtfs/kcm_gtfs.zip"); - public static final File SIMPLE_GTFS = RES.file("/gtfs/simple/"); public static final File SHAPE_DIST_GTFS = RES.file("/gtfs/shape_dist_traveled/"); @@ -130,8 +128,7 @@ public static TestOtpModel buildNewPortlandGraph(boolean withElevation) { var transitModel = new TransitModel(new StopModel(), deduplicator); // Add street data from OSM { - File osmFile = PORTLAND_CENTRAL_OSM; - OsmProvider osmProvider = new OsmProvider(osmFile, false); + OsmProvider osmProvider = new OsmProvider(PORTLAND_CENTRAL_OSM, false); OsmModule osmModule = OsmModule .of(osmProvider, graph) .withStaticParkAndRide(true) diff --git a/src/test/java/org/opentripplanner/graph_builder/module/FakeGraph.java b/src/test/java/org/opentripplanner/graph_builder/module/FakeGraph.java index 17ce9e6d5d3..c1bc5a30fae 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/FakeGraph.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/FakeGraph.java @@ -1,14 +1,6 @@ package org.opentripplanner.graph_builder.module; -import java.io.File; import java.util.List; -import org.opentripplanner.TestOtpModel; -import org.opentripplanner.graph_builder.module.osm.OsmModule; -import org.opentripplanner.graph_builder.module.osm.OsmModuleTest; -import org.opentripplanner.gtfs.graphbuilder.GtfsBundle; -import org.opentripplanner.gtfs.graphbuilder.GtfsModule; -import org.opentripplanner.model.calendar.ServiceDateInterval; -import org.opentripplanner.openstreetmap.OsmProvider; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.linking.LinkingDirection; import org.opentripplanner.routing.linking.VertexLinker; @@ -17,11 +9,8 @@ import org.opentripplanner.street.model.vertex.TransitStopVertexBuilder; import org.opentripplanner.street.search.TraverseMode; import org.opentripplanner.street.search.TraverseModeSet; -import org.opentripplanner.test.support.ResourceLoader; import org.opentripplanner.transit.model._data.TransitModelForTest; -import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.model.site.RegularStop; -import org.opentripplanner.transit.service.StopModel; import org.opentripplanner.transit.service.TransitModel; /** @@ -30,16 +19,6 @@ */ public class FakeGraph { - private static final ResourceLoader RES = ResourceLoader.of(OsmModuleTest.class); - - /** - * This introduces a 1MB test resource but is only used by TestIntermediatePlaces. - */ - public static void addPerpendicularRoutes(Graph graph, TransitModel transitModel) { - var input = List.of(new GtfsBundle(RES.file("addPerpendicularRoutes.gtfs.zip"))); - new GtfsModule(input, transitModel, graph, ServiceDateInterval.unbounded()).buildGraph(); - } - /** Add a regular grid of stops to the graph */ public static void addRegularStopGrid(Graph graph) { int count = 0; diff --git a/src/test/java/org/opentripplanner/graph_builder/module/GtfsModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/GtfsModuleTest.java index 0be8ab258c9..c628a14ad43 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/GtfsModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/GtfsModuleTest.java @@ -67,7 +67,7 @@ record TestModels(Graph graph, TransitModel transitModel) {} class Interlining { static GtfsBundle bundle(String feedId) { - var b = new GtfsBundle(ResourceLoader.of(GtfsModuleTest.class).file("interlining")); + var b = new GtfsBundle(ResourceLoader.of(GtfsModuleTest.class).file("/gtfs/interlining")); b.setFeedId(new GtfsFeedId.Builder().id(feedId).build()); return b; } diff --git a/src/test/java/org/opentripplanner/transit/service/TransitModelTest.java b/src/test/java/org/opentripplanner/transit/service/TransitModelTest.java index 9f14760010f..95c20245f9a 100644 --- a/src/test/java/org/opentripplanner/transit/service/TransitModelTest.java +++ b/src/test/java/org/opentripplanner/transit/service/TransitModelTest.java @@ -11,6 +11,7 @@ import org.opentripplanner.graph_builder.module.TimeZoneAdjusterModule; import org.opentripplanner.model.Timetable; import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.test.support.ResourceLoader; import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.timetable.Trip; @@ -19,6 +20,7 @@ class TransitModelTest { public static final String FAKE_FEED_ID = "FAKE"; public static final FeedScopedId SAMPLE_TRIP_ID = new FeedScopedId(FAKE_FEED_ID, "1.2"); + private static final ResourceLoader RESOURCE_LOADER = ResourceLoader.of(TransitModelTest.class); @Test void validateTimeZones() { @@ -51,7 +53,7 @@ void validateTimeZones() { ConstantsForTests.addGtfsToGraph( graph, transitModel, - ConstantsForTests.KCM_GTFS, + RESOURCE_LOADER.file("kcm_gtfs.zip"), new DefaultFareServiceFactory(), null ), @@ -86,7 +88,7 @@ void validateTimeZonesWithExplicitTimeZone() { ConstantsForTests.addGtfsToGraph( graph, transitModel, - ConstantsForTests.KCM_GTFS, + RESOURCE_LOADER.file("kcm_gtfs.zip"), new DefaultFareServiceFactory(), null ); diff --git a/src/test/resources/gtfs/addPerpendicularRoutes.gtfs.zip b/src/test/resources/gtfs/addPerpendicularRoutes.gtfs.zip deleted file mode 100644 index c1feca1d88fc300dd003add37db95f7dda540b0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1240947 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 Y{TSM)YaxUEVO6qBYZS%eF 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:pcMV^FX0l9ygYfb+Mt)U7oqqh^?w>3+tD8K=y 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 diff --git a/src/test/resources/org/opentripplanner/graph_builder/module/interlining/agency.txt b/src/test/resources/gtfs/interlining/agency.txt similarity index 100% rename from src/test/resources/org/opentripplanner/graph_builder/module/interlining/agency.txt rename to src/test/resources/gtfs/interlining/agency.txt diff --git a/src/test/resources/org/opentripplanner/graph_builder/module/interlining/calendar_dates.txt b/src/test/resources/gtfs/interlining/calendar_dates.txt similarity index 100% rename from src/test/resources/org/opentripplanner/graph_builder/module/interlining/calendar_dates.txt rename to src/test/resources/gtfs/interlining/calendar_dates.txt diff --git a/src/test/resources/org/opentripplanner/graph_builder/module/interlining/description.txt b/src/test/resources/gtfs/interlining/description.txt similarity index 100% rename from src/test/resources/org/opentripplanner/graph_builder/module/interlining/description.txt rename to src/test/resources/gtfs/interlining/description.txt diff --git a/src/test/resources/org/opentripplanner/graph_builder/module/interlining/routes.txt b/src/test/resources/gtfs/interlining/routes.txt similarity index 100% rename from src/test/resources/org/opentripplanner/graph_builder/module/interlining/routes.txt rename to src/test/resources/gtfs/interlining/routes.txt diff --git a/src/test/resources/org/opentripplanner/graph_builder/module/interlining/stop_times.txt b/src/test/resources/gtfs/interlining/stop_times.txt similarity index 100% rename from src/test/resources/org/opentripplanner/graph_builder/module/interlining/stop_times.txt rename to src/test/resources/gtfs/interlining/stop_times.txt diff --git a/src/test/resources/org/opentripplanner/graph_builder/module/interlining/stops.txt b/src/test/resources/gtfs/interlining/stops.txt similarity index 100% rename from src/test/resources/org/opentripplanner/graph_builder/module/interlining/stops.txt rename to src/test/resources/gtfs/interlining/stops.txt diff --git a/src/test/resources/org/opentripplanner/graph_builder/module/interlining/transfers.txt b/src/test/resources/gtfs/interlining/transfers.txt similarity index 100% rename from src/test/resources/org/opentripplanner/graph_builder/module/interlining/transfers.txt rename to src/test/resources/gtfs/interlining/transfers.txt diff --git a/src/test/resources/org/opentripplanner/graph_builder/module/interlining/trips.txt b/src/test/resources/gtfs/interlining/trips.txt similarity index 100% rename from src/test/resources/org/opentripplanner/graph_builder/module/interlining/trips.txt rename to src/test/resources/gtfs/interlining/trips.txt diff --git a/src/test/resources/gtfs/kcm_gtfs.zip b/src/test/resources/org/opentripplanner/transit/service/kcm_gtfs.zip similarity index 100% rename from src/test/resources/gtfs/kcm_gtfs.zip rename to src/test/resources/org/opentripplanner/transit/service/kcm_gtfs.zip From 32bf3d044c9c43c52e794b036f453bd40f4b51d1 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 25 Sep 2023 12:44:07 +0200 Subject: [PATCH 47/95] Move flex test resources into subfolders --- .../org/opentripplanner/ext/flex/FlexTest.java | 12 ++++++------ .../ext}/flex/aspen-flex-on-demand.gtfs.zip | Bin .../ext}/flex/cobb-county.filtered.osm.pbf | Bin .../ext}/flex/cobblinc-bus-30-only.gtfs.zip | Bin .../cobblinc-scheduled-deviated-flex.gtfs.zip | Bin .../ext}/flex/lincoln-county-flex.gtfs.zip | Bin .../ext}/flex/marta-bus-856-only.gtfs.zip | Bin .../module/OsmBoardingLocationsModuleTest.java | 4 +++- .../islandpruning/PruneNoThruIslandsTest.java | 7 ++++++- .../module/osm/UnconnectedAreasTest.java | 6 +----- .../street/integration/BarrierRoutingTest.java | 4 +--- ...rg-hindenburgstr-under-construction.osm.pbf | Bin .../integration/herrenberg-minimal.osm.pbf | Bin 0 -> 187120 bytes 13 files changed, 17 insertions(+), 16 deletions(-) rename src/ext-test/resources/{ => org/opentripplanner/ext}/flex/aspen-flex-on-demand.gtfs.zip (100%) rename src/ext-test/resources/{ => org/opentripplanner/ext}/flex/cobb-county.filtered.osm.pbf (100%) rename src/ext-test/resources/{ => org/opentripplanner/ext}/flex/cobblinc-bus-30-only.gtfs.zip (100%) rename src/ext-test/resources/{ => org/opentripplanner/ext}/flex/cobblinc-scheduled-deviated-flex.gtfs.zip (100%) rename src/ext-test/resources/{ => org/opentripplanner/ext}/flex/lincoln-county-flex.gtfs.zip (100%) rename src/ext-test/resources/{ => org/opentripplanner/ext}/flex/marta-bus-856-only.gtfs.zip (100%) rename src/test/resources/{osm => org/opentripplanner/street/integration}/herrenberg-hindenburgstr-under-construction.osm.pbf (100%) create mode 100644 src/test/resources/org/opentripplanner/street/integration/herrenberg-minimal.osm.pbf diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/FlexTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/FlexTest.java index d9ab0d91ca0..48b66eb54dd 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/FlexTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/FlexTest.java @@ -24,14 +24,14 @@ public abstract class FlexTest { private static final ResourceLoader RES = ResourceLoader.of(FlexTest.class); - protected static final File ASPEN_GTFS = RES.file("/flex/aspen-flex-on-demand.gtfs.zip"); + protected static final File ASPEN_GTFS = RES.file("aspen-flex-on-demand.gtfs.zip"); protected static final File COBB_FLEX_GTFS = RES.file( - "/flex/cobblinc-scheduled-deviated-flex.gtfs.zip" + "cobblinc-scheduled-deviated-flex.gtfs.zip" ); - protected static final File COBB_BUS_30_GTFS = RES.file("/flex/cobblinc-bus-30-only.gtfs.zip"); - protected static final File MARTA_BUS_856_GTFS = RES.file("/flex/marta-bus-856-only.gtfs.zip"); - protected static final File LINCOLN_COUNTY_GBFS = RES.file("/flex/lincoln-county-flex.gtfs.zip"); - protected static final File COBB_OSM = RES.file("/flex/cobb-county.filtered.osm.pbf"); + protected static final File COBB_BUS_30_GTFS = RES.file("cobblinc-bus-30-only.gtfs.zip"); + protected static final File MARTA_BUS_856_GTFS = RES.file("marta-bus-856-only.gtfs.zip"); + protected static final File LINCOLN_COUNTY_GBFS = RES.file("lincoln-county-flex.gtfs.zip"); + protected static final File COBB_OSM = RES.file("cobb-county.filtered.osm.pbf"); protected static final DirectFlexPathCalculator calculator = new DirectFlexPathCalculator(); protected static final LocalDate serviceDate = LocalDate.of(2021, 4, 11); diff --git a/src/ext-test/resources/flex/aspen-flex-on-demand.gtfs.zip b/src/ext-test/resources/org/opentripplanner/ext/flex/aspen-flex-on-demand.gtfs.zip similarity index 100% rename from src/ext-test/resources/flex/aspen-flex-on-demand.gtfs.zip rename to src/ext-test/resources/org/opentripplanner/ext/flex/aspen-flex-on-demand.gtfs.zip diff --git a/src/ext-test/resources/flex/cobb-county.filtered.osm.pbf b/src/ext-test/resources/org/opentripplanner/ext/flex/cobb-county.filtered.osm.pbf similarity index 100% rename from src/ext-test/resources/flex/cobb-county.filtered.osm.pbf rename to src/ext-test/resources/org/opentripplanner/ext/flex/cobb-county.filtered.osm.pbf diff --git a/src/ext-test/resources/flex/cobblinc-bus-30-only.gtfs.zip b/src/ext-test/resources/org/opentripplanner/ext/flex/cobblinc-bus-30-only.gtfs.zip similarity index 100% rename from src/ext-test/resources/flex/cobblinc-bus-30-only.gtfs.zip rename to src/ext-test/resources/org/opentripplanner/ext/flex/cobblinc-bus-30-only.gtfs.zip diff --git a/src/ext-test/resources/flex/cobblinc-scheduled-deviated-flex.gtfs.zip b/src/ext-test/resources/org/opentripplanner/ext/flex/cobblinc-scheduled-deviated-flex.gtfs.zip similarity index 100% rename from src/ext-test/resources/flex/cobblinc-scheduled-deviated-flex.gtfs.zip rename to src/ext-test/resources/org/opentripplanner/ext/flex/cobblinc-scheduled-deviated-flex.gtfs.zip diff --git a/src/ext-test/resources/flex/lincoln-county-flex.gtfs.zip b/src/ext-test/resources/org/opentripplanner/ext/flex/lincoln-county-flex.gtfs.zip similarity index 100% rename from src/ext-test/resources/flex/lincoln-county-flex.gtfs.zip rename to src/ext-test/resources/org/opentripplanner/ext/flex/lincoln-county-flex.gtfs.zip diff --git a/src/ext-test/resources/flex/marta-bus-856-only.gtfs.zip b/src/ext-test/resources/org/opentripplanner/ext/flex/marta-bus-856-only.gtfs.zip similarity index 100% rename from src/ext-test/resources/flex/marta-bus-856-only.gtfs.zip rename to src/ext-test/resources/org/opentripplanner/ext/flex/marta-bus-856-only.gtfs.zip diff --git a/src/test/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModuleTest.java index fb05619ca62..980c33d6164 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModuleTest.java @@ -37,7 +37,9 @@ */ class OsmBoardingLocationsModuleTest { - File file = ResourceLoader.of(OsmBoardingLocationsModuleTest.class).file("herrenberg-minimal.osm.pbf"); + File file = ResourceLoader + .of(OsmBoardingLocationsModuleTest.class) + .file("herrenberg-minimal.osm.pbf"); RegularStop platform = TransitModelForTest .stop("de:08115:4512:4:101") .withCoordinate(48.59328, 8.86128) diff --git a/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/PruneNoThruIslandsTest.java b/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/PruneNoThruIslandsTest.java index 29c74d07264..53a2d60fe0f 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/PruneNoThruIslandsTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/PruneNoThruIslandsTest.java @@ -22,7 +22,12 @@ public class PruneNoThruIslandsTest { @BeforeAll static void setup() { - graph = buildOsmGraph(ResourceLoader.of(PruneNoThruIslandsTest.class).file("herrenberg-island-prune-nothru.osm.pbf")); + graph = + buildOsmGraph( + ResourceLoader + .of(PruneNoThruIslandsTest.class) + .file("herrenberg-island-prune-nothru.osm.pbf") + ); } @Test diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/UnconnectedAreasTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/UnconnectedAreasTest.java index e50ddb4f5dc..2b4b5411cf7 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/UnconnectedAreasTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/UnconnectedAreasTest.java @@ -78,11 +78,7 @@ public void unconnectedBikeParkAndRide() { */ @Test public void testCoincidentNodeUnconnectedParkAndRide() { - List connections = testGeometricGraphWithClasspathFile( - "hackett_pr.osm.pbf", - 4, - 8 - ); + List connections = testGeometricGraphWithClasspathFile("hackett_pr.osm.pbf", 4, 8); assertTrue(connections.contains(VertexLabel.osm(3096570222L))); assertTrue(connections.contains(VertexLabel.osm(3094264704L))); diff --git a/src/test/java/org/opentripplanner/street/integration/BarrierRoutingTest.java b/src/test/java/org/opentripplanner/street/integration/BarrierRoutingTest.java index ea662bdc236..e9b979e3b5b 100644 --- a/src/test/java/org/opentripplanner/street/integration/BarrierRoutingTest.java +++ b/src/test/java/org/opentripplanner/street/integration/BarrierRoutingTest.java @@ -42,9 +42,7 @@ public class BarrierRoutingTest { @BeforeAll public static void createGraph() { TestOtpModel model = ConstantsForTests.buildOsmGraph( - ResourceLoader.of(BarrierRoutingTest.class).file( - "herrenberg-barrier-gates.osm.pbf" - ) + ResourceLoader.of(BarrierRoutingTest.class).file("herrenberg-barrier-gates.osm.pbf") ); graph = model.graph(); graph.index(model.transitModel().getStopModel()); diff --git a/src/test/resources/osm/herrenberg-hindenburgstr-under-construction.osm.pbf b/src/test/resources/org/opentripplanner/street/integration/herrenberg-hindenburgstr-under-construction.osm.pbf similarity index 100% rename from src/test/resources/osm/herrenberg-hindenburgstr-under-construction.osm.pbf rename to src/test/resources/org/opentripplanner/street/integration/herrenberg-hindenburgstr-under-construction.osm.pbf diff --git a/src/test/resources/org/opentripplanner/street/integration/herrenberg-minimal.osm.pbf b/src/test/resources/org/opentripplanner/street/integration/herrenberg-minimal.osm.pbf new file mode 100644 index 0000000000000000000000000000000000000000..e57768e166b5131257e70f678e88d17974938e4b GIT binary patch literal 187120 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±;l#2-*zFIG7Bo_tMXa*V5PAAxIOS~P!OW-DakidBy1ylOqgRr9xR|B(1 z(_fVm0o3NbxaT{fc`he+g_TQiT)0U#Ud;bbrwbIHslMCZk#Y7LiKLEH^{D;3A%^Ea-P6QGuyh5xMQu2ZMiS=;c{ zFdZkdwF26yPV$n@xxA2YHJ8<`vr2}Mn|dbSm{Dnz302C{_96z^wxg_4E10XE^6ujC z?mw0PIdXw=I%6ZPSGSS|sZFwSC97&t+eGFBF|0KX=TDDAr*^8ulbru>@marz<*u+^ zTzqj_V^giUn+m#Vc4SdBt5$w=l=9&@2`V}31mZ_lA!Wn63;y8*6Q~SM5K;a4I=B5+ zXbs#eV>zgsz1{j&^_BG`XaZOH?Ygrt2qu}5ooW;QtZlJ}DEH1dJ3+Pi{F@)yz!YS* zieV`%hfxqYC-5ilh1^(G+0>bU#mDm5qr`1LwPmW2Oc4I#_U(32Eo>y`*}ux7g7_Yy zNw`Tx8m3)rCTfUEp&j&-nz2ofHJ+)Yo)40^pnffgM)K<*I1l59{=40pqpE?x zgj7HL^!~$D#J>6bT0IwG^#j=%&pmc2X{U;0fNDf^DW(F*2$3JlhQC0Vv5k1igrDY! z32Ao8;lZ@wE_;`X;|CA{svbNq-2j0PtUSN_RPQOi1>wdjN0M7g00+|OT-q1o7v{x2 zFyqYEW9%q#~dn8)eN_JrLYrP5gyOxCGrMUtzfxICpTZT z3mvF5whu4nND%45>V?|*&`iIX>jK|VPvB2$FT$*6%-mq*T`OIio9m^7+l2&a7(3we zTz(M0T4T+Dy?oV3;%9g%>cQrj#+PLLfD^Ua+jg+?)r2ntE_(Y=4gs*7v=iCDLZqrP z9LT;AVpG%Oh23wx^gKwP>(wV8^KGAA`J|BV!CGdw8kj*uD;T5S{=9J`YySOy7h?El z%c@adjBZdJL6r9JYfiS%PG@Blj+mpv(Bcsb-Q}m-k3<}CVO#NwFPX8Ew7gE{>$wmy zmoFHpJFoFiI0@9nCgj6a87vQn?Uitt2ooN=p8yMCH|25!@f`6xFSgi)xb4G!>_y1_ zCR1yM%RYqBmE3B$doo5GBsDrVuI^}q#<7ZsdbIho zEPGZi93_AE`sUdyUO5>-G-{-gCWJS16IY7@C@0QG7Mg!7$sn4w zd^puGLQu=AWXG|4`9{1bb^sh_MVgS+24=*%C`;W$(61V%86@o&tX>}%rCsh6M{vU4 zH77mJx{03p;CE51a%!54qh$O}RtTq2XZHE^Ppt;vs1Iz{9{>5~k!E%$yOLE;4YEAQ zQnD7t!bcXuz9FJiOQts}22n&k5k_spQ53iT-`?H=KCYsBAMcsDcklYfHriN|wy7I+ zpry10TA(cyD9#HMcZ$8p8+X@@Z#M4k?(WjKOXKeL+ek~X_XWQE|L2p<-Z?XKcI27o zoO9FOWm*zjQqUYRL0O|R$t6?;XJ;f^Sv^%Kf+palFzN|6&h;_+3?-DyAk688j3Uw; zfGW%pEz9 zu``UQl6ZJd>2X~(Q*3%j!15#8A8RLOjZl${=xfj#-zpI!Ro(klf>SL~M~a0Qt`t3$T*cis~KZ3RBr_DKqPaDOLEhy#X) zhw#NIF;;_UZ$21-@4yfW6R+YaN1{&5%@X@<;aCwXh0odcs3HAiNj_{!gEg|8|+)-U=N7PF;LL)PZs^{k52~>pAX!Mapsr_@Ib}0t?UVY_o1ju?P{q=o15K5+G=FTiEgZ9p}cS?p-&Kg zxRF-_F33`$l`11vi>Yp!C6z$4M7BFEX&GSe1(&SPw@Y2^B*aIXsS6?a;?^LXBaS6p z$m{@=wWlHqmT(k2xW!r=UBq38**_=Qd?MvckdmoP9aVqGClZoC^jHGtR=}I}AS6Pd zO1o+)_0?&V8Cm+C$9uh+EF{R7C{GQ#1D2s>opE3*`RATt8G z!~xVQw5>vQ1EhA7Z61wqXLu`FRAy2w`}!UgM)psP%PWSPEbfvp3}7VqxC-& zt~IQY?gPV;4YX1k#}hAu_P%z75LzUezJ2+a(T(DB&acP68Y|sig7qJZkk(dgm5F$$ z@$KF=NXdd39SE?IEDj?IXKCK1DE)HNPl2~mCs1G~|9ZRCV&ejc(9D zPmv7IVb>ok$)yRt4!aV%qGU7ybCo#xHrQxJtInzoZm+(fBOeim9G~tzRrQpqO3js= zItbg7>OmO>qTD8^?1!XQ!t2|Db`-7;Gm)hC6Y(D}J~#Vf_4|>R^C=sC&z^CX-|JzI z`|UK{z4YXhp(QMW)-ySV5ZOUr-tQ|J$P6XCkQdnCYMQ5#tGdmh)sutg z0#BJn5p~QvBDyVWh5C3&G4bTUXXfXddm-=vPc1WDDE*<&3WbXxz>rqR^k>xO5xRy@ zL5@mC?=?E%FA>_savaA&$xvbh3r}@_U*!ZVwqP61DRn|#MQB<87RTZa6bVr=G42A{ z9+I;~5N{1Wg~w7;9|*9)LABeO4CsONtT9t?6UvYBt2uhA&MJxTJIgoiGE;DJfN8Z{ zJPJLp*%1rM-Ad6g%InuEz2Kk+eVo>Pbw|r=p*bFPN=FA1Rj=7;c^~PcMz_UEnMw4? z<$lxlp_!_|M_tbw9?b6tUlGKAYnKT6V+W4;Q~-&<*7e)#gB#YKjeW>g1eif@FeZFoeDz>JLWm8O^dM564j@Y%gbIBp3gU zue$h*XK*ZtStV~PB1gSduKl7Vrw{~}{K8PNyp;o~uSdG~p@54~H=3_hnWM=uqUS%& z!9<^wemn>$-5rtCq5zU#i(Ev87mX{xa9gvM>k!rEjAKS1Kag~N*wOS$M1%QnqSooqMW+C)&%VpEspdkm3U-43{zg5sj*d~JWBpx<^#Xe&4OILB} zfwBdh5eG~3X4;sI^2E)0cZiXk+vfd7eJ~0Nh7RPlf1(5Y9t@E(|1SHPW)UvU;(GsN zxePHp+jj6dFc=8gsLlp0m(Z7Iz*(h<7P~iY2DioFkbYd8z^)W0k%erB6;2r8vcIOA zo9_-?eyH%2W~(zY_7SIjnGwi3iJ3+;?CwzYQxf<=S2*;j+mME*!;cJIHbKKjBOaUh z!Y>qb$?z$%^=OQ=YYBZ!ZAcdw$rI#rLTpZEnq&X2|$KxvH z3Vk@E9kdDqG$R#b4Q8WY?SS%qxDgT5!U|=5%5b+4E!0>HTmKQt(Tb7I(lf*D#n98t z^HWapL!-k~j9g0vwm9k>32m-;L>HC1qmBS_r~|tDA;K3Tw9*c0%U<*zB|UG16=Tz< zQ;bl`k&!EHQ^@GF_wHn#jgS}&0;`wuA9pwux=Z9pIIrNw%N4JC5!td8Uk(_+$)%gc zd#l6n)QvlNFi-&Fko#190`^r}f|BpG$?h-BCP4RV-LHG>P(>E$=7)RJa7rc~9EXY} zq-O*MpL>)e^JFkg2CrK^;ZWttsLr5fZ=<@8{A7I+Aq84KuD#fq0`7;q-nM_wF9*l& zSbip4%4*-VF4P`3G(f@^w9mJ`RB|rigYF+2C6Xi?r93#zOlOT?$);`2YKgpvler?O z(k#|SqML(`P%dF_55BzaL@3`=+CW>>2M_RgdO8oZrDtCdDqHi5pRr+A$yq<)rC9T! z>z6B`(5pWFao;XW@zF=+=L24}Hin7>qL|b)J%*xw-_QbkEBZ&ywTtc{x5f(GmiuwML0oLmzj4!73A`Z!R7-cy!PjeEq=t!|3y=TiuTZm@wPJP;Uenv_WDr2n(p` zQDw*LAt~U0EcoTDdgx1F8_4&Z%G^8RMlN|$)r&ahz1!`mk?4+KcuS=C5`A8T&j*o- zq@GF~qg)TUJS$T4?&su1Rh`FT8*`|BP2WHCM;;mCRzr%i5TbI%(Mce9ilAPj`nvvto%hSfxsr`Ebo= zI8IUyj|Z#U@g`1sSxFkUf~f;6U&x#yv`_1+6se^Xg^!x?0z~}mPI9V_^h{KUJXc~u zx*hF%z|fmtOdlu9_cq9OM5DB?3ZBVm1gi*+O*<}@&smg-oKWfb)?A{T4=iCU5#&d13&ZKmQNp~vZ-p?B+iwN=GkZo)A%+Itg=_dDQC=cReFs2LdtTxkD5sR4&6;7 zN-0++lV9zlTF$I*hT83R4_TacJ(9iC`RVxcG1ippvBI61k3}^cvx`2Zcc{#cOwIyl z&5!_MWeU8KApf+U9u>Q*mFS|U9U7DKlnmeNRfJ7(Ee03E!I7(Y|Bhl;E{adbJ)1k7h6(`JEH=iuY0;)&6YmUSp^ATY__<~ypDn?DTOqv&?YG)PFVcANAO1iKenHAhPNdZ*tYv;`<*(y31p~5?$ zeQ@c>pk&Sq=8Q&0Yzxd#5u~Adq-shjWXUWjYx7)at#UaU8ALrY;+W71kvaX2x*L5A z3)H81^Q>wrl+(|&zGOpp#Eps>*F;>wu1HtZy3bMGKSaVb416w$Jn0QD1%4j+*us+y zKa_ml#@3PWWzF+TWh+?)47W~t+M5pZ#s|W1a^VXEA^(3-A34&Aj{hju_t7Ac@&y? zpu-Eo4|N`nmM}df+L?IBr;FiKhDMK9A~&8(I28T6X#ddjivcjVHDder>0T&_z;(=u z{Xx>RLZrI_CgzmS?jkkq<5Tj^lEr6-w@1o{zj}Lb<#{tw#LnBhaHjc2yC1asKu#FM zd=Qld9;?XNjFXcmU7f&+mU5V9>Ju)OXcF%Dl$BxWgGfk$# z-!gl*_k{bUDg*H1nizwK8DI&%S>U1%#hS(T5U-juC07wnCGrX+N*-~0NTB9(ApfV7 z(}MkiH+z0^(da*tNA>hn4S)Vt^2K=h%2UG+MBlJWhU{<3 zxN%>&(0Y%hZ|!{0NvZyXh3v#p8-C4USD8pJ(yNUb8-(l}sXFi(L%r?bVF0=Ouw1nG zppN1UyX1N>BRc^NFtxc!c9i2w^WBtFAmnjVBtvEAY@j^~c{@;Z7Mz;P6BnB_?>v?B zAB&H4BOo&m)mzFn|o! z{*)el)))MyaD^l3*aiYUH1}*nFe^C(oWii*_m!oP(}NwJ4}8h<)0UfhCt{TI&gBfC z2=$Piw(3Kk5XqlC+oqLiN0z_mt6aRdKrWyS{AR#Nha76;ILVo*)kxXTqOo1A@-I6O zLud2UTwoUIVZR~;aSXOnN+j7pXD7IcU`3|MVTaZJd^2d+TCP?)#vWInlnLJB{9@`Z zg9j^>8$Pg}q6*CTE07#38>K6orj@`cAg9<)`E1hOoUY3# zM!C3_9Y%9tFHq^)tT+pq^a7&oaz`q1Y(%YAIz74y9k$5cdXd9J%p&7hoV0xhH(b0OeRjWiun0!A$pkrH^&DgW){Rk+GEY zleYJPJa-5>y0p*jLXLt+&XCj16mUgyz1!w?3MCD93Rz*S5N?LT(hbq;Q%Nv?O*aQ; zIKbTdFW~BX$~p;}F93D1`xL36(-bNEH#rwlKbD@m9JoM8E96C!XWW4U`;K z^6?|`C<`it*((`GE=U!ruk3KqaA63QU7}U(cjnHLzK&qm0Tv119fn=FRf<*e>0h+v zNe14*izr#LeMiw&(cYoQij}ez(vIdUSn!jBVEDnxC##O6zGSeyMcVxlH`#`_Gi3Ak z+mAwdF3whP(2RR-fS2UPThvF33s}zo6?YFXS38)x)2!%h@?vLu4e#py#5qm}aDB)n-I2^OKUb_^$4CgsJ* z=laTpA9Q(VR>S{YkuQ`#YZ1(7jv@@(C|i3ddv@)g*_K1&w?aQ~D~0YtaCvIU28N>O zM*Lg_uX;}J!@EmfcK&S166UO6dIc7=M)yY;(~Q2$8;8(XoSN?{FU1t%3=fFb$GVSAla-Cgu?j_*F??X))<_6P zs9E0&6FXXWbf5HpFzmV5v$5D+ruBMw8}exYVJ+w@xuOnCSQH8pVJNUpP z#5m*075U~ONr4mD`b~^Ps`RKkD@DkZ<9HEOe6x?bO}fwq8c`54OfGL8mcA5+f*JB*vToddW+Q1? zOXdbJbGHV}LCPa#MdY1Jc02SRcVSySSLvzb!O*76DS^r=rO=f1DGC4eG!$5E5rly)pKT!7EpS&ql;xvjkjr z%$ag{S*SiU0v5L!mFH4pQB@?=+$Ia1s46`q=1bdf-fij1|4fr}1>m9VMEAoMXt+qB zxEzJFqtcJP-w6r>Z@y96fn+%dBj&YhkDHDt69+v~Xw;=ZfAZ8g$UaRqrD+kLXJ1vN@ids2_c5rH8@7 z8V@nYUdegQL@VV9K_EePfRMbHiAJqa>(WIP*<9KFD&4oyyB!$;k>4MgHn zUo0wz$zJHasx6fi2`;`w*#I-MgxA_jge@||f3R8QMtJjFvzVQY%xPmHW)5N^A^&;d z%dz@kbSP;@qKGyzK^DoEr*7@`l(rN|7hbgpkxD(+b;R|QgHS5xbE6N6X9+)l)Eq^3 zTb8!4p@%NW_<2ZA4@s?@C~CtA2@wlgQy4$ZL%MBIDklJz!m)85Z(g&w z79z?BlL;hd?2$|m|2z2^KZ)(}-B5qfiO=M)-}nXN=C66W8T_>skO*=qjetVC5eQGFnU?ovyW?7y}4 z=ixZ4v&t|uP_k*HC4A`1E7mOgM9&5y)hDp!)01aJ$Co~{as@Muk=M;FV47S0mOjVG z5?Xyw%jb8L?E)Yz5`sQ*k{IrUh%kuy#Q8|_zC_+2E&O;hE=z@QMJHa%gNr9WFg;kj z3X^fzV9&j6&a}*CI)q5J$#!riE+unQJh3S+v;#aeO`d40WaLbx(~*&6Xrs!G>Q6=-O!cV;3g~qSZW|_=@nz?7?QaB@wDUZ6p~>rfD|c zT;3HYHJAx;H_GWF;LV-OqR;JKft6fF`XbG*1k&_DSVzuh5k*hjd9qWF=;^?BRKL6T z;K@pWVeTGK7_i;Y1_*!4)DXe_>m*B0|IE1@#*HfSMtpZ`PFnYRG!CA zD&PmU@>6>G(^dSj4*vX%e26o4>){(hXr4^x&cI_~)iQbw()#Kv>bBm0I8uFX(P!M zqN!RYNRL}dOj8{U6J()S;t8FC9bvl2>{idJZ0#bu@5>;v`zJ%qHr^N~2d8s&*$GS? znl9U>Te{L6+iQzs=hP{v`c{-L&Dy?7Yn5Md0c~>2zJ)lH(?F)l> z$E$Uyix%`~nGb3RL9H`L_xSurHO8RxMXRdiR^6jTCWK!eEpKa#O7`KhI}lB4iN|ie zvgCO_e;%0UL%=&W$>i#*!>pjEJZ8Z1*;dW&lRp1)R})DjYEICD zcw*A2%!1CBjYpCFl6+-5^s|f)mN|kw&#DT-2Z>b0nRI5__T*`ml<2L7`%G$NBW6&@ z$4lD-&lS8|{%?Uh6v?|BuD{`Vt4d)g7DS&|HQd}}qhu{l2XWGu@oJrohsIas@ijm{NwXtC|3hB}9EYm<7aJk4AhIEiF8bX~kZALGv zj4b2Fsmz%xI=D;UVc1AiDB4I2)smjc%nlbqdyp8`WljZBJ)yLb5%BS$_tn$v#u%*; zc#-eLwpVgtj>?R3O{f?=bKd=g1yxNOJUPm(Il9c8J>p9r$C-?zuQGp}uSCKI`ThjC zrD)W0)kch`Z>HbrP+2|ZIEFG~p=NtdFg88!Vv1svOlV_i=r+?p)9Qn2E|3Bp95h}_T->pU!)rhc>_ds3Xx||*eS*j zVk6G*URi^$tayeGyXeph%jt1H#7lI~(9;XS!I7HK7EdgSZ^8?x6o*u)y8?UZCfo1q ztT&>i2GbJ42qyy^)Q@~=LCiMpN;Jf0!GyHGg6++I6izRb6Fi=(10^Q}9XMQ@sJq#9 zbXa3>ca75gYrPR@HWJK4L`p8Qlc+02L0yt}qOp*#%QK=|UJ+>6<7^$Wvj`8-%q0W9 z$l6xw17Qx3NCexPeWT^&l@}XmV=_q$56~eVt9jX4bya0b#Y+c- zua*-T8vUngX)Y@H+ic^Bfg^LLa`u=RLZCO&d$5%j#|mnN8r~vV%XX|_O^B!k#o1BD zqy=P|Kw1yI>!D66e}R@&O!WuSN)#IEU}BqFC(ba%O{5J!7SAiDuoo>T<1AwA*{Wu2 z5QR+6)ToVnl1Zm4pX}IG;s`D%3f>(!I?h;?z9SdK>Z?UT`K+w-ZDN1^VV;J&Hxv$X zGK6$NJsByHRDwp0saaQ=c>$_tPtAh|ZR(>A-WChez#p5Zf!-~TSd`ukWsazb7EkM8 zn{V~!`(#wBP8U=6N>6 zytKBmnE}?bp|ta9ZIaFf(9{XH^O^b2=^m?QHNRMSx^rjFvD+7N?7?rVr$jL~n3Iu! zPu;bPgl6I{SXp91aQE@Cb+0knReXeL_h;WRA+65Rz zO=(yt?QpO6FB$f`}L%#|_6EQ;8Im4dGKr-x|3-P&WaKj$~~oEyjVHbfz{H zbmNIGWgSywI1Ht#@rPG6>psyFLQpI?#6ldW^1=!pQOw$(x!+Jr#|n}@EYt{At+`;h zWAur(1M^h2YL0U2JGGy$I6$%h($yy<1$i)-_<+?QIKJaE+$##8r zTUuBI#r`mrjtu^t-ULE%HpYtUgYQKTkq@D}0gvA~-laD%N zdO!7SgN%4^e(TQT&N`4&3ZG0q5UH3fZ+$gU-BP~phgmD4o}ajP=EOG4-`l;|`i<$A$pRE_PZnu)(gN8Fq^Ep@7uS<; zqOEG(*)@_u&pr~dGj)b23nM-ANH-(epf6Fm3?W}-$lqqnXR`Aw$v95GVvLHb=8Ag$ z_JZx6TEXD6*Z-j7zNWo->bIr6ne+5Et`jSg;d#j`6F2aB(_Iyl_X2G-TgP~HezDqq-FfBy2elQjRdIvj?gv!UMOkK(O&$uht-wf%8 zvhVB6A<>k0dHIBg)a3Du^M&g13KI(`S0h9{Sl#<4|5B87aL@zd4lJvMvQ!hn@Q~Bq zyw4n%R`0mHm~edljOi5vWjjkqNPzSQSJfw9s>*|=7ZY!V-5mMU1p>b-`g-~^kMqU! zI3)dfc@ zSY*^oPg}x_s>gGEpGH}OuN(N&f)vBvoPWebBUmx)@xUjHl`04wWr5a!G6F-tCb~J>3zAEGv+gEle0d-`fHc@Z$xDK+I0vFg=94 z38P>rD}w3WZxqd=(H2~F-I=U!!rHs?EeQ{G;VZ_soTN>+_tUkT(S!hJ=tJ*r;jZui zmU295jv$@KQHM2hOva6q#H0&-DGuypTMMwc9v*C>i)M%kPo(FAM%+oSB(g!t{c;~e zHyM{_<7A?u9(%Q5H!p1GiQQ7MvFdC!C0L?qOU#>NQ*+WG0$CLx>m%*d5YtExmylWC z+XoPKt$6Zk-SZXaGaSJ;8rr9!M85kDp4S*>E$(+3W9G+0gCW@3QNv?Ym^r=047+pe zPPU;+1+PBV6jl0g{?@Q^vVmto%RQJKNmkJ7LZEkFcnB=rnxijjBaeCrdAc|jioOwN zN?9nA-*1Slr_l)W_VG?ak2Aw_LFo)HWGc?83!u>s{N5|k0}u5HYZ!ULVF+gyp;}7d zORN0C@K}!{s-e5#+`WS$v^0x?7;SHPoiYrES~FV98~*=^uY+f%?tYJUCK>|Hjc>o- zEGpR`8EMW9{>8&jNxq?O1J~-%x7 z;rj>v8+q-|K0E22A%@1E?C}@nw|4JM*bN-8E2w;Uzs_U5{Bab1KNd-zeuO$ts+(-A zn^HIut0i~ud)o=uf&gE2#`|3`>5A)M&!g%wNM#Uxy0Ec$YKP8|$NtHuN3G4{_%BKt z8ksl?7ys!;TK##`_k{P{C(ki>ue@V~NqLIo?8v@N1+}2oi&jT&JD8jArp$MiZO7OHI#^ zGSUoLj-hzUJ4tzRecI1Z_jC$!JE=|W-7&1!O~HEmUm{$*dYnYG^><;-^@9&FR5xPe&Jn_dR;udLO*?4o^e%36& zCZIM~)=1pq@OLpd7}hN1WAc9kYi-NcmgsJI4Vq=hSe#^#oK#Rgv4PhLv<_G?Qvc5j z_AA*Kxgm}Lj{e$wDfx~0ZQ$o;J-Mm>BYEjh9U-L%w2WrZ`?+hbh7)Xy6)%GCJrsg zUmc0{R=VQ#Uu&_>5lW(sX`B+_>a`E|!5zpKmy5mbdc^a-J-Rm#Z%`??HxMN|^nMEZ z)oku;%%)U4n_{tr*XzL#H-JCB8i4l)VlVjDRq-m9V)Z2WO;E6AvIp+&h##&HcYAWZ zTRf-?qu-4y#9eIfUu$=`R*xz_1;~qqUv=F&{YduyN=%DP>Ky{IhCZ zHpxt_60N`P262pyr{O+BzPbUXl{J7pwMkf;RqGYgzhSn;yS%ktn)yY^4BcOv_(h%e z-MU}%>w2x_-P(JarMtbgu3u|Lms9VdagFu1{8ibUr_LJ5KDBQ4x0-)SlzL%MJ#ZV@3yExm($_?GxTRf$@+d)Gl6Th=yLyk ztfuzW-!SA6FU!BzoczVr-HSF?1AQl@y9JYqdMHUCqS}$(?{_mq% z^QO6I!=aj>O^3RJ?gn|tqEPufB~3@3{)Ux5f8MFA9W7M6RL<#7PDP zAq#2*`ozH9$phV@I3Wy7@wbU6d=ccGhA zIdH8JUCxC2sP#A2#X7Z{oDR544u1egK)AncKmJ+ym4Dn!2F>wb_e=kqqwj&$cP#)E zE0nx8Gm2k_;-7DHQ0n^4$rH!MYZ=bS-HP>8Bm`5N*~rLWK<3&+rx3Me?e$LyPaFS@ zxa_~m8mnKw>w~szx$o*v%1;i!7)ReW{_pr}E&czi!~4hN=C6=gc^8TI9f!>{o>Km! zFWS3ljDKNht$bJRyYYO#`u|(`&2HTbO4csKuZ^E|`3G(u{<)h7e_ej>cKYUU|7g@l zUS5mee}4a8`d`=ob)f&QmERXJMt-No`c(c$&%Z3|M(>}?`u)hib?5FED`E_9Tf-gd z{{aq@aM1t&01XNUPg6}qVRT^__NxUD^419&?yCiOoUD8aKorNeXm`&r49zO8F~&^1 zxrtdohF#3fy&Cr=@o8?nFYo@x9hiohFwAg!W+eLZ-p3_~JK~0jJ0fnVsHnILB5tU- z@5Ti;+;NTKKc~8Tre_$``~PpK?y6I#PMtbcb?Thznr2Hj(JaM!8;r?@bloskF^sda z-c~!Gq)X&jrCFECvHnAJ!_3^^M1zS-=1i=&&YEV>nQSa$wy+8=C7o6OloW3@rdrHU zlH-ROp=3Z$-e^rU^tUJ3hCnG+ovQoUn#OT@R$(&QSnni@#fJ4MlYZ{pne|RF>Fidc zmG$L0t5MIHZAKkblweF8mT2NwRu9l*v(5&^RTeWx80EPnmWc=h3P6gbwXCnvoMhos zbv7d~!bF9!UM8IxT0@7k$~2wLz1c%9J-4f3+uxHKT%y_@D+&Nduml4vIU423o# z@=(OJ+APSw-*9-u0rjLLVsfyx4RRw%)qo)(y}R{d{ZlQ_xcH%*!HDwr zwxk$M##B30$tCk#GU*5}9)^sKRVVTms}&c=Fm(}Z`9X1(lI z9aI*Wrn6c(b27&h#;hz8qQ$NXk7d2hoGsnLr?7rfjhrnJxa72^sBqQ?vv|9SL%yx4 zAw1hcLKDY9e5yqcBSnHj?;0(vcRH6~HA1(0!3cu3qf~81voIsPZ3fFm>5B5?5r>n&R|E9{K}$PzeyWwHpSE0i)3M11x$y@E}eX z7;rWnZ;RLCv;Z9iGXmzTl2p$+M+NI+|3XtfXlFr$TD)rN=0hH5(# z^zbmeAx0!bqW%aGs7M_Xu*2BKK&#YY7CZW8oef5~d9SOlLgG8JFXP3vgf|3X&KP4+{y4fPX0- zErUmkG{v|BC)PC{m5}Jx_20<_c5{``rS%pVJ!y-FD zM*y>m@aSl`*Ls^A`lQjf%efcIDe>l{P!x>%Ax)kbOwz83KD9t-zyBn)@pB{C8VMn?drk)2r@ zSd0Q;zzsS+3Oa>`euufJiWaERFa%;?P;LE zDo0m759Vptq$I1YyufB-107L~HZ&$BRR-XA&G#_#+B*QhbSp1S0JW6NnSc4dd`kj} zvFfe{3vW*6lG&iHxPXJcHAoe;?rKWlcw0z+3vU|^=plo}4F9es8>i`}(_3wJSnPaB zaTRTizl8nH783B+o5@_xK^yYY& zV$c|q2!dbN)KqAi&Xk}t zpb%26oC$@{s;e0m;gD{;Ju$`1@s1V;bj4+hgW5nF!!i&AX`6vd;mi_*?F#w4@n@Wr z0bT7$8aBZ|3K*+(6{uJ0Vn)E5<1!-xpUa>*Af_K5Sp-X>_RvQabkR*f!MGeAD z#>Is7m0}v049*0MZvSq;yTOu#asn~6cP67itbrXb27`98F!m(V5cO=G}C5(4;A#b(5`0x>T?lIQEln z95mVh1|Dbb0zxk@H7D2;4PCy2DnkZ93Pu@L-deybF91Yd)6awg=Am*B4J@q&%TTOX z6n{4^85UHBuzkC6=HXH%p!2xg@*1hPAe%SqH3Pr|;@P&{z|=5+=sDhQPS$`S3z)z+ ztTVvB5kb&zI+F&BLs%BTJnUxVp<{741a<@4P{)HkAyQU%Guds{G&>K{=XHiutCSJg zjmP0=wnzZB8*ee2g+BCi#x>Y)vQ&giW;j%UDYP^{kYXA;w1sqM0+)mfd@{6yRduu4 zk~Q7MXAkk&6D$(Dt}|Gt-9Y3*BUqdkR$+ER+)ARFZ)EZAvUm?!yeIH#x4}G1E-$d6 z9s8kt@X!>4x)et0OJ)$AIy@@ zfTC%HBBv7mR*V>Tj2Kr8ZK(FG7;mA`1uPA>#)OAAYD_GkyzfcFcM+yC`yNhMjTZ2_ z^s}d>iB*wKdtb=vtZ~T;7Xn-4I&ctt9CREwa*{)NN2Nn~N2Nn~E!)~77?{*}&TvG-9Tg3?MmU&( z_)t~_u3tEK1OfPr==>$PGT|Y>B!NeU3GnDJgolNIf7nb&1MN5fMTbO06I66GaleAM z^I&=gbk`YEWp-;&cO7`w$+8jiitY}Mo>9{wA~Y;QgL_8_uug;FkPfyPF4j&{C4NXm z$dH)OG<{MB4IpTS0^LLdOjR(-Y+z~q*k@o!S02{+p_+~cn=LJ_QzvkaLv;kig4+3^iNKiTY4|La3hW1U^5X3^rV-fYC{p zX6$4IEeLiF50Qj4-trUpx}BmEJ9A;MJcmTbMn{B1a#6aFgfMM1BqKQR2O_zoi11D# za!(`p`9B)dK0^r&HNx7iOV`Z{2H{Ee&Vcx!gU(@j^)x7y^9>( zsi~bx3VI(z-{E^ZpW1=Vp}QM7Vhd@yfyPX?^LkDLlOAd^f*;LmdV&Q7ZBOA0u)^p$ zyG^j2tQv6>O~WC1u>X^6#6B_TY*5~a7M#%xwy#;!F&V6Fv_U@8Bm#rxjq7ncs&>e6O}hhkXAj6<|I>j0j5KG z>d^Nn-;$Vum|)PNzu++OdYI5;6-$9QOoXjoXm4W6powfi55kg7D)W91J_Yhnny|WS zdJP;5Mt^8zC~?$%dRQecI`**gM$Hfl4_3GajlNWvDG>V^=%iUpBtZ;TmhQ>=0Gp!4 z>Wv(ueb~7tZg~jIq#N~SV{!^~4;s+GW=Bv>nhx6V4xecTp^8Ix2HWg5TQXY35pORV zD=&$Q6x-LOkXbJlf+^3DIp6U|t0qFzAsPzmgaOjk4laX%7q)~TJusYzZ(s!b56`qj zOif47Sq@6>RA6D$^n$Tt)d=JwoP5fb;=hv`_&2cYkkFSNMD(R5fitF}9wRd_#b~mm zlA-%QkzrqUX8--&sK>excj~$(se>l|hLE;4AsbD{{*I1?X=p9q0+YdNBa@GJRK@=C z79v91wXWu?(Zv=RW|HrUCojTr6-6?Eot{j8SQg-Eu$YFk z@Abr;Uf|jg(vycWoXOE3KSve{#U35+j~0F}F2T;*tho81=^YBP z2C<_0cg-0h&++NSCF!tT9NWGZ$7A2XB(tC%bJ@(Z@T?!?TO4~4-5aZyl=3ZR@!c8G z1)6AKT!JnzTX?-=Y|3;&%sXl9R98KVN;>*Y1)D4v&USV$mj)tF(j@@U4F2w$^j6QN zwVuYI_CP~G@f1s1Qj_z!%bT?)D842&a71IVJC-F>YKYs({%3G6Fn~-(+i;m}{%NoBEol3I6&6PXVYehekD&1eJ=D*L9;g9QYzrr% ztoZ-x-RXyr-a31lO_Hs}VCFPGSj3U=B{6@JJg<|2rQulRJ(wsyy?YD_4GW77)v^J- zbqR>zu*!O)SxZQ;G0wQi02rWPYumt%dgBWDzO0}w)m4+T&3mK&Z4%~kzw%Op8Pu{r z0HLcvq#O^)V3HIK+HP32jg+&7^yW;dcAI8!nhtuLM|MHsrh!E_ob>~NIjz(-y+Ncz z!{H|C7Ta)#%25H0WZjQ!AUu(n0bhRM8}DW?j8b)GGgJcVj;MWBHnM`8agCO2WVDt+ za2MZ;bgnl+5PQYFwhoGAe)dvZcAD203DxV6Pn*f z1}3wDyv@U%kCyH^2Av6n93TV#_+2-)g***Dq_6_CmRnYio`m_;%9$Z;c#~aiAAk?H zBxBi@o}1UKahsa=Hu6^74VUj^`yx4LNxRBAf(;-{4o(Z{^Yecv)p+7Vt&%Uk8zGw1F0r5mqQq z?>0!kH=W^Xo(Kj^0Me8ViT-5Ut|<~--j%p$9`$=8wSSS=NV<2Lt1d=IzJHJIA%jW! zO`1@qr&HhTbi};b=lCyo9-3Sa8rRdbyO}JBDQ_?OnOo5} zG%?N%6#e;*M%#%e09sX`S zZlrK*k9Q&Ub~1gPWQLQirzTmF+S@d(p6^5pJMYbOn)NO(#W#&G^Q48ryHB#Pf2HnY zvKfiH5!5GDGXQK|6BvnR@|s=^kii_P{Q!)ihD#RS9s5E02Mds}VlOm@)DR;YbDCt% zng;8aEtK``V@|ec`oTh~v$27Y;Ej4sZ%_gzBbUIw*T-yzr3y%ZX^5S!i3$q~(Z*@R zS(ao8KHi7UxW=i6ov&}%u1EShL8lprkFDuxOt$lEKtFtK(!&lc6BDX6Ne?iZOy#A}ZrS^l zeq>yvqpyyhc|RlX@HCxqO}I-cO2h*bMvpw11j8BaAQX*10<2DJwjVe$FvCrnKCG`Z z-UkYTgCRc_;@{7n0QTtL`t<3e>D-z1>1Q{HFMU=0!1IiVL=1j7=UB14F*)3x%!Y`O z5Sh{Y22IhrAM~n$3+ZPunbM6iv(&dAIC7{iPyj0NR0BREqQhC={{4o8{AfpIWMkR> zoMR6opg%{>6H4vy>yH%;7xs=>|NdxcX@)>Y;dZKTe+$@tyr#EZKa5rOwpZ<1z zx{+9GzWwb+T>Z!nu)4n;{QMX#cnJONw&9wt*mYX;Q(ElHL}|fSp9nS59v1KOHF!udZ;Pc6q1l0s^>mqeV|A6 zWG8|3$MqQy_l-vr){bBkqV#;~6=4yLL+74DLXa7jhFq@u-?)EeX?#cq`~ z!N%FG!!`XZW+Rt4+&oMZqDioT7EDaE*v&RJ01#{jqt2?qy`vO1Xn^kLkRHQq21l1S zL&zX5S$qay)PZ_GuftGNgBjcw!U$PIBvL=L5Q(_Q2aoscCn zL%qM%rRd0u2Qd=Db7^=Qx5c+Qo1MpVmU5*Y_$?U4np93>!!0Ntw8*y>v+SUakMnJl zzZ4brqP)KwJRlx4EE|maUzaMp27;9ebPmgYhsNFau#Upg_MOQX6A=~_$+jN&$si5B z9Tv97oXu$4M24nEFP0ey{WUOx4TOB1-iXRyU=0DnEP|qDy^d`;P-i!V^b+0yIrldM z1}5oD!;>9*BkF+$lMxht8p{qe>C(~!CEx?zfes9M9Y}7V1^qI|2=X0haq8p1fxIOR z^{PWc{}!u_(8l2LLS~Q_LNu!$gey0BWi?2Lqh2%IE^Ym4I%#B!Ztx&jq8tmMP{S94 zpeu~n3X{!67ke@r{RZi5$y@^Z?!dX2BGc9GOyRP@dP54uOTZWrol-I7XwD$cXb`kk zONgRU)Q1SWl_3L(Vrq_+gM^8I?d9`?&1DHVM3*c-H`k1CYLG9wtp{0z{pUo3o$OUO zaY4=&)Y1-Fv>0T8X+!*Mr%}SH2KVV25gx^=2lodh3`P*E96Y3FEE_EF>GHnK4`E#S zyi|0}9VsvgV7>lyFgX_nUWdVC`%=RhxKy2qSkR=U8Y}Xi0CEi^h_bb|x$N)_5eoKs-3qK#9m2zlLCsY`#nvPxVHVeLJ(R7W~e)g|3A`J0nQrbDdn>PZ1tI)y& zFv=q|+7mQ9%p)|`6Eq?WM!ExvR-yl24fu-4aa{VZrd08TwlLs@Xe#a~5fkIDxRn88 z+yH%x-gzjL3}u_SfQ=Sw3aj|xhrz7R54d5X!;&BWKn^GIR^cen_n`8UhJtbz&Pq$K zi9UZ9tS+FC^%k}n+O8qJ!8&kQBw>!-H}MtHw;+i(DtXlpyas=@@uADsorf1#pL*ID z^rZa%LyjnH3%>I%poVM%zJc8YIq`I~Rd`Ut9RL4Pk9!@8A9x+B9Mab|g!LNI54;A} zXUKq%p8b0DFqm1zkRNoc-w<$Xh#Nx|=^?_YJ~BSQII#h>kbY*1$@$9t6EHc`(U0U^ zj`M5CmU2p}rmH!b?45l+1UE~JsWOj8aLItIUV6DC9)kYT6E4w;F31s~;i&cehTy)R z$p+3<8klxN$dCSrjnQHVM8IAL+YdUHyxRs&e1?GMBD5)Bh|y%=OsUcw`3%yg6iX@| zy-RhzX-yN|9r?no{&$@@J`A@sf`9z3o5t$}%N8rwo`bREoxW$m7Wt;!V8O%2H zW$e!LBcJ~W{btj2=!rWq9W)(#r)fVEmKkV*PR~iZ>UcuJ2XL*z-b&z)*i_lRrvHyd z{CP0U3*y+nC+_Wl@bx^W<3f53OEv4PMtdq?idh3dx9Ql9QgXOG6?}a@6@@1lo|+_{ zGio{x;qVyqXHcgdHHDx8It}@T{XhgbT|Z(gOD`=0#Oxn2jX!?+F`Y|H!3mGAt;=T& z{PqWwv185f{V^TPa^&X)0ZU0-+!uf z`k(AWb*x&aA8NGXj}`rN$)M=$CL1{es{n(PO~%a%oELUB*lnpG zZ?f(D=A#;)u}YoE63cq)K%zo3@bs&VO9Wmx$b@bI?s*%yoblv`-rkT9ev~Rkgx|)h z9Y4BNB0PlS6Y^1geP(PDOF`U#n0$}0h1^8lBiKgchh6L@S5K8uy@j$|gzu`{95+E&e zyzr}WPS2_nb!j>>w3{P-Gk)>S+uMXPbKh zx)F?u>)sgDqcNx_8{`U#4Qm9^x?$0CD-na>mr&I^o@p znf2C#s|c!)RfBkpL-EIT;ZjWV7fr_jmXN_ZSY_iP!a}rRabaO!LUkCAg7{}$%oacz z*cE zczUR28175MdR7V**|1X_Kgkz=yxz7E$oZ9n_ZsI(pH>KzXW#$VN zd}2<3JO{M79J7ndvgPCa34eei8qevhpbedAvxT=AV8prt2$nk#mo|A(tHthGlmOg| z5+Ermkj7zUw5F0b6-vD&-OT#yc^F$E(uWV!B#HCZOAjJUV%u=|HXL8{_2yD+ z!jV4VpqPTZzvEJ2sY^-4ZyE$~Fxrz*&&7*h22m5E!VGiXiz9Y{53I&I6ZtAgN9XG0 za&EuzY5jI@Jj?&Vo0m!-DO=A5i&6Q?r{sP%+Srz!mqkSq2df<1;_({~tZ$Oh%*7}3 zI{c`GSCa8(Fb90_%PK^PvVP+CTH?sZR{W)uph;W0B%Qk16--LHp-NgaPy$N;6^!yE z?q~c?j4;}j_|Xy8C)t>6jM0WgM6&8+=*xK0nfP%EtN4kNaPb)-J|o3v6rl02I+G-} z5uPwo^lWp-M@V2+fJui7D;wkl;wdax%oPTmkyYaXNAkguKq(fNNRCpn{tf_D(fIr5ut)w3@rKg8l zQ|t!(F;bYelV^B%6Yz*8;E_$hqndz6Hv#YL8Lka$0^S7w+9vqdHo>R134XLOO~9Mr zGrS2t!<*nUya_(Tn~-yO6a0sRocl^YJp;=u73`?cbm;z6PH)r+zg2@&C;XvUoap4m z%YF(M0(K{8Rj?T?p+BW1v){feE%JV!Z0yR(IZjH(hsFN?rKgP`^#1(L^qfQzgl20A zfj$xeJdu-NHd;dr4onm%*)U78!|HBFyQ358cwizWElH>ElRPPl*(E2v(9n zz6^j=FxClw>IMrp8m#{#8aL+uNlCDV#6*ULBUxOXZ*Lh(N}{ZT@!mzW z5`GaBb~A+iBF|u3Hv%=n{YAoMNlM7}bIA#or@9CjE-%bfH zF&UF=@nqHXGLal_lMx0Zes>F8V4aD4CCuAoNhjW!0=-5x+~Tdq;hYd7PNjD$IU~X& z$VNVl!TO}?ewKV;>0@4T;)llk9RR8H&oHEhkuvE*8T=Gnx>1kE)1_EknqipLn1~18 zQaKZ-1?$hO7noxJP^Kp7heL6rSV$_fj%<|pnk~eQ$8&6KYiTDB zfBE(uZ*%PEsmvB)Zo!l6r6|qFY)E4}h7Yenx?B@v5J`Nk{0VwXp!el7l z!VkyevE6ts70(gkJ7ILJqdb-(KxZ*?qhbMqQ|qwb{48ml8Fw|vmV>{P627UVwpddW zj49}&9nLuciSs0l)9b;KW8sOK&WK;sO9Mq^v|7Qkgl2)>#qSBS z-f1T2*@$q~7Y#y~IAq(;PyFFCe%URB^@kLgZMI?Yyan`{BF#D!nq`M>PK?JkS@E~z zteV#)=o0jH6MnW1)K7EiGiV9;p4tl9+G=%vf-V`{j#g4an6i!yBnOgdcmn&OlyFRv zxJ1A*BX@XGmHoUig43Nz;B_fnJZ{pEo(pn(0Z;lSTPt@+9QU&>QO29&GjBMKZ0!4< z;c-NmA2g9EPUsJt$mY2;jx=1;1cKFo%cdDHnyLW}d2o80yJeG)*tvhP4uJx{?L6j4 zOH^A=xnfg1=7~)F(PN$+CZf@1w{h{}XYyLOqZz@>W8d=x6}WX5y^tj` zmYriD-3SJfwCS7320A`IX*7{R(M@&0>y{!iP zun0K%(zjiOk6^ZRNjg>3U&nxtCuTG+*xkx3YEV4iM_@KIP%1uY}d4NC)*7! z?7+AI#roW7NDX=44er8-3NmpxC`n3zXY_roCRn8O@riac(5+yJr&_>~0xL@V`YSOD z{ltV|2C^^@biCDKj)?^{GEW5a96yr_c0~taOIBx3()2DbHE`rdw2s%*5ao637Xpsm zOb(-%z)5g_gRUpPe#JO`Rg_DM7tS;*$#nC_d%R=GQ;DzhSgSn^zf)wz56OxfXDVTK zz;hV>IEhu+!Xv|@64(Iz04=mMUif-1>uuAUz%VkhEbzl6Scrw8!jA;<7Pf_C(7X5s zZ9PFno8R9x*;2Cl$vPfyv&0iu#^2Q$kgEB`N?d?=0UGG$IXH>A))8a_&ycfqk-o_c zu9MA(RyoctZyWS$YG*AQY_mYCZC3fjR@z|3gG9u^LMpeielCM2P@aNrWZ!cG$i5rf z(ml&c_U;;$7-|G-hLi}hp^A!!t~0{$!xv!q^=JIjqo9q3#is*x_I(>)UXW}f+xd;G z@9;NdeyK?Tw^P$k`Y4o!fU$$|=sp;D_;p&=4-5q`?5x6zGfY{`LjB;hz*q&l7h>La zv+%uMKf4vzetf+F(*jd`e0X?7SZq`z+g$t@v(utRZwcR=#N$g@FYvrM3+pA!D0pyI zEvv?@6tLcPtPjT0&CGJOJmAk4 z*uY=#Yn0MW8_Bl+-!3Rp1;Hnaa zz4rcrS(~m0^lvwbR*t4a|E4UWyOq$YzN$3kT;|7o`X8;7ZInzv`>*~MG>L8#H<}J^ z;TOwAl7H7Jz*((m)H+A->JkNgtatJJFK;NT9bPwME*xhv^|oJj%v(+~%&m^&gLysDwFnxxc_RH{^;G{IPHSMsuiQSG%V2J2R~ zLCPxS#>;gymMzgD%`@LHOwK|j8wBxZ$6tFD&VREAOT(BQv1;bUk&-xOBh6gC^*;oh znemWTJF)Vvyrwta>cI%Y7O#%3l*VNZE2^5Se6iHPFqdEcld0cEzbqUB8P!wn1u)8u z`PY4NCa(@=vd?-G{I*bJ^G6I*b!VnR5WVuk6I!5E^LO%ixM8ev?xZ91+^tHc?ZZ_y zh?IYFeKRI!nTk<0tUeON9LSmWLq=v+Xj$<*1$HaQi-o_sp^OR$qJY#4%c_-XmQkzJ zY9*tB$c^$r%rY-I^D0@qz4FW|shQ+a7S@FD!)@c& z$9gf$$knbm%De9~%-lbd=$5nkcEDkPALFNDO4iUp0OXnajU_Dz-_;XOH!GP%pBut3 zb*mqD=%MC)qV9OVl7u1_v=qvXzq2UgkzH_{H@Oa z*0q}o1`{ym80OOP^=;gmAWO4Sgp8bh9K&q++QGuvP0fS>^>ReLH?y(SkEyRjvS(_& z_Ki@?-zxRMw9=idlcbA$-*D~ARWLJ_{MLcla$yoHl`FgI#f&bv4!y98q*oL|%ZnEU z-k7T4>RMSw5xP zihVFzn7u1--|UP(l7>EmYEQIsQk(X89nGlfM~O*iW0ep0NgN({`bahU!phE(GA;_g zF8d{GtE#4Ev4Uy1ltpJQJ4)A1nS(72xHLWEGi=MVNBzsDD@Tv6#{RCJwF>Sz#qySs z+D^@XKr`bm8!~geUD~;D9AR_ehlM$_2;j_?L5KGYVv7FGJUrTZZYk!@7~@|yWepVH zVw<-4Vf&_qwydWVYnWm#EWH5JxM=iV5YL3}z(#c!B@SAZFzi5Ks8tLbpgKI!3$%aj zs1~CSUI*S@-Vp?H>n!PpYiGX@NvfE)T-n=l+A_n=M*m3!L|B;d@I+(3yWGdzU44S#HzEvZ-;-#E42Zr_NQ7!guxy!rL9U0NG;cJCv5}Qg4NvL%lk4f#>ko&hYLBnz zRb*sxCf7@=etpdWB%qvo@fc3%g`*TBryLGE^Jr%DxMz!+HNnKCOTPs(>uwixz(juC zGB%(xZ)OW|1e|)XidHe|0H%dn>fOQ>dm;XE+s6>!F*!fDv`_ti*XGY`RWt91LZx=_ zU68%P?|_D3+gB7%r4KK;?IW*q%bmra1Bc3GQ|Cu!<{YM(ZRe)6WFEH6Te*NPx?SO1 zW}Zxb?O&X6=|9ExO?$39_p}`O$i6W%ymG>1SWnyJT|V8CDJ)$`mJ+t)-%#^TPzah; zRVfUPvN4ZBv&Zd)I!?*w#*G&@TQW>W+1Ln@oKZ9RQz`4}-fqm{yodjbDZKZ)mzr7U z%P0#$tFI$w4MT*{q@o>t_fTv_VForMxO981f+<>hwq;YuO5nzWfTe4{8#){{EV~)KXWtsb` z(ywx#P%y7@PZvfQGSlv_8Xz(Dau|b(OiIrGul!1yIa8j|ZPMD)*c0cEwy#~a08PY< zl@r6aR2;$j4vaeyOw5AJr`NrhaWj|p7x;AA8`!7S`+Q3mGNu1~2pnuY>{q#mzIuRF zGFinpi6|!JDVRywil9lmbIJj*@jSAB=GVK;S_BX(;|{H8%{;tz^PBu@x1q9AQy2V= zIk(~>NO#}81L}3hHz=9HBWfmh-#> zuQJ^hP-(aXxR050^{b+lmp7maCbM|obDEjE7d2eb$t5VP`&pft%$f>!7vsib=FIAC z-pt*}cmE2lo`14dOW><_c}eC?>2~Px70(9*xfNwjYz!K`>4;C>wNl05=LEtQWqHk= zKaUnIswa8ytlV+(+btKLdgZQ~^S6CRj^siO2H^dF-DU%LPHvR5db~Lg9?79XX89dY1;vM&G}}xmd#UC=e@Sob0;chx z+$g=3b8M)x5mez;K%r=a(wJq1ONt=_!v#aBPzs9{88!H?^uV;Nl($L81HyfArq>`4fk|;E0ta@4J4OaS%pF=X#oYJaL;R!wmT|;iR{a2XpALI zdd796$}yTQOuI;?DXsR#LY|Y3k=TS}8QQ5~oFpWT^~US1>x9CsrA!kl--D(Qb#miz z^RG|%#^t4slBhF|as02Xwv8)!^Q82|54k)SYkGN<<2UYCQDe&1JNJ$fWGvSPZ)aRs z={zV*kE6hYT6`OQhH)P(Z$N%yri(}z4-tp;AX|u?e7KC$|Ld{fas?mw!{PaLS?tTq z$*<_&@y1YK$DXBX ziGGAPC`wVQ|C6cAeB5<61T3{~>9#&=AxKZCMOEC;;sYdJoL9>nymFIP-u+pq!Es@H zNBL3uOkvB_55Jyt?9s3EM7&W{<>9|Dlb%id6GI)^cC$0p=we)xyKZFBch@W#dcp7!e>@JMa#_QdyCLMv zU;YP*S~q9Zdb)gT@hN&f-sa(r%7(1T5b_t-x0(%sqNWuNY^qtZWS)vVGSyXGFRdH# zH|iqZDC+)$O$usbbd8?QRj;@_(U+P(?Xo{L4{jL2r@&TCfe#|S^!^p;X2NY_yPWe2 zH_(&u2B5pur~Ik&d!PEO8nsK&xc?m2t+Si_sUs)gM%9%~W~daZ`nIC5KA4*LDtOYX zVCw$S3nld4$6KevAg;p4=B$Sk=u`VI4momoqyC)yuN-o#VqOVLpKa1WtbLnn+1KzTVT4xj#H-oWNz1C+{_M`V!L!hYZ z<6g9(a_((V-nx0Kg5I#<%r5%e?#iBLAxO`nvYbCp$UbRUyYJ=_dL7=zLuv^?p?1u~ zD0FNgJQhPJ6!F&}s@PW&3`kVs#Z~m~b7#Z0{XQie@mJmY{VQrU z-cY);ik27Cd3amA>-pydXK~rpy#nzY+iGUgR3T(+I+Y8PZ|0)4E%GN{YBw7KReGUV zxgG*^%|g|jg(~+kR53$UKEoBtxZ?=dDFuuotTAWr)4wBN3OKN98$KjrBfWWRb!m(XmklOzp?lD2iGCof^ZXpm|26lGxi++VEP^cTe0y! zg^V4AUr;4W|8is_s2lMqEfslj2ibU18A=x^c(CHRTWnC%7+>~uPS*I1gd2A)IY6(AdtMVW$t^| zAyCwby7RO{N<|}E&7UC(VBbaP{RQ%9^2G^vRo$^B<=t z=KRO~oO0jA4ZD1|1VKifH1tQ09nDY^r=6l7ZW=6=x{T_?{i+(jzr`L1q{CNS`p@YQ zo~-{==#RT4Y1<({QD5rGwKbz?oRzsSfsagCvz{JZw?aJ%g2>ygPS57$wLCq!4r$dc zo@1MOrl!xrYey{9ez?{AM{dvENh^nq6L09Y|66Q?KgxEI45;@LuG7>>FpLwFlQT*C1g0 z)V>D+)UPMb1>|%ic_((h@H-Ac;I3$n(CB?n&IC}*k<)AaY9LHrRvu7zyvR!3+)?=j zwf*IpUOONx*mazyDyr%RltY+*`?mp0r!N1ME?-p?P+d~@hB@T83N!A`p~qhv^W){q z!EDRFm~!&YZ$CK-=AOnS^J*2Y#AWL~C$Qr2W0~98W0`GU)K)LJ zfz*>5Gcq!81YweC2%u&xTpPLjNJ+%PgJ`~<=_ug{wY+$`Ka0LsRFFkenVEt{JFvum z^>LrOR}Obtbo_9qbJeY$t^0_o+I-9@jO^U2`pFQQk^|K+<*NSoDQ*Z0mtWN{Sndiu zmeuTj{*Da#9^NQt^wdjL!FBq@J|8MGm;N=Erg9EtPoX#LpFD;3xPr~SwIYLlbR2Fk zPZh+hTs1n1x}S5J$)2*&_x9wCzLz$w`gC4V*(dWM;FDB+%lp9Al27(SfXTkRTWQYy zKdy&B%?->Qb^niJE*$Sr`upEJHjkm|iqoi~9WQB&XOvefsc~CJCI0qngJ4xsRSiBe z8RK<$W+dn#9mHzSU7JWh$h;6KmU$q2P zZz(*sj9e3S<+t(t-CbLIQg`6???O%++o`efug+7 zy@3Xim4T@{k;UBKGn%G~f0a*?`U9DeyuWy&(DIjs)sEbAYZ>bBh|>z{{x(g)y4{*Z z5QzM!LWnJfAjAkVW=cr-Bmm5ifXqS>umGQ!SRew9-P`c_<|!A5(w_D>n40i(vS+>0 zgee5quHr+?+%wf7J8t^SJY62*XxyF;UC=gyC=PdWEqddE`uYX65W)27N_x`qgLHnk z6}NJ~U0J(+z>c#Zp%e2fqy=_9L|%@chcmgbJ`Z@h@aScivk>lX$%(oHA#b;@E5`@& zPl!ItnVNacPC@`yno)&yXk<~-)YO)#)qhz#?Z}_*Up}p<*co+$CCJ6c)=DiNck4Ps zjqn}mD+Ow-Iz#bV%pS+kV_;M(#(7Z{FV8X~mw^Yg?$r!>;RIqKZY;n0_4YM8zk+C0 z&iF1^Vkh_&8Baf=?q@d;8Ye~^_~JMO7s+hvneB79#W@wVF#LkwEf`)!Gl+!t`%p#A zseOlzfU;X~h6YBAj|oa(F(csbQSYt^LgNA$N9E^Q+qt7A#<5#=fZ~{QoG#;FcJw?MM)Dnp&i?M_{--PPg>1hJ%)yh=bA zTk+e6)Pb|xaEZ8faf+~tAA9iU?HB$m7MW&(`C1XucFNPwbDn+eB)`)Tq$fcW z^Y(qTdYvYFDf`r$K+gp(oFfUe4BXd@b?52a$t`DG+{jF5_@J)lIoaLFYg7AZ7F}8s zIR}zu&)7>9(-EFi)7+)mH50?~_s>*N4TnWf;qD6`YS~fmtn%J%Uw>3IXSDyhxf8xW z3js6FOgi_$X$Vb9QMwN2AhmVvE-n78`9%lE+}Oqh~9gjPN;gbo@uBA)xW0rW$760vR3|y~^*c zyuN(V{6f{n=I0?`^2T0cz@_bLe6JQx*G@Sy@hj@^)8I`tG{sjPfi`6n7y7K~(D0cbj1%A2rAG0$dP!n#=^BE6e!ia^w z<00IAeI90O&6Q1Ew_lmrcLxM~QV6~SVa4NvUdthXhf?39c_h1L2)gDBT@jFxf29@m zdiE9MsHphwRAt3Rv_mG2x(=gtmg`0N$X)y#bxv=1Z zX2kp@8d*iVGC;t?CxMq$_2Pzt+5Y3+bOmApUZs8?xrwZUk&E57F>LaRt0zGN07d3N&SMCHa>eTE-ai^!Q1YLFp z?z2{3?sTAJRNMW}AF*Rs9SRx;fugn+9DeWq@VYZwLuWK(LK8-uuJ{&7kA$ZXuPZ*Q zp0Vs^50W~zX5)9H>N|&DDz9DI4BpJh7vt36^B?k~?p>>CajDD2E{Fe}+Oj39#qT3B zL6+;TZv6PlxLZLF=A33P9yzJLw7(Sv+HZBSl3GqPa=N9`!gc#@4hHn09pyKOfocAENle5YNLNB7(b zp>B#-f5t$eQCJNrnFb2}t|l$yK#LG*)JH=?NtImZ;w zN6r=KOsiSfbLIBUA70CO{z)wa`IC|ZC<2kL!z*5OIs~Cn>e%g8nMJbYy6XDJZxd+I z!zbQLAV^QCp=1TP|HOOkeF7`K)JAOiLYB%O(d^u>weO9uU)gNzohlIC3E#WQoQ&be z9}W|c%C-iP=c&hrlaCF~nB(4XWqauH(?voH@)j7WSqmgHKYzV(_IjhNsC4DN4~u14 z>nl$$1g$wSUA3zr-+Lzn%#z~sS*r9Fm?p$5$_5K-WQ#5P8R}~Jv-VdYQ2FH#f@VVy z2-fCZR$YSt94!5lqnfNGs=8`%t*SyBDR<$SRx=^slbW&q*{3T{)_hI5UL!v&IQ^*Y za|qkAmn$Y_EmxdF#`Z~e?3OW!)axrhT$=uoRN~Z;wv9VuTS>9kRtOkBw=c8ZSqOLc z2&PIy!SoKVAizZPqAK^D{S2qtU6_$aj-Kc_^5jBh1O$=J+@Kj1YrQUJ6(FU|o5%ie zq;z+iyxm(rr)J^p*uf8u9{d3Aj*6$9ef9BF2uJ2!2YdLO>#k_gBJG3h<6I?$E&wP^GDMaO9ueLfyXE5o0TVMDlZS# z3~Jf@I?d_YvX3*E*K~ty3$?1O|DH0UPfFHSE2t|4dlmHoB&U9Cj^Ysn0dJDv@L#@N z1%P>F8H)K3X7AgjsJvDDF*Ro1_CV@%?VT29Ak2Evo!WPJsb(*P!V3#EE3b{y&YrMY zQ8LxT8=^+nvbvHkJI5dWv~J|opzZsVi;7PC0sJh6TK(em7pox1P#b~fBIi3Ajx-;N;C+(9+$UDfpv1R?J-;$JP?-eJk^g&iCva?4R~R%L^hJMdC)hE7y{eY{E< z;#b{#IJ_E^uashhdbw|B;DZH=0?%AUm2uA36?1gOi16oopJ}OM&xI0~w<$`Bm>Z`u z0~R06446^mHNQelRc>+$07u4B2u-!!GlD1J+$GAw_4`8?L!hL{uJv?9&v6YjHUHCj zpHg!lE{%P3cxkN1dg^BF34J_uPiWb;i$Rm`O$(y3o*7mw%T(N6nyIMS`kU|K?G?U- z5b%lkSvTmpCwKYIfdEVTre;}l*Z7WsK$Uq_=dSVH@N5@*V1_3n7_Vd7z(L$WI;v>I zJerzNzV#2I4i$>(d&MGmcRZsM?C#n>LTp#=ny=<;AOBUpva)93r}Gxw`$UFzZu${n zm0$g2Z_p|TS6?3Yx_x7ti%WO)P#=6uc&-Eg`XJD}yn!jL6ox|0!|K>05Ma!WdM{_m zt(MfiTZd60-G@iX+KX+MuA0@U;n>_Z)GN3xD~O*q;Rnb)_3G35gMAO45H;Q-gNwQ{D!hAwkD& zWL>a!!E9OR_U>r)VrJE1Mu=d#;M*Hpx?K&ds=BLry=I@LbXq92;&q;6CxOj-ZY6m; zaBR*$UIiRGW{Pp8VgD!imX{i>{>%SDp^L;W{ZLGNOy`71`yH4j5z z1}yr^lcPAW>h?_PaJnEDSH7$R1scSj4mNznR6pv{tqG)jao)35D3GItJln{K}CAK>;xz) z4VAwKMZc`N2Sru>K7+2mgKymG^3X43Z~S>%XQ-=X^$8I#lj|5rWhdF?orAFjloTfI4s|2Z!vvtCNM1aCyaN zI}1M(i=2T-=F|2;GcSLTd-(&nQ_tp2Qa^gv{&_be9KNhd54D|jNHOjmw6p5Bh@yP#l9-@tm+$Gi!Ue&FNh*^ zQh;meTAyXYvirjSxj%+?A@_W@`o(TFAxq_rjH9Smle)aM(7_qXvK7zhO)H)WyV=hM zQX`Hwo4x0&k-Hr;J8SGCdJF_=dG&9K6o(r)1_l$P__cH@rtHf_EawRgT3JmBu3 z+O2?CpX>7|*GH5RwewmX{jB2po7fkL3K8i#d;cjtY2#?%ZX(<Y&rC|atHb~#n?>u&n+w9U*R2-0)Sy;01Jm#y<3-KJ+lnEtYL2|$1VX1GOVxFS0u z1WHj7f>`EwgD(nd&N@+dEZX5u6+jSWMaC?Si)oS&i8LLObc*i({n}YYSO8%WF-Tq2 z1m-RQo5G1mhMM!*U4N0TgaFZf53=dK53-vgRJMWaq(8bcpE$;gOEAuMo>AO0} zDb=6oK-J9IN<=|`FgprneFako=9A-2ITEr-Y>3h zP;rsyMhb)K@RhM%yAO=<5_yukR8Ch8lj8Cx63-JAf^&?zmGwjz6i0FEwKH4FW;(xsFf#2GsO^K?2Tj!AP8AIGf&XFAW+njJ7rD^H}db%ZjE$`e%skxL4CSi0tR$k zo~vea<7U#%u8qV_P2abUnE?Ug4^}^;A3~TYBIY#Arl&x_I8F@cU+86=EAA5mg>$3= zwaC$eFJ4Z+rPzMGSV8T;oBUexV4I={!lqZx8#Q9rUJ=^6)>l|?GDWWmU#s8##Fo>1cHT_fh^0MYeQ!G~))JXq7g9fjav1@u!0GAs`uw{;2_!y4VDhA=rAL){Sy zJTMFTfTGsUBv$6CjEL14qQ6WU2JG(3yYEVLtF%y3o+Q`ARk`C9y&Hnt&=f8hxUOOZ zvbSfxPtAQD=>S1*uHNsyH%7fFyD*Gc>C@xIhtE9%S9)-5e1b8gnT17gSWBCqYsr7dP>YKH{F zqy#2F3EP1yp*j*0M&-R+@9Q)6sKwS&jzRv;mzxfNYvYJ{O^TrLRdN+(_2UC&UZ6Ta zfly8}6t20iucERWe{+#%ig#~{!&qE}9hWftHZE_sVR<{(B6(vbH@U_4u&CbaTEhtF z;>Byoa0iBFlLG{0C4vjI6U@zxx7F0tCu9k_TX>Sb1L19rbV#Q@SGIk4YQ{}S?O*lK z2dCJ)OFNOy&S~PS-%-b*hejXwA_Hm$8M`ooYuDwg$5uS{2K)B0H;GWV_Ozj@e|OuM z6bNC3jXD+ANu1F&Q@%L=>?7RHx-aN_n9UN-{p?D5&Vu9g-Lw0VEl~mF9Gt19rr?c2 zJ&y=()bFl6>wO18ld@RVc!3ZTzJ;p|6 z`%{zAgNGr~ouY*Eoznh-pR|?j(xgyw|CH+ZsoTjHa)ksSH5FAAFt6!t8d4l@(iJ-n zM$K5CaX!$Ge~U|4w$YAz2SA;D}lOJq%-$u5Ix7zi*sl0+*4~k$8J(oZi(FOEO zA%B|pR5Dc5zJA`$aiYk5W8)G)ORsw+~16y27pR> z|L}zu1@!;o>Ghu%6)U{%-xjzv&^3dKjZwu>LZJZvyYwTv(N9V|UERNlqsYOFI{LO; zCT>X=H3Or;m9`hvWL=~!ZdV;h@-~rX3vmU9;&HA*a~#}sZ)p=(K0W*Y5ck&cab3sa zxX(Ekm$X{#N~@J*%a%dL7!p%r$8i#+;WW^siQA@qX1{CX3Nx zW@d}YV))&=k{roU_rgYnKLtI;94s`>bHx@-GXH+RpR^Z{iypJ&pu4> z=%Kxj#JKX9@;(;y@p`ws;)Un;J?c_SJhSiQ0{0%5+&NN;A1uH(xa*HUiHgZMn{^&} zZJ!+8D0g2vclYXFD^&NeJPg0vOpr;0Hd`r`)qOa*g!Xx`GI(+uIWOPX_w4r6$9752 z@3l1hyL7D&*vAmtw~Y3A0=Xb=JIid{$+w>31neX8ur;a}+Zs~bY+*NOKZRz*`}Y1y z;lIA_9|Gq3-9MYS_v#VsOIP9x_U*FAr_AU0BPVaa9Vd51FOqPjT^=~ro{uf<^syF3 zk28v2SP;0f5yV0f+2lM6u+K!{Qk;kX_56Wrdz-s5xie$m0MQF<>i=R3m zFgSfnPVS2%JR)LUu=mVM7Xlq$%XUq%oNeSJEBKX?Bgpot|C@>cxdm%z3I(r)E=|I`W32W z!QO3(t--$SGGS7j8^?14fxFQ8 zL}VD|$<47M(;DzJwNT*vUeg7ut)^V(k8o%15GEGMlrdTxehnAF~ z^z6u~Vq)Wna|9^u!*2g+Lyq}AUoO>G-8iv0DLN*P=9|3lf8fqt3YMW^D76IhuoM`g zSCh~J90!BQA5VeYbHNyI9HhZ(=3p3FllR~VF%QeINa%?yu_|8BJ<1)H@-j4>MNJ3f z{Hjup^DJKUwA&glcLWSTpPv;`c${VFBc-CBRx{Wz?4bPN8 z0pxXn-+wy^KELb=K7RMxQ1D$O68zpAIKy1b6b9)m;O^K4T>2ssS`^?>(^x2(fHE|V zi6m|`ugeYZl3Dr_E!YTQpk8JNU#p7)@nppv;0c#5dVH%FGTkn~UsybU@8g!&%qAE{nk&lrFkbDqCIAq>Q&*EHb2-vOhjkBWiuNgM zsNLGviyWMQT%5l@np47?iz0L!pWElS4x5p~b9XOgp@r)SS9)AP8NEOZ2_rQc@Y-Fo2;V`_&n@Dv9?sI~-5+?lXFd!A?WqzsPMFk5lCRQa1nS)2@~QC`9q3kfSkVUp zi|FA7=qF0N(9Ta(nnDtDbPu_D0%@D#!(tyuvrEwGN@nIX%_?jE2s|Q_SRH(EGHsH0koU|uHe)aE0#4w+a&KO$IQE-~jhU#L*^B>N--D$$&^pPRf9 zp_2HMGL&%5OV5mBhM99>--^N9Q8eG75Tt3327@}4^!=&yrpUn%m`Rf9!RM;6N|`4) zuGUBHo8Wu!oR1`_go>Os+?AU^!+TY-7HU9q1@nXzhjU?xikaFAaYPJdMa+MaMV3>I zU;~qL7l}y9s{>kSInet&FHWgus@7ij;I+~oWWygnlT#C76I917Ky}z!G-p<3L+GpR z`)m+ILqjqU4Y#v%dZzkT!2}3*z`r^glfBbmqTz%PeVkFDpuSnbXQql*O=-foiOu{{3QlxN~E2r^I<3$ z6=s3om8e$#Z?w{wvI!MO7$3L)O{~yXnqd!~Zp4Sg4WJJ$&)~zPegb+u7X$!tw-?BN z&A%NE9WeHRmKngXi)i@eOdFl9Wcd2zwTx`2>kEf~BVl>g#DZwywg{}L4s05fg5P>m zn&LJ5Z6>ebp*B=n3lDT?TdS`ruTdRz zHsz{ZC^aOUxuN1Jr8df+vp@`>an(&vEQKEsi~9zEz-3M(sH2UkIy!+&qw5&0TRiLh z^RYWb+5*^6^-2Z~0t@asR;k(N2#xsmE+8%x7J0*ZP7G#22SY<{FubPxqQ-ErYqVBO$<{$UF9QD_J(#_JB{I->!y0~m7J zvY@}7Id589A6tuATno(sX&@(~4H&;w4D?08 zwaDk?g+t+5OLRCUG8E@>?I!q!qInb85+rcg0CKHvooP`B-g(*tRms{zl*-T$$mZG$ zZVsFXKe1#8%J?-$Q`FML;h3B@6gYoiT>?#2UB9Uc0lu^`yx`6WGy|b-pl}FF*A3vf z&BN(a@k~6hJlh}+r`qkgi&yLfp&ApuAc~We3|cNVX@tB!=LVuA{v0>uyqAijpuQuo z?4wd%15BZlyg-}@ZTyog6^om#68$!4dcH_K_!~2|;!_UKR=p@ki$8I^6mmV|#WIb8 zHarnH(TVy@T(c&uxwn~GWr6D`a1^Z3;rs?-8C0Q}bG5)loWotHfl03}OFM6@oUBr_ zqq^vUuLom5>qQ5S0Z13VUC9l;k+OdgxpTU;;tnjGOubQeaHA0{9M3-G(gwoo$mXjf zPx@Gai4MG6d+~L1@l+2MYD9S6h1$y7<;OT#4N~dB=!0&R@ zC$R>w3M9cKEE=;$E?!_+l!ynQFmiSZwi_aItz=hnyaTwmV8Ndi&cL=m+aK{B03ii< z3GI#oJ7IV==+JQdyw)6SIAB>{r8c7lC#>yKZ98i90Upoh{jy$bb{zy%Fc}M*@`pzH zpxGYOwgYEF$|W4O_JVp4z>yP&joYx^WiWMd?#R?8s6cV6V9EfMSE7sv5cySN0t!W; zF#4XrkXy#rN5RK#gKRVot-&|>lsT0F=7HeJY+y-$Sc>;i1C%wnr0yu{;pMpjM{X`} zo?Okx%dEiCp^PW;PJ0sxL^y4Wx&#(RVry%#pbz^hU~mjDp*+Z+n4$r7TWKT664xAy zl356Ax%HsO3j8(sgJ~TTS*F-awNloKSurchiD{~hHV}EMR}1G)`G~{#z2rT@3ufnm z?vDfTesTA;A*`|(%NCBRHwl>nwFyKLj*?i)m6JdltBvlTq~vIcv?OYDmgqjwfU^I& zArCN~1rm-KN`kfWkI!TNS_A4`c%)!}m)wTzj}^aJ7KH^@L(2nlriO_`8`xnpNjv6* z4X1(-asr0IF;6@WrlO%Hn18ky8=D2WalrD_%4<_uFuMud(Ot6zi{BgBfa)urqCRV2 zreQ1@y0HFY9h`jIPp3v{92%T&kr;^mk9zR#Y1atk9AA)gXvGh#NW*>@IMw?5`PYYE zoFH=>QG-1lz&O_le__>)_J8QBSz`%;QLIB}5*4Fpe1p_KRVV8?SSqNY@@9nYYr?U& zE4r~|wPk$}eJ)@YuJ|IO3DV+NQY!FlCI;(-;pW%^ncD-FkUL_01y6$wR4$%@@9U9# z7>OpqTQc2A>{C}@3B`J{&t-a0uL;yJiHI`}DLu`T#^5@q2MmC3_Pu6~${^zrvWv#V zG|0tbnf&97RiWE>29>W>$QflqVuO}*7AU%4^<~PDlq0r!Mg`FMZ^awHK-xhss$|gl zI=yt$XXAK0Uj5&;e^`ENL96wxNI3eN63XOYADwc_7G=GXOpUxU^REA!Yibd2^Si3Q zX59uuC@~1Rz11Kw>45g{nUW3rm$Yh5*JyP)1BVb8OXThk-5>p0L>Dq`g07zjCIZKE zMUf~aiEA^h?095SXnA;4B|#BAn*#E4(#y?hzJ+-2{rp>P~d)8Xj0lBK#QFO5HNKwqiOgR^YSOHj@` zTeSqnT|kw{p2}U~WnFE&x|qS8`iBi?nd2$x1f8gY`dHJMpEUy8^tm4Vh3CiBT0p%{ z48Ok~#EoMa7t_e}i{@XLx{#SXC-sP5W>wJ3q`XR{2Jco~^#vZ{S|d>IB^W>2OdM`g z9ko}Ec-LZyQKzT9qeZn7Zgm3+eaxZOX}kpu2}sk9mM zRcT5F6~aOFD)4UB;c;XS3RNS4C&(pC!7I{FGr%kGuZG||@^|^*@|j!yfO>;C`qt&& zh+a_r1M&0`D`vZ$>K7I36Y;MPUhCGr2Wznq%0|Nxw<4SPvseqv z2OFL+Pf#w*xe=s1?gP@fGlT^Z|3dp18ki-TlYsGa8JA~FK)=+b3a>c$k_GHWm1H@u zl`IMZ(PwNxS04J~zTh{!3^5~=tsaGfJ%OdT>0)0oS_fVH8twX{Oiy19a$YOo3@XL{ z+5PmgG+TY56ci=`w?tThBIR)7?TJd@gSmgwsTxf-9xQyy^jZDiRf2ZS94hR!47zp* zrIUqrg#9E~lQYe|zoyNT&e)Px*j>T^`KuVC)ejo+NaY6H-k3<%%)$mZjaUWmZ`SnR z^|i|AIY|_VR`LOVEtb|1(I|ngJ}>~%m8MQF68=W8z>dyW4-lt?@~14hcKj26PhKKM zy!dzVC}AOPx5CNUf0vhx#bHf^!y=xivNXz@yk<|BfLY-5e3Y&^&+>SkT8m2D*&^KS zv(I{v$!TRXRU^D`3~apS@n+B$Lx+n_MQY|1gF+<}o{<%pMb<srf*7W+brX&Wnzd?V)p&!h zzvQrc8OKB)xE<*{-4#s^AF1fX4G%R7>)tov#EA!`=25_gGNA7%nQ**>i^?;+JMQ#w zAboEJtOypgte;4mrJTe=U?~C{6S*muh|FYpN(G$E2`0X*Pl1w~6?b79U5w`+E&h1i z0%Y0%pAJy3&z)7Oa#D&gMkNR5tdad+vRddlwJ~8hW%Yqvz3E_Y6gqgl0Mx=xWPzB4 z!8|Mn6)(-+T;a%-a`($E;4-ol%+pgx{gfIhujlNKCjPgTs6cs<8dQx^Nk3Vuwvocm zd#%L+bOS5x6>;#IydR|*PdTxJDJP_F4l-y5V-**e}5uTN?u zErJzkv8J!Ox4N904ebO02XmmWctal_X@xmQbF)zaO!~^`)V(GUOn6?DUyIi4-(O$} zMu%a!6Erz-*9qk4cz}(+EgY_Che5D149lR7*7me8VaIwT=)UE8gINgA`Ok>$A0UC_19q~$K~&7ZJy7T zx}&hCpYYb{KCEetiAK~MzYGuO#3@(3XJ?yaX*;BT{^*1612#Js@W#+ULSN;3D?zE?fpls>dFWfCFwqr*$wZ! zWbOit%aGe0%=dlc4QRqt3K#Bv;(n^{Ov(X6lw}2mT!9=-oJl!ra;)v}D$jA52yOte zY6Hrx4Wbf3!5uK?ftd$l&9hij6j^eY^tUDd(CS2vt27+Z&xbZ^;I;Q z0~0Lfh?SDzJy@B9Ted)#ZWx;f)4H%hNuI39>lEyHc49e4w*)}9Cbq)H8=3SY{TX~Y9PQ1FS?r^c~+l^l+h-R1QV5q-eOnXG>%MX-3d z@fR8V>Luu$1l*p~NO-a(8_Z;b>F@m#fZ2a}y25)Iu&E!82+PF_g#_K z<*MVkupNA7iZaMK-rPTe1*=3BcV4zBH6T)Sv-FH#ow^wKv{M>j2kHBBt^~f2J$g79RK|$%P>_16 za(x9>M_MTlQsL**$&>?rg!8d5av=-a5tFyE0#neH4=hYT()Ue2ak2u&`XKv6i3gZd z>Nq{FvCJ!^2I!QZE;cJo8Y)#7fMwi{47l2O+VHyB*&TGRrpuyOjwLY{Nx}qku zOgQL-H$rDgYzSBr7Kn#esNr`K-i>(Q&m1z2o&&1mfiEu2_l8dv#epO%&e$d|x=PtuHCOresi+rHv`jdQ@S-DHA_|WNYMZO+A03PWN!(DP z3Ufe-SUY^G34~I0!Wz}m3-;P|baOE_5GybUlbBe8PA0!Z0`pKTDjbhABJRqT|CXLC z?n%c2*Li8K$|ay~kp&N%FOC~ihH{YLWZ+5S`u8PA_o9mi|6 z5;XHK)Q5l!WJ!!;b2p#xQ;oe;s^b0HI;s?U;6qMas}^xqK8(AP#81fNc3q2lzWjPo zAGF|Sb)tN+{cpl6sXlH=JG3$6*W5vge~kWo^JdLECVGRY_}9rdr*y`z-s9R*cYa-uDk?e zf)U`)Gg4~u0d+Sm*3kHI=H%%V(+m70evnGTGd{wYLm@|sFIXv!9hl=RYo%%AGiLO} zrf?gv$tp1a$^`ldt_UFrIJf$lq)5%hu5o-Fd(XUH+4uK%zK%idtp;3Oz zQ;CLnBstEF7J5o29O$%f+CPbK#s*fnjThUcZv2HDEJT`VjpC2R$U4(Fn^C~k4mQ{j zYxStu7B~rv4Z->*sJ(DkDb@$faQl?!4$O-40)Xi(SbnnVl*NgA{8FA=+I=urSgkSe zVf)D>I$v}AS0*RC&Lmvy*kAN|;MIF7{`;r-@oLptaupA*U7LDGqs$cdB3DISAFsgb zw~OD1$P{#&gDJY+6>Du}ydhU?$Zya!=a~Pdm>i;NwC<8Q2H?+?D*H;-!I$2&p(s~# zUM=Ui6|B5A%NbCwiv`ZVcLUY57YvY1X@;CKJ?$aRfjQAIyaDSY0)Y`etCm`cz6|3Q ze-kL``}>zp>Ydz4IdO+tEg+g+>QSBfsTW>+S7Ra_mYZNDzo~a441=J10A6=AZ&+Yb z2?H0@2OMB=8a}AjD(bnWubKPQM1W`!xnM=8%^#EpfssOxeAP^S!5RdZ!5nPh4m7HO zefB6b36}Q35*OslZ)-+5cTsd2wsywO43EsgGk&pjy(JK2aSG^(li6B5hx^{$__gaB zlX`6j+VE)bgAqJlp_WDsaBOK;rQ2L*f%)^DH5@N`ESnp{X^W#g!8~QiEu}L+tK_b- z3E_|iOn!4ahA!*F`+7mV3!bSKbEtg3$vc6zWFNA%Ku!Z>x{cOfDzFoY@4O&b5qn~x zN|soIFci}ljvz;nO_-Np?VfPpx2}Rnm7D-j_htk3^3e4b*QY`@ueN|fIFGMkUp~<` zFEj_Umm?#Q_sf~@7b!*LL(@~EJi~(x&{ERCD?HL7o8%>Fc^?keNPT8{71S<)8dU#I za0Q%D4W+5DmLTT@t@mGI;!7>EQy_9yMZ;EHmX?@py6xvLpwYQD& zOcyYqWab6hk0iX)Y6`|+-J2WQckN(?EsS{I(bhH7epQz@Xu2uzuS$Qfug zrv8*!B9=R(HghrdHH$Ts(RdZVIo?PFZ@z|%e{xhd8;a+AIr;63p7)>DU&(`G73BE) zrgW|)(C-5tA0;-xnrx8Dtwn||Fdb*g^22WKXyBW?Dttf?@z%HP*JnI|ohO!bZ1Y%Z zJg|G(xS32kQt*MP8;a4(*Xz9*UH}GvTYt(?Fc$(Mzg~DRDp)0lOnoD24XsG}gx0VU z^P;_I15qBdg5GqXinZpbdgaSeFpS6nLsKvtJC%_jIJ!d4fo9b-Fu}EgtoDmws*~1lS(z zGX$GCAS4dlHUSf-@1C+d;T=Jnfgt1q{qPl6wfbmL&y_od(ApQbF-5Jh(6tP-eL3@% zk1@V+GDm1Rrj|2uAmR_bX#CXY-uu(}KDr%O9n?*(0J22u+&RId1E|n(7FVC`J3bZ+mQ)LWy{uR4 z32K{x)#Z+xB}Zy~MS&fh<`Wg?Vot8XkF&t9!)kvomssw@v6ho zfY&UJ7ZKAZd?X{s5{?<4YtgQp2H|+k`Fm(mv<{aJ%il16%3s<4Wb6|yrwb1nb%3xJ z<}X@0LZ_1!$J>uoo{c}&7mS8IfFD>;O4O`>+0RR*UudIdL>KTo)zF8MzNXY5EfEDEiXcVz{n4_zJ&KOMmPp;xZp@7=&jtX~XVC6#f=-Vap7 zfQ_ffzsQUB8pADVZ495Z*fQ2Eb|K4#3miPjfAute(b-d(oTJ8Weh+x0Lyd$G)pS4LY!3R)Iv-qn z!YvPkA4v3vX{BJ{&vSubr3-*;7ujY>Qrhu@QO?^O?xYI!FAE4$Gk7pNRcAA^t zrkrwMYQA;Y9IsNGP9-s6x|;S5B~n8K&1B#dv~2{vT9wR3Qh`ex2=oG#&$#`nM(TVr z=Ct*h6lvxYCA{2giD#>&-Z#Uz-Rf1e)!Fc?Axfcm6&gJhubeFQyqTzz_(H;Ymy=~Y zlcOewif`JV^E+jTZ+CVTO&ygTMk1^h&`bs5b=8 z_`DOIe6|^{K9{Uxb0SYE;KXP;u)^ccjp@W+ogF~cP0;E4n5S2R_-Rf`Hu>1-l5+jE zMw!`@MKT|Co3qW5yo2#aOOC~#sXX7S-u!&dQ%;gh?VtlTuQi`4IpyIFDsQx!a{d|- z!0W`SLP0_=u>X0RHEmWvNj-J(EA?Wv|Gf~ou;4E|pJ zvFWD{f2#XnP#+RZvj` zipiQTtSte>32f&HBiv3L0t$GBbR{X3h;OQzl6gH@3 z!8mRwKM8j5Dq(mMFiTMCvj9F^0=x`|Hbruvl35g3}@I zpRMBQg7%-(=s1HUorVj#ji8bk`mUA^^9Nzvl~ZG?#_z7--E{xo?e|Y=O&@M}W=gA! zFr-R|5G&w9MGK?J4c-DV%V|@o{4ASk3#rmZn7zOJy(lt9lFT2Z7U3{fOLxeX%izmX znhS4fMdZ2$ zDz1B}`Uob9;9Z@#_ZQVHd{Lm8$UmIq-VGqf$ARmAr4j?2jVDYvh6jer1%r81ovYd% z(vn_4LCv6^o1H>93H#moX+K1nf;P_RD<02#atvRsY{A2!@0BoU;|wFQrVCM6S)^*1 zY=sH(_M8CV2sa3GXmZ}^&^?t3lt2ffHtj;uJ;DUIQbejQ{GCx6&&|eUw;lrO!rP`8#JPm}KgL%T{gB;DlCMx%=EnWFT zuP}}p)v-C~bh7NZpkQEuRfGz>2hm0gW@CmAJu$~AQ(1NerRpV5PVie*CPaQ9o^tsp z61F#r0=q?a!c0zk5qCmTKMo9E4f{Iv@Mxzvy$_GLhew9f5hlvyG+6Q@`s(2F1gPaU zd-2v+KtU5+%D{sALE-PR#GXS${W2X>*$a9b@p- z_kvD_9cnr}E-_S{*XmL8KUse?n_3er;LWgP9`^v2+OwSEOtS68T0`Ch?udq!!(lKs ziiMo7!j_oca|uS{6+{PCXHWMB!GwH41htA!A9R@D`oCcSV$xtSS%qEbN9nj1J@{ch zJt@b=rm#LQG|GP}`~|0ZSjTms>VI0BET~joDLUWiCvv~wl*3*5qGVaMPIa2MirfFC zU8nhELJOSwyrKb23tFfq6bVF`&LB1iZu)_MVpz!^lq4T)ea_>GkzoG#kjn5cm$`nZ zfb>OnWG~v}Oppdz4!Ti)p1AJruGUMg9@d@2%0cc+pU1G)@{Dp?2Hue2D!u{YeM&dA~oTaC^N z&}5BTUW~uS)G2f7PM6o8Nd8IahryMw`iYc-`C%$)FNZX$RJPF(>&V#$_~P;2Al8Ck z-a}T^3BBgQI`XCcrD-fd2dJB+5sf|0s`CFTzqMk^LGB)ack~(_&@c;ff`99)3cIf7GHAutXGr$zF*{E? zL*O|q?w|elEdS1>XFw>FQbUcxT1F{#)Sv1p*hoz<`WU9B;0S}eOZ{n-8Uhki;&L9b z?~*CTDAJv(pkO6MR#A1}a_-}>kGVw9oXmoDWHD5KagmGxhaUa+xI9Uy`0Tpfe*Ayk z^d=f1d&x_AG_&_4VrTC^Iz}MLIL2R=NQnu4If&e+SBQT662Xz;AGU&hD=%*&(07E@U{b`6A1d=6e zpI*fd|9?fjL_SEqjP1K*ve$5};BpSJZx5;kR8vg~d+Is_||cScNy5=gJ0}`-I#5}-oZVy{x7e` zh2te^fmo&j9ya8^5x+yo9$-c}0IUQ3Y6U;q42vj#`mi6pL`^e1^U=2fAcy{!kzr33 zc_5|e^boN`GW963|FbKV*!gD7k9YrnuIGxkeC?5b#tc4Yl}aGK4FjK6Z=o9y7KJgB z73cu)Y1J0G4#zq$iN#LRt=Ph5V3-XqG2g+KZ((a7)&M1T;M+(Awss4<2{8vGaRlE+ zD6n zC6eR-Mr;<{b|0-^mW(9sJ3|fILv2XGEF6;*0!9byP$&63`Qu0gCdu0wY2PMF$N12O zsv)p6Oyad8UeoqqJ;Ek2N!AX!bsL?7*-)+&W`#?fcciZ0k%1X1CNOJU;$X+67Nm>!sWDjmjb|Uo`&%j@u zVd@JJv>>es8}c4{QFNP(2fGH$$WUO;3_dhqL1sd0a+c{1Y!!uy9PAp(fVSiu+Jjcq zH4DgsTn5(U0A@!fKTKxFh?lKE$Nq@6Bom<(IfU7gX%7xrku$)COkx%r#Mq%!#n914 zT2hI&Sx$o=X)d+U47QShij%pWTyE1ai-wH%Dg+$F?{D$nxpsKn=c8b4e5kskwz4Yj@zXG zcBBK@l56nMqn70KeY)L>+gZw*?89tGcV>f^yx{g0?lPF(T^MuH5!q#%q}+@m;=WNjfuo82Wc?r6?uHJ#m=b|G;i`wxlz*>(QLye*e*o%myHl zOsA*+-)ifBU7IV+`bg`Vkwp*5@R(?!2o94Ec4}s1_rI|ThBkvvW9Yy8bnx+kg*|nv zyIk#(CGnrwa_9OF`#n8Za)j(A6}2y^sC|9GWxofc)LZ?r*zez6ueMViP*LFTDp2nN zNe{NSly3uB+IZ&CECzhwY3r(&*_E+oXHz_=meiLxNrEpIqD4 z-Rr))O}P1@h3?r@71JYBD|&>-p!L@9OWySMzG|p;Ms6pXk-LGFd*7vL@=;rY zTYZUwK9kj#C(SBo&l0s;or#_Ub~u z{`Rch?;&>2vO^CYfDZ6zR3$yc*ibL+$2c=g4KU%siB^P%t=4%DSvz1oGLUL@9OVh6&Pza#qA`{+$!!u@NJj!e^fQ9m@^NIYGy3V=rJp{ur60Ww6h;P9BV^b8H-wJ8-pGn)VCI_`f=^bA8#vu6u9!o+v*>0 ztL|^3oT2)(kKB5Y-$zfPOMdqstl`dG@cp~6`=jGqcY4(r^H7KGuMy13vz1@B5JC6% z$i5KkB498HvDiI-orhs#1DUCdtip6)Eb+IkIe!bFb~bTe?a}v@m%kHi6vt~z8Z(wn z($*BcF;A)Huh?-QrB{ZS0Y|om zSF{vN_5ZINYWF4T9mQq#fNHvJ2Sw_y|IeGnB+rh*j@~+4N_|{Tz5ClwByW85``^ga zUwiAPznEWT_fV=GsXe) zrm!Jsctunh-#9F`jrK30*B#Qb=ss*7!=u-?ofiSGx{w^+^9*sVvJ>mj&kKEL?46iE|SjH{a zs{r6N(`T>Y$^r;iCz&4up{155@WI$S3uk37Qr;|oE;z%mPofH_<4DF;hEUZU1yL&B z5ej)-Y98RPq~9^DHJ`vR0K*D4pHPJ?8v-y~#7IUeLscsTS!%H`s&;Mxm#z*w9oJqg z|62Elo0kF|lS>7SQ~Kll%AAVpAiH2RTu|mX)+_=MO^x~o!&v~B%e$n>L_C+W>8|ZG z96ceHy+MU^MMDvfQO46e?4nqLUcM<4Xg{SXmEFaENq$Idke@M2>Ie;8omO#ae|Gwg zX~fj#Db8y3K-nxQ-JoyBSfx~F#) z!VoW;99ZbdfcM%{ndfX4WU?T^!mR;VM`T#=>sM#pdBZ6T&YT@EVS(d@VU_?PGxkS9 zY1$x=y-YO~WuFxUH-uE6f?UpOB^yLq(dbm=d@E@>@eN; zZtBi5OlMgZBo7Rf4G$49^Jc40N3+1gxW1LPV0khckzJ>p#~HRb!15?0Ba|)hQLyFX zby1G_xU_o6Orkv*i*%`*DlPK{IyJ$`vD~OY2EdiE_$#uvsEsXpM%64mRP{wHJy%#& zF0aI;zQOgi`x_{5Gmg!ndj?{<+(W~M0n#jT5 z$yr18F_pi#WkQTOSTgD#GEai^!U)BIT3lSZgG<+&%ha~zJ3lnAP$crpTJ{!3r#BSP zZdTqFR8`ersakr$og5_>_Hi+{#~oQ$tOaltqm262mzfplKLQONrZ~GiQd?8jjeSV0 zkQ=1JS^8W%j0kD)qUE`r12@z*td(S9!uadYseH2>R((9|0;@h%YwQmM6ZDmY#x_;u z%z2NukZMO7&O_Z<^5&^*BO@Kkx+39<8S}~REYAn&EC?K6fDa3NCmG=4zXT6c)~OR)9Pf7^Zerfb|px zK>f6}2(X^Y02s^;1+XA3ZWJXlpWt}s5&VvEM63D1rUx- z)<(hG)i#x!fb8xio<9qGSkRTv%xq`DxPjwWv9~KNcy27d#mMoiM&<+McgC0bF)V=j zMwKhP(L%o;!b;i4hI@52Xd)yv3^rNxMyh2k);p`qu2S9X&M`)-FUW9Z)j9ktX5XYR zWgxHum(F_z@3^FLms#~ku(*WbRKNn}K)gX&7AdsG_{c}1f7AQ@5!E^3RzMJFd0&|Wlcv2<4R18aj8*bCZTQMi|Yz+a=Ln}+XPW{ z`kQ>&JJj6$wJ>E>2SSlb+p4^MaA}c^0arU=?mJ!X%~OiSQqtO6c$GB_opa^HGT(*; z5E{lAE%7&F82}C28m%QjcAN?>-Yo?y{oTFN6W$KhAEf|BcV)$U1*U}MMh=X?xi(ol z0v{jh>n#ILHI83Z0SH&v^_DzG!BiQu{v6h|xOz(%n$r`^hZvKB6H51bu&W{gs|d4O z?o8p*wY$+rwu94^HgDFFEBUMZzdauWEaV+4{FVui4Zc~WF&;Luy3BW8XI$8U1-?Vf zV{2wZHLs~I^^nW}P-}OXWfU3n_*SO(hu>{2lu*bnl-+uNp?BGZmIfR~FKo6kz&dQm z6k4#rGoDeU8w)CGS0lh377Y1^MS^mVgb3hb_GdUy%}h(KT{DFFEHJf->OVKbGQngY zQtQU<+akQMCBj|w)mcSRR`lR$J>6we{ZWVih8axFh% z>W_fo%04Rq8EX%-w7aamu1G~ZVXWCaBWcul_Ku`i^!tXj>@!oYXZSJpOl3i-Wzs0W zhy^YgDc9YZPw2esI6sI5xpNH2VgV(4g_?L^l~sHzVZ}YT%7MsUeNz~c*cSo%W+b(S zs?jX_DhH;j>-6x;*Jk`Z&TJ1anXke>RfP?~!F9OQv%_Duy;GgF-eE2Mij?cVv#V3U zY~1G!C)2I~;Jv|YeaJgX)yX|dRrWO1X}049+a+hgR>{eGxa1Ug`uN_e8xt>=i05z@ z$XLtC+h){;KL05K~ieO2YVh0GA>`Dl0Jyr)X z>9GDis2$ve>!#v2IibDP4rGXHd@><>mP$9@tGX^lE~SO?2URCtV+<5L#uzA{(fUL) z1JWe2r>Ket7^Er~kiyW;1%^SaN$w5G89tJC#CMQ7O>`V*vfgyVP=WAG{)Ujd3(C#cDwzIW_pE9o*<0{z-q0- zSyT26H8P{nI-@YQnx&LgrvSNC&kBXHiv~?hy=R=Sv3&|&T8~|(co<2@oV23%MQIF^BE}5r* zLNV3@B}WXF9wzEL?Gb(FVp;m>oY=2RO7-1Fe8EDv$ zgiGZm$;#TJ3sAQj-qh`hC>sR6b4iuFB4$n9wZfS2~4yd({tmZAaw~ukGq8Y#h zrQzAVYEQsfW6{D)!?9XiS{QFH(@wVjvu-iCc_<;g&IaCgbWi2Ygr$t|8{#u3LFxR; zGS@AXd7P0~TxZM}F}IgrEVgBrhfW_ciy>U5*inD=5~QJtJ^wg+Yu! zdm|Xv{q@c3Es4puyt~Y-1?|SUIXs8VniG!6RzRND^oPhhu}fUeT#o%HFLZsiMXlLs zZbh2py2#{>?ut9Y3IE&ONOpm;S>8_Xh)wS%h>a11aNhc%27(&RNxtQ9JK>7#6y?Ga zDJr#7vp7@K;X?ntMICLOpL{E}vHl*Ay-Kw%?{xtiwDmF}`N92bM>9Q&V@bjl5FBIH z!VhBsa>XNa3gSTs3uNz8Z7bV(DuiMMjz>^oO}89-rcx|gzr9l_p3dz^zP04iqc5m* zDu&lQHkfiQc#Q#0O~$xQs0&-KdQPtanKF=k%ROs+jlb;G zjEEQ?1+O;t2iDF)=2W|{?lN95Qh0oM(t;6_NyF2L?HHoeHo-vIqyyfZKd5>@B$N=b{<+ym%E+W)L#hnFI4Et=pKqCQgtKhPU)o0Q`p?AUkhAK3<@K%po$m(GL;%7Fk)Y4dBQ?VI9 z)ECcdSQ*D>?U!AqmRNq|&igI)G<#rM^}0}4mz}mDXSDt9{*Ug7o3wth?=9av z%U~{(*jHG}-F1E#w*5YE(Y@N3kjAyN?KJ7)l(fQIv!kvP!2hndp$taVCQfNEp=AAm z&7^FXK$CHITbNi>mA+G@D+l%rYsuNS5lngB%)abk;$m3(Uh3xUbMz3k%$2EsriGsQ z>vC~<;6@}}#sW9L=Kqhi_kfP;xYC5rtA73Zbwr~9lFdgFf<#J!L`tM&iMBkFWy9K$ zY)}5jGb@|6#(Q?g^32ZIp4~J1d)7Or5gIuIM9vu?at4T;bIv*EoHPEq^|}GD8?tA1 ze@gg(2Hw3DUe&F8Z{50;Vb1+AI}B{-yA;g$ho;@SVZQo&63_UVSx zv0dSPoPIGKBWW@nQ?$o;I@GuH-%m4#JaPJB-N?Ry*^qpldV~0n)?d!Y}-({zzL=)+W$gw&O}@V#l${ zT0=Wd9Gg}6g#!XN+@G;ePYmczzLM_lX-?r6lm440ayvf^fn{6u)KdW3!3Td%svfpc zV42bn9C=$jjwW@t-`c9fYQ@&hbseWa01w7);i8~=IeT1C0YDG0C;WrG?UAi0!ePyw z-Y$wteaSs^%JbT82nLF($mwyv>vEd#54ePyQ+)}N3Yy69op{?Ztl1s3TIiF907s{wDsoR015CX?&oaDSvm+H zV$sxyn*>B4s+7+sL6*8_U&zuA4{O4o6l7*bQyr215maSNxI2ooN&j&`^jao`n!B9C zZzu-8U-i|~e; z-Kk;pb^G*+%2^^(JN56Xp5m(NF}26IdcWY_W4-FrMZ~@QF}Rn_%q)0k_cdi|UQ90T z?4o*8oh{2|?X{g!qxL_<7NPc8li1in!zxRREW+ivJ#M?FYmu5R>5WHS;I~8A;;k>l z3@(rRW<2jV;2ou(p}Q+UCuOh?)Jgqy-x^RYeYO>Zsl)&a$HR z?w_@+9XKK?Ev9SOykJPQeSGS5Z{%oTKm-4 z0&4tg;z`8jf*lAXTB*r5;AS$71zGG^%0FSj5{JKW-(}-~QZLA21VkA48>(Bkw{b0U zI(&awpFZy*uL0-`$@Y;8Gt0LG+^1|-HFP%aYoemCSViGA6wo8moO^?W>L}MhiKTnY zzgoRytp)|f=P4*C0Ignm8*;Di+;t(=DhBQPFKS~$69%bMH@ji7^SvKDIe$ear$p&~ zvS}++Cv?T>`%N)9Id>=Wq_6=ERob~C{PKr9`o%B!l+GPP<%We`2geXfG#MV*PBY&+ z9;svsJN=jCTqI(bd@z^aw}wZHm)M~mW$ILUmbJjd7DXdoP z6FO7DY16{a?sf;PjF@|Bb0ej-8+`-fIFF4sdd}0aLvb85FZQ?h3NXtq?j8`kk44ss zyGJqX!p9i+LkfT|d<+Ii3g3@3Yn+U!6L-}=FA;kI_lntx3hvLou|}vvb|TCo+ui1s zEpGU?p72m(9fpz#Ow+zd=_3dzL-f z$f_S%o4DXX;MTESN-?fo7_so^+=!CA;KQlreK^7^d^nQeuUk*D-Iz&yYd0CLzYXr$}_`~W-wXunZ^XUZEO zc`;^E{(OHiK(@32e_{3~QkE$1NetYZY!6g}*Jqqzx+7)Vi_=}y-PJflX=tqIZ^N_a z!o5f+B0Bo4TRzs^(z??1lSQ4gll>&xacoLH;8Q;+O9`BFkc1q}I!N%8%OAlkz%?o% z;?l_1pk6P^d6F{QcKSwK#ev%$ySUyB8Tp7MTo2G*atqh#{FN1k6YeIs3p^$@1-EMY zL)UL+^pEyR5&#hQ8Qafu(ff1j^xnT%7pQ8o=zo(V# zU)(BGZ$;VP)OTcP6D~3mh*E7_24p?}?f~2mS-bHUEB;K#H-5~p5^p*kx6Dwb!ubN* z9KjTTSAY0-Y{T2#WuCjMxF1g8cSiu+J8Y%_TStbMV!Lgsp`#=+$WQb{xkBJppp*hd_>y5bY{pAn`|GjQ{$vt zpj(BeDM-Zod^E-*L(ytycXzn4sgklUUA2{CNw8s1?RcFJ-l*F0c3kad#~^14@dIFQ zTsNzTqbZCynuN+OV@H$7X`*$8S`y=FbYxIkm`*`#H!RI$eGb#uhn)<`x>7OV@*F2F zmlvrkZ|aKj);BP16Nhct$qD^X7raA;`VK|Bg{HJM7wi#-Cc*j8?s@``S6dxSN zImkNggHlk^R^2K(JnR`Tiy55lHp2s1GfCQk-`n>%k>n};E9OK*M)Iu7V3JT>TUn~m z8H8r_N7YX>DFw|`{EmHx!hXYZi^aN!>*R|au}SeAQE8-m0DZHMt24!Yo7MWwOH^X& zZ9IWeS#W`Vj?jV-iTX&ROqA)7ZZ9@UKI?)Fp+t`YD!?uE0rvyegK$s*3qh0k9Zm*& za;Q{jCq5=Tq{ck1&rWIPsoku8ZzLT9L?zvmt`we)X;0_%vyoAtDB4a;CekvADe5?M z(#Hj!?h$(m90cG{p-r2F{DerY%)pCr?82}U?l^~+H??~ce>iNiYV;AWYAd3m>a6<6Qi1Z7-Ly^d(qj2d7jb-Sb<$vOZU0qOu4 z{QKdU9T}dVT7S~V;G$woy_f`+SFs1XV{spM`g!a7SW%?GZz+=Qx5xC?)mTArx=iUz z>nVo}DNUD8aJ|}P_Wh1$G8+Iq*<3FZI|#m#n4JaUa24X-VpoBN-##?M>UV5?ZH-Xf zGv#?*Z*qnE<0?jXBuBhV;UeM#E)|jXJBpZxA_8yG+(&FL(7*!1d+cg;tzwiZZAqA_ z_Q>bFRjWYNd}nwvvo>^$3yuT}>|pN=~iV>NmkZoD9vcHV@rDdxk0kZBtN! zU2TSHIi!Br{IKS1@UEaLDSq#MNI_J&tS5f;zT23GR^8FmbBqES@WWVPSqS7uFdo1# zDUd4?J$jc|q@(c$lfpbQEWziUfVS1Gw>OJQPtl3@E?$m4V!eZ)cRLvhkKtTO@^jM8 z<$bRcp?dhJd_3%C5kaPeu&J2?W| z#E`{X0zrU{ZMG_@&~q6^a#YCtC6!@_enhww`lTNT)jsouZ|XMC#RZ<>PgaZ8Hsn}3 zM~CPoh|UeMA^asjI%|yfn1fSr>B|&e++u-BNZb!#mEA#4g`}*GlV(h_wU&9lxGiU3 zn%=CiCE-a12JSJNmbZQ}cf`u6^}%eR`ZzksRA)h(2j}i=Q{yf9FO6;|ZgUd=*RXiV z0XlSp;t*1hZ9UC?dXHI9!CAe2~zzqGDS`_Kcbh2u$sOQR$`~4?QN=) zaTVeLvCGrEO%s15I-~f_fmkA|vM*$nk`ST#;>~nw?I{NCpV=dz6_Rxt6a*cyLbu2Am^TBvz+$LeFROwLYz0LHPI90Z5d4sBae`#G-0k8?M1F+mP>7{lY-i?sD z0JQAjkF@~Y|HJx~ying1T_cALCGs2n3Ao6bRX;DIQoWE%-z|D&8vIZ44c{&5L za3$_iADLIP;D3`eF`yHGy%iQgWm!F*q1Q*ihDr6DLF9tw@w7BKsJf70!`Db;#(6)Uu(wMW@H z2ad{W+=GWCL+xcxDZgYVVMBNAKNqwnda$_wH20TmWqUX9PGe$4zsX|1%Q5ITNFYM| zfa$F1!P;?H>MecnYQA^?Nomk9t^PmN#im8v9UC-Ea1 zHqB*ZFHy8Z;f0ge_ly)_acSwv2~`@kMsxp(4blwY7jRcaeFRa@2uVs=UF`DOVrQ~Y zUAn%k&;=5z+nWl%*mQR6L@mXB*;HIi&?wbg>X#zGY(@y1OIvNn{8k&Qbj>vgKvzfS zRzL9*cngpw#Wj~Xd4^%PV>(8P7nVRJ`pzodvdTxPfO@JB^jq(&>G9ut)o*pk^j%Qi&V2k}(9d zpV6G~frhdZl9-Wz2D@>gx-8S?CCkQYQ>!l8+N`vEF++~d<^s~O#*e%*`tp?Azp@hv zBSfx|xOCphFH>rQ{jsjwG|+uRSKGe`?bDo5nYpd9@Wm{df9Pfr>kHrp;0%D!VT^l} zd=CKBS~MJje>)ID{)FoGJaec<05_NJdD&iZT_QJ#;GXtkFiiSQ;0--l;yML z=NUq^Lt(a3-&e7t8NSG4`+4O}+_POkCBQZ21Pa7&4nz~b>&%7UmC?BRo>AAi*VrkW zG5t#eC(*b;tZMyH>>2eLwnIGa@pED_w7w=N6k{C^Mi(~hHV~;)n|%*f7{)wW^;-cJ z$?LulzttMpit`IJico^_FN-)9|mF-wI;#GmGP&XS05)fQ#hPQ9<@ zf|(tNKG(3nAOZIFXDc?0dcI}sq0VrWlarN+9#^XL55fAso zi>;AprsnQAH06Ek^fo4O1(M65_=s0wk_-ntW8W11#9M-2h#MgxyFR2C5$_XBu5wKg zs-v@GP4psC43qS*V=EdIdH1WR1XoarLLCdvC>opxy+srH0snb`5LS1oGG;gv4|w}Q z&4np14g^Pzm#+(lRod?5`4<|Lsf(rPF^Xg|ZS`Byp!vv^Wa-cTr)vcMhL!3bASWi; z=R*QqAg#<;Si^ez%24cm%l#8SWECv5<~o70yD_^?w*0gq_Ca=qXT@88G(uQ>%KEI$ z^KSuH8#k0+&f9ZfN36c^d`IjhH|M4qYVkDkR?)NNWN9t{lMI*ufVi)-zQ71Z+oy7K zL6w+eF}I%}5rfEZHsZ+GJ1uJvgi`q#)e0?vz zOy-cc5qx_t7ehusahOcA-`xiRiB1Enm)KbRoCus%Ly69vj<

k`wneW{v>TqJ^dJ^D6&dmcb3Rt1Xq8On)~ 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ℜN~TRN3NU|I0|fx=^O=Qy;F#{DX=x+Wm_PqIg&s3W<`tB50sHHU?waLhC7PHE+7{ecyd(zXXI?%?hr6YhxkaM_adl7w zMLm!DXZb7irb*d+`vOsQ-a9LKQ6xppSTO{}7>E^;Q}DS{&gz=UdsdJodi5({L|Irc zmStgkTKftJ+4iCBgS4fHL@6r}(h@M1SpO=pn1lS!FPD&T90%c&&=>k7A^20BrDA(U zKb;WHlk*CBhl02-q$wKu9yNp?nH_zIFs3W&^N9MObrW<1?l;2BjQSL|NFmw!0C4KA z6$ZSLQ;=mH67`UOgU(se$1$IpF})0tg|tbgfDn}ud;;UrfNXTS@LMi@Q59F#q$J6< zP1>F9P6g(;Q_5GkNdL_1m4)i(j1aDmue3{9;x?`PPh4t4;iqPn@WP?Jc~?Jg+%6#Q z9|>Cdw+*=QEhXU2mw((aHI&7|;kdX{TI9XjCvj-+T_Q7Mn|#esPj#)7>h@H6*bEYA z$g4=bXfru&o?TK{v!4K`+NbVil8?3CXb`e11NaFvHM)VH!>$)N_y|c#e@Xs^_mrG6 zNN}FUSMws*s}_jA7|GBVh)ci~5O9AHw%zq9pM)FkF~RBCDu}nRB<0zz5N$ZtJwzSt zR31_t+bex1?1|SpJvF~JV-Dx9g(2N}=$*WcD%;7>gu`JYYI^&mk#OTsnm3B-dVt3r z>2E9^c>ZK%(Al!{G@pKHOyYTnI3Ea_XXOCrZ%U*pvkb%Rvt{WB7K`I^yvS7mLN|O{ zr$P9(yj;W&>tZF(tDRWm`hk7estm+y`5DA#TEwF0JhBspFZ68GR9L?hmH3c51Agrk zF<@oVfR#i`Rqfi$b&(CmSsDcw=;)+PQxG!^t)O*)(GEd|{*}83zoBiF6xB-5fYqzH zr8jf4Wl%l=w~Gx%SWTkp(03iM8ssSv5n#?S;ytS?$?6UOQo}W7SxiBE>Hv=EIckKH zO#@B|+!C&Y>addXd=Ieo+KJJyTDXm-4M=^KDf?xLA)jXuFYz1_W-atu6&r_8MRFSfb#Rv3s3A;1oJ$SKBcX7u<#9;2%f^Y=!r(cjTsiM{-rbZ${8) z*6v?#D{0tP&I`Cr1Yo^)a9xsW{f^{Huq0vuD*4ce6ryh6eV-7CUPOnCBVFzrLK9ed z0JqaJHL^r8Qf3kj)d~7RTktV%fyM}gl$U0Pqf>zUS%Su#SkeGs@>zl=7_dm+iM`fn z+OX00y4%wSq=LipDD#&-ew$`j!|TZw@AYv(aeDTY0FCx;?6ubU>5mCB4h6ssz+SAe z9u`jnFG~mnBse)t#$N+ahJfdCb`cKiYs{i`PnL`dD#mc%1)=UD0t#7YXQbMv+F>hXbZZXi}W8_&CQ2BcHtb^PjVgquAE zZbF7w8;r5;V^%3*ZM@FfYruM{DYeNM>mjfTz#_ZZ7^_1MVwIVr-L=ICs}OerLE*H7 ztwxwlyMURLkOG2`G~$kX%yo9?fN1%0Cp`U~?rEO>tX;x$2c#k69l_$c((HCL<>~ae z$POWz{H$C~k_>2a9oH%@^oNQA;H*2hx zIy{nKbv|#5)dmE@vM(58^}(`pV1}t>g=~0k)dOgeNtuo{*Q8sx(_5B5Ahj#&3NmfH zu)m^%gEDNSRi#tQ;820Ip6iWoo;bE+6yv^4ud8k6YM=%E(B2^+b+x(vZy+Um_4=3? zblRP9&3tPFA|yNDk5=4q?{e$c;Rd$fmfZj+PCZ=A_Rx>gc9=LEg6HC?!E1f99twR^ zAFrKYizTSJ&mhieW1KP2F(0G(H8W^reoYS)&I!_DTU@PHD%yg@Z^Ou6FMUJ5bao!( zAR+AG+~AjYmC~&Ou{xEbk^_NQAD1CI00ga4^G497KII5~4!~>>&f+zs56&*@Y$K%H zM?a&J*zPcE;GTcM@s-pHX+7m<*BXe(&|%T@ukoh@DQrF&YPmoE9sq!iITKb zhZK_p9NWFz=04TQibcp<3jk4&qD34-R;(t-g(dqsnvy<%Ks&lWM|}johj)FM-|=eg zJml-z4@=Nk$J;DVw-GK&qi!Z>O8Np0-mX4^G*Wx?@9-9C-!^qfD+1l!)CoT28r+W(NEWux0s&+(YCo2Y|7M$`2ocDFsE`8RA-C)sD zkusl(1NzVcju4`aK!AQMfx-qvkroVGnEDwhOH>2ZavVp}Xkh(~aElXwTS_VAR@P{+ zs!ryvpEe!!9nwW&wS(IC3RiP-O%x}6PVu<|;uJLR9JonQ z(Y0!J(ar2!zawJ*=55eD&*nCDi;iu+H|z zn4O>x61%sXvWZ@{)w}7+TTY87fhqU(*7R%nlh* zOSF9!B7utleT8$sUQfJ!J>tm_gp=)^K^xxSJdLo+ykLT41UD03tF@^|ke4}ct#DoZ zZG(YuYyk)-gcZKg;Y7FZJN2`QF$$)9Af33 zkE~liH$kgziKFdj{HpZ@zl7#Bv~GdfPlm=cbF#B=mUM6rI~G4LRX(FYT~i7mZ6&_? z>HlOMV^2nk3^1dyhv;fkbj1o=cD6B7g{xqtUrGsU;wy#M<5o>l%OSEGx)VKnZJLe~<>gw;E=$V6Z* z_^Vr%uiUb1TBjnvI%K;8FWMZHjWy?YHQqE8chr8&#Kjnu~FYS+(h za+0sscapb=D_U)k+Jl#TZ!aQGfS@z~p}Q{>@Cn4#qojOTJ%M4gUd9gc<-vvH5dgFU zXfR*o4!{`^G}R%dW{&o9M5tBAY7E~8f%-XiBxwP~J_4u7$qq58M2F`S;MXVs!-C&o zp=$ND2HCdblV4P|%@&Xzaq}HwF{Ove^rqkeaX$Xd%I%<<=(1kLSOS`Sb04 z5r2(mrJDcNXl?1Q?Ob**OgJ-2N=nVFe*A-He;|QpT3a<#bTgDaZ%Ut#{<8#jR2M$kw`x7PkvI282U1THfsNh=7eih|6vmm|0Qg@Lq z@h<0jF!Y%jYu3ozB0d2(VbWZr;O&wO-GrUyoR_4z*iHBwoErck6W}K}G@*ijtcVTh zcVrX+`Pmv1qON0~P_ky_uv3B-4|f1*-;_ytoIXF}HH#{NG??DNtDSUdnT4n!CnMfk@-uLc1YX@14(W3X>%tLt5#azD=$LW# zrdY(@N+!ceWD3}?zknJ5ME#Kn5H0x4A*f)}y0Vk<#l8!4IJS#wQ6G+lO(2Z}u@5h$ zGuInxP_!)Ntl{&hqbAM^6Rv+}>N7~W-$=^YCCLV^C8GRibSsYufE(Y;#jxd&nH3B*Ztx|1smv}jfQ;xm88+c4Mk*x;nr7k9 z$)Ynf!$4YK5ur-ms{99TXsY}RKD!8IdPVRnp-&HZOy-|yJSJZq?m5^eh?w&FM1}A1 zP=R=L>@Zrq0mQR@ZCm0LJ?om%Is#$iYJD% zPDN-w9s*9+)W;BHch+7j^@(~iOYWt2Yy!wF89+MuybVEBVDx)wJF`Vzxiu|74qGpM zQ1@x=BCT+W$y6tmFzo=)PpC)Fa@YkIMRRwg?CQ7ItbYvBzsj4y_36?#!GYqQ z>{bk!puO#U!1-TPI|#fBP_~0#E(hxecB8_70MlF}g>re&IDzms_>}-JKdGpDN$kI4OGQ5e2N!7qKq%{< z|La9q^C)cgC(F=_I$0ol3gRI=Y-x)oWKnlM8nhSC<}=po^Dk|6n`a~xb`ZUaE9cC3Ji(GYc7z_Qrzo|dQeHVzc5QfJug+l|ST?Hn09*X8s_7No10kWClv1a4|~LX=Yq7Laq|c^dH$XiVtGV$o)J| zH)%z3pWSonh*@bpHF;)2h7%}%!JU=a{~NYAzazmpE3P<&GMwU5$Gc9>qH$Oc8Zx!^ zm<%T#9#D^8oSi>xG$Se0xy*Qv&u8QaA`~%-=mh!^(DXL?V-NlcN=s_szg_bfx)=&e zr4=2vnNoF}!EM;TC^RdlfmcUK@XJl06&E({YR5V@Di3iAzmB#9`-Fji)3Ild?$i6m zZ*`0|cOwP?EoNz&A{YiP3MqIqa?phm^3?&z$n1Ki$;iy)02DYKjd3ObCxu$+WQ3E; z0!|49sW+W&tCSQs4oev{48iTaH8VXzVD4 z$%JB~E6!rTn0YP*zz?(u>yL(^6#?hk_7+97ASn8x7L4*r+rVpVxvbKThVKw*bqg5? zb1m7AK<;diu4fP|ifwTg?1xs1iQLk_tfLHG@NZ~Zdq$?U;k90C0S?J9HJCrS>?1R8QhuQzYrC+4~ z(M<7$9Ul?E!XQ;-lHgZ$9QKnYHMt@`;8R691RdBjis_Y5wMo_khk3^}Dg@F*R)fV{ z36Nm;)dz?#5aqYHCfklb@jh+fY%j$AXis-Y6LstqI$Eq{*Z4r@=-mCXsIg8)_Zcq;&_)Me2v&+)C2X|1_TTi}VAzy@$6DL5p z`<&@LfdFY%?sK^|gkBzVfxOtjU0LD>dCcFUQa76;1!%;_bD9xOI~^U0dqUA3U>~eW zT^;~-UQJg~OOUH|LWdojvKUlgx!-tNYO%i1LW=0R35P*C7YO=TCiS2<#64b3udlEu z1O9vrdWoAcCLZUJfiP&DMQh2dF=jRhn9aZ+Y|a=jlK{LF((QKh2NA2*7KD&z>RUX@ zmc4SSEZC#7q7l>3hpy@yeh8O{SpN`q+rKA~{gdz@CxwY8}qEi4AhPCKO@{e|0f zPON9}txO=0AEf9S{*d4UMP*d>4AY9Wv7EcsZCifi!*4$}b92b8vLJUpv|ETDu{@ba z1PNV_;kq{&>7F2TiicL{6u7g$Oh+R-`{Bfg6y&WeyN)6ZQH%?&4)RuFW(FIDegsyh zrdEdpe`i-`@Y3f2sPMt{8FMrhN=FA(oqwO-o#%VeR2bh_S0{j$kK^-86r5k8fuL>1 zbqYWnAD{)DtGJino+f=zDQ1$QagL@og!#TO$;(cQ{%AK-d1aa99_o7w6q`$AK!-hbiFi(ekqqP9+T%EL`obR)-?q>sGc+`WDJ;6|ASU04a}am|y%r z<>6@Z{&$o@(ws4W{T|8kA?EqDdqBH(EAZm_wW#)x2x=CLHXs{mm_t6fcYc1fXViOhYK$y`FAZL@3eoge6DqwqQ%q7ANyVi(Q6OZW~S-rFX z#liI}Lqdiz-~d{}@QSf^=V6U$fmYzy~~?WjszA>Wa#y1Kt& zS=30rw%_9T**Das2+7=hP#3&0VUg-s7$eTlh-ns1=MF4cpiMj_JrY%?lZhwpdL|x- zeBoG;YRf2mf-+yTPHleip$GBUtf*n-%7Sc|+Y!{FABv!a%3pH_{^?&q^dKVWM$uWh zO^|`;qVa|?Tg!!Pn*vyPlbtGWnxO*uj{Xc~@rO`KDF2>Ixfw*u;~mQm(NdRi^iZAg z4}S`&(H(IX-h)MdSyC1pC=J-~A7)yU(>E;K+HJkxa|VL+l|a{OR47GA-a}%RJ0x4XUc z*BQ-A36Nem7z3C;piSyMh?S1&s->Vd7#O#X5eG)m+BoxuI+iw5;uq^+3Asc0ArFeOG*R6xl z0e6FWEDxx5t9hMTa+}+GnPEhf1a(pv#?rj#tVpN&BZD?~K=mXauP#be=X-GK;oC~e zBirWBX+>mQk;aiydalOs!oJ5vYlWrmzpj#X6a!mzfHE(C1YDRrDKi@7lHj^Rwj968A{6|TMab6hOa9)zt$T#!fqnbAS;$1Y{WCk~^R>!-AeHdz z_S{*$X|3@%T#5?AzpMk@k}Uk#*51P6q-?1U&QdHG-DNI^|WHWzQ|I2P_}DmFR@HHj|&ke z0zvw6xmPEGYA*wC2rDbY4e35F7Yuk!^_b%n*NO<1$Qoz>ESjC~#HRcm+4`idqhxRtcZ-@g4{RGHx@zMz$fICe9$V8r*zR3%>$8*O0PMZV66fFLG&x*X0R10C~bzwf)ir%p|Pq zp0%BtX#{sjUG33@w4!vd+0;6-Dvo&AkG8T7kbZKzWa`{@Ai zd$A6h^v*csMv9AVwFG^FZl?h*XnSd7!S7uJt`9Ac=p9eJ+QiLyTG3lLYOR7Qy$YR* z$i>a@pgZnsZcbcF)nXFW3WPvM&1M@HS9CHmBOu5aLz1s6^qhfx|V)|odQ zkZC$%q9yhr>Yw4lNH;+jYX0jAImk1uJb4H%UYjiU#JGIUup-^rY(??53NVU$=Jb@natD zv5!TkPDkxzzT0U7q5N0QDG{ov9{yGtGncG zOHzYS`5!q4Ur!HI8;(Ah&Z_b*qj`d(5W*!JT(2oxX;%_b=~=%`o-)c+>R05?KS00Q zV9!*!p#!?Gs_No`C@T-VrTp*Qa0pa!7lJ}!ch^h~LQP)3z*G2< z*s%QDe$(rAWJ*fT(Q^ll=K-?X>i&4qQruLPMEMc7y6Nmis2%?w3I3!Mo8HBjcONiU ze7?#CgK$zd5F0A8>^wwLlml0If6Z;+Qq;Ni?g<_%O-ymn$q@2@RTs3UzolEOUzAi? zHQVjaSlW$`?cBAGsK^D+0iVkdAJw97&hK8qXB%kNAGA-sroNt8x368d3i7mxeAqnM zT>J9+PV78~G^GO1Oax{4w3$j4Qn%U@{JrczLm}EPcMpeNP{l72t*6dxSxu>~WD7A; zo1AA$47hB3(8!W!2wZr3|U6IJ?AH7?PGrH1XB0=FpSs6Z$7Qb@IlyX9Pt-) z4?ek#mX;w6h)U4KOPZh;l~owF znkgs%l6rm~N-H}_Zn|Bs5sbVq4Td4aM>7mbq|ZRnwv|tTZ=Apri1u$J=>dPZ0@41B zA8Y$JlD2<4y`K~-s0Z?F0?=3>Q`NsEMbvgaV%-5EAc#ZwGc9^b+><*Ltw_mS+_C5w ziO3MN0wKw{ruhgnYuDz3V3Og!ap*Z(U_p|r+rK4QJ1B5w9-$NB|Fx_(F;-9xBs%_I zN8ulbfP{JsHwcF-5g`~qsK^^g?HfgJkcDrQy-t$<9s~an2;~31tp4p8k}m`r5~ym; zKu$#wNf7Jvox^l(X?OW8nidcZ;~AIYMDOE436WCbwrc?fM@#M6c?w@)iNUj&X%+P4 zAXL?tk4s0(|EeCW$N(m90$4X7Ix!5bW1AWj|M1f8HHbb$*Xm96hbVQ*b+Y{i89DaE z=Pq2*IFMY$fZlj>@)+MPq`!LPC#nr8Cy3tU;%%#l#nE#z6bSm?a5TY?c}*kdWHu0T zE^wfL7za`Yzn23UiYCa8YL^k_ykT^gBkCRI+GTA(f){4mWmU_FFoOKF0vprfD$iXr zCTgvA_d&cV7h_6Bo5q`S!#$Wb= zchZ8hfN+i`!Mb(f8iJ7fG{To2T&MZcMLzHpo9B>uXr_FZv!4Za8V*!%;Iy&{w7u{t zv08=WwwVEBcy`9iXyvM3#y~VaCVcKWY~?`7Hw*Nv9Nz1nC`T*He7Z-RU8`# zKrBq5t^M%V9{#`e!XIlul>d!uTf0^X5|2ObmEolA?OGeC$CvOJ;NZ~NXR(%L)+qxFJRPnKZLx=4FECGx_|=69OUFYo(t|C zyS~~=f=NEj&ys%}t*D=`)?%2ltL)%gr1B+hkLTC1>KwK4y`U8lw!P}hJ*I+ZevylC zY4z9k7A2p{=t62h$!zm$>Z}M%T25lwt)D7C=6su+0|+xQpIHG?mj6j9Eu{Z^7ay4m zC?-#cZ*xO;nn%z9Z*cgo^=uN_vh9e+!qw5GdkHJ$%?i|M^@Do>ye=lV-y+0)FyZVP zPbX-c&v!;gH<6RwDmi-b%` z^-N@zq+};NK;gYu9%7XPi)5nLSc9-HDI-+kuv_-KxSWD53#T*Nd3n9lSe;297>oq1 zOcan!@O}5xRIT4pzQegxMT8JI5vnDa3;kK0YX+~76?Hjnv?5_;T%Dtt399OfRS1;d z=3FsOsJ@{RC?$V}#P(l~_5(Dl^dLAl4CnA=2cX4&zDw$WS$TRbWXTyA)MUx|w6_w0 zJIBupB!pAh)H3M|!rxd*Nb76yaSvD^>oGbH~!CFHJxm6KIXDM6`Ta-wx>$)s$`3gojH3yhlU4_qsh=uafq@hD@$@D{ z?Z}SHwGku$L5ERa)qvhXwf{-UnclUfN&epFCJ72!s)l8o2i~n5jzI5sO>#hDT$Mh~ z1kVGGA$I_({Cm!ypAI;CGy2N^i&D&5-&NxLLO8h)CqK)D0X=Hg4Cs9y7|>+sv*hhs z7h!I3!YlHujU(T$4K@0XTPi>00&t=7Ih?)-F88zvjI7^Q+&yLZ3jXUAi|Y^@+RRzh zB%(TOGn$L78eH1#M7>*cAspIy0&&UOCzI**EA*{LuSc3_VkdxbcM)rlQPwnIg)8x3 zqpW$r3ZdmfbXZ}>aq0@4rWL1KE?=`Q?4Ta*2+-nOi9fYRT$s^fBluz;ap6W;!+2p| z;UX}rfWVni+L))XM*+J8;st&s_Bdddqa7ff z#8tp-nF$e1(Do6F0lE>K46>yEzny(&a9l}pX1v$k?^SmL-3>#q@j!r(xSZh(wHohs zZ{b`_z}?#293oNfc&JHip3@Mr)H?+xL-M*|>05CGx5 zCp=-UvR=PNqZ>35BTxiqD!-~%CMzp5E7LL^C@e)(CZYK&lpWrSX)Ih>J&S`}T*^h! zAMnECVwhMlEIVHKG~J!nB={Fii-SgbwngLBd#TpkFOhE!;9Y?Lif$~`&KhSg;%wOt zi7)NU>UGIBzx;}E0he=I)d@)rwcl+>4W&9b42_@63(wla>HWMs6S?2#!MQ|*~nq?Tc>QbkC}!!!w|2<#MAY$59p!eoDs>FCP|Q3DXdo7a2jbj~k^vbGdv z+?Jnm4FX=nJrlFP)K9rYm2W!b(xyGP8*M{fcuc=Sk@J8f9F3y8oQ1Pt(dgHV#~EnR z7rPBwbQ$rXKkF}EvEu$5O+5+42;J6)ATc#Ra+SWu&RZ2%jn^yeqw1 zWY`P5$7?EX3;s({d377~3(u~9;r<3q5UBIaj-T?qv?iGy0c%ABBl6p9J}pFl73kB8 zn$Pv=o8ggj>SLe#5z3s=%BOE*o-;`xEiT|GjImC6-e=Y^<#IsAp5^fPyqCOxWuTAd z{tQhKEVxvBiVq~#nM@FWBAJofuR~YjCsIA-sjf%C@~WNFK6QD2W{aBp7EKeNtNP`0 z@%;qu-&H&(*YL#w#K3#fL6?Osis}gNW z%oA;1*@Qq@TDWiIL6Hlxzli%=RNnQx7+gKhH=TGS%K@<*`IP-yLu8%5rhj zt;*B3%T?SR^oZ}}OXX@tb!;kE%NC!;%&E5mMjO@mirPQ`WC8$|o-GXsxzd+n9G|R( z(W~RyemlG)nqK>HYLiLe7mHZcYU=SU(*;M5$v5goD&_dr(DI)IzGT#xQ2=JSpQF~U zi+kfj)>Bx|7cLEqc|?Kgk(4ncLwgZdc(EFWo3^kyKx_UmZ3f9o4eCbP} zb|(8*@le-q0?lK*^@{a38xdTvWER9DH7j@CD9I5bEn-j z9>v}x!eWdWAZH2e4|WGQw(y>VI#aI}S&&Y9N@dlAEL(8@iuwu4%?VzRzT7wCBT<>O zPWt~o;G9@UMhR&_FZyVF$FOXn^h12Y%T)^r8|xBvT-S^5IX}=gQ@2VWjOMddH`=48 z_Djsobfm%a6Gz6Yuh?QSZ*QvDNyaQKqqFX|a7x>(k1jZdhs{-rBA{sT^qix(-=YBm z6JtF5CuUZiucn8KMNSNRL z_CvBR%rKNBiNt8(#<)0XadpUw`vppgs*g2Q-F47^_(V|Mxgz3ueu?VNKBy^WPMrq2 zyQtn}jiTY%Zl`>~-#(9F3=<7K+}q%HVrQ#_MVsK8qx zfV5(IMQWq!z}9MIJ}M>Uf>(%+%svQtGg$}zNp#!H;&iF!ST z4khYmK|Sy4$NTn3*~*4m%pjNJAupR3FL0r0+FamLpkTywR>tI=H2g!;2&_sY1o_-j z5T}74czF?qAb7HMzycm* zC~G=53E0N~Xj*I52-PsKRrU6O#>?}M`w99N=;^k1g0W$$t1=Vcxl*>RZl%JZS;Pq_g1>g3f=~rX559`$2U!Y||j|6(mZUu589PFDC+owVo7NH9n57!P9 zku@C;0fNJUWdPKk&W3$LtgoZPJ`atJZ(&>T?sBWg@a|Gp-JDfmf1DYSY7L$l zhU$vTt6MZdb&5J51h>(c5kA;!dZ)5+kIu9~3TNHZWa?HneV1tg63DzTZiN1l%4{2{ z4A!cNH-PTqp>LySuZL!Ogvyg|fZQiko_YgholtrD4Uh+g$}?|({1_@?Q*gGGAAf4S}jGX!WyRSJxtdz)I4};@9n9K1~SX%m^wyr@F+t*uums zVt6i+otT?q{M@29QNesT?hk(8S!XoVCqGM7o~DDS9leY_jlUD z5(?8iB{Yy-wowY%WfjEuaTy&`40PbM*ZQO}^}?DreeKQEg;d0F@Pf9DRFx3TFeiDB zre(Z`v?gJ78lRZ;``4YTwmYa>L!!INCLq!cv{%^nLV>5F-K3#o(}vhK(hw|u&5BT$ zAKleus9UpXs4E-nsEXlu&%IwDw@rP=$U8oocG3r7Yk69XxBGTMtRNJ2KNMQ679dCBr@T^JFL}Di;32y-4<&xX_C+vj=m;NpT$<@VJjqicxh? zPd?I3K)j;T$5ze@t+uxo*t^Q36I~n^liSoJij6;P0JoUb3vJNmZjF2!E~!(Ix3CPH zMyi9s9s3!mdLdA_!fd3d+uEggLqg)9tZ;57?r z_hR}IHRQhaCGng3mi|q!-fv1`{)9!@122el1Aepe*2uXEId>1m5_&}FE2JNAGCjg` zn#VBny|Ni8(uUJbq1sQ{ie}Pny$^!MZ1H7lAum${HmaYHRLG8@Y$dzQlC|5@8rmsQ zhb--1bJ)ocWvro`^Pz+5nEg88S*D+@gjx`)g~nv^ko>#1i`?c;86u+7O-i4 z@}=1J4p3XtHZ~Z5O^~oG^BpmXRBa{ZVV3wX&|nggFM|HN+Z^R7*Y2ar;}NPYsdiMhbHG<=Xn?=gI;o@SS8J_`Vi{v@%WX-$f&F zA>w4TdY1B__QbVP(uo(i3(*f1cGsN{OD5L<$d;K zpT+C=@nWVH4haSCEclK>?`P3t8bDFD45`xkM(=l9V6x+r0%NkT75eK%G10QR8);dO zXFONLZV6qkEcXL+;#Y{t#Dq%xjv&Q7CaSyaPJ`;sii~h6L=_T?pDwr0)7*bWNfjsB z3(tF_^`gq4sb$%|4N`b-rtFMZ+|L-*sM|{Rm#Ic5xIah9FM}B}g>mYD%;BwG>!fTka@b?OCvN-j|1>T=YwP|@2 z9|h_i;QDyPw%vI`YFI%7d1Ep>kf$<)PXXCBX9=oRCeunx9l2S;F`I)|Zwi%EU^AqSEhWi-z@0f-DS(x`vUI+aP zf&L-x|DcYdd<;mkZ7wo~WShiQ1jANR9#R_^%M}r%+5}&b5AlR3Rkh=whu*F{*F(QO z*0}0)6zeie&F}PSx~gz?=mMD zRdctwuaK5dN;RzBe!nzNLV$HF#M4Ru9k4%k&YiFKwQg(B12ruD^0*XB>t<%G16(50RwMgYb0S2V-CV2ScsqMF`m=#{9S z))fecfoX4(XS;ve@;9C+L=QGt>U11amb-&Z(XUtvLh(#j|j00 z6aT3tCdbm33!6ERD0zRp z)hFyd?a<~al?erR8_m1=E!G9KDtyEov*izeMYviz&=0b-Hi4&d+|&>94xUH6v3!r!)p;l zFz&C=Dxp^3*==QctukP!GDXhL*VGr()U4`7Z3mWD%5>e|*UMYVHUL_mUQ47Sb{cmb zFca$xNXWnBbnY^9*DBl%b*_G*DS@cm@gd3iZ=i0y5&{ZT`uxvKb$g>NzL7=e0R6cI zL3D{2=hmS;rHb9U10iHzWFb#*9t&CLafJCpaNqR1g0uZ7hyoT6orhFLGlIbkIpMRs zRje12&AeTB!W(#LN13&6`QP*`ZTkAi`rp9aPk>a2~J5Qb%?pGm6DonD#y!oUv zB_B@iKgBJ6^kaPFSXj}toi|;={Ht(r941d^cHDgpk7z?uo0c5>{b%q=i>jOVkH`wKfI`soOimc{tOn#j4=wYzi8VNy)#knB)z}cr~%AeY4dhZ_(}e zI7v=m6>z^n-L0jtvEZf`i{cQzpG&hCns#D&zkEtnUD1s9K>Svuu&h5meqOjw(UI`D z$*Vsx9l1jR^SdLW>GDoZ5GsO#rrms_CM?c;a6xn5qS~$1eCkyhv5#Js5gVBw2eJ?l zoYK>P|Nk02IPot8ru3yPJP5GzVueQqt);z~E^V;mUHuq-UVY$tW3*&v>kIk`lw(ks z{6>@iKMckBZ}>1A?F%e4#KWX*9Qmy>wFU>Fw$pGB+SkBAxZ3G^>sIAGiW;~l`Qy@i zhiiQ$miW{dBN)hF%$EugDtNFsQ(L044)YMYDY&&+Q4aS7@w8n@``sNHqnu zDnao$rtxf21Pp}?TGF7qeYOe&#{S21A;Vh|^N*f70 zkeXb**icv&G}KpUXoxwg$5i0XJ@vY(vL;BKkstgZp96D*S&v!^M{$qhJ#&N}F?-M> zDVQT(-+kr)IzmG_Ka(!o%YU%4XkAH;M5{7peR zsXBs70*8s5^y)&dn8*%^y7S4PkXuzo(56-ow;()!$0U%5sgFpovES)ASo275fkZv* z(W){SAwV+|9NIr<0S~SMic6>qnE;-fvVCXrXgxn$Q+b7^)3o@nj)2y1b<$+QDBeag z76L_u5}0$n>x}7BLA&CBgU)MCIs2*2TnrTSTQ}aiL396z!U(Bxj9;WWjOz7Z)FmUM zf&=2`k5?npujg+p<)VR}nhKQsY*3blf2ed^WZ<9G^7MI!H7In?Gq{4CttEPfJX!h% zg`YPdo?-MS;Sp(}{JaH+;>z9V>rK0@RTj-qkQegQ)0~rT5>6??+QrN4{{w^tIGm%S~7YN?h9-a6%P_zG*gnP;^7F z2?b)pT;i9V@MnZC0Oon*q%AQYOd{UzWD`qCSuYIN@L~v`y0McRYt6lnA_y5DEm|MP z{FFSt)fy!rmtWy@le6hnbh?T18PKkPMKG@(Uu$i(oOSfj#{0nyn9eb9S6R($c z7DkJ81Qj^hUYnyu67_L%w3R-1md(iikdLE5)?7%>i?zfn=}|yu`RgN$`E<4TJA%#g zhsalxTS*y3V28%0TziWp>eYZ^VP0*!!dG1An#c^TgP87bI!hwr`4`BK5R0$k>k$^G z|0I-s3B9suKGdHM=x$DwcW19=$+@4PCo$udCf6gq#;;xr>?pp^z>XZ+4w6e8^Y`~Q zsVXQa8M2Y7M9dYR2Naf5I#MSv5(_H~pjcR9!d1&GUx`g|oi30L&-N^^n90ONK=)e| zJ{)@q#<}5d*36I_;~rM0NNmX11%=Kasg8K(B(OB_?n&Q~{+szHT`z5}EOC6)f8wGX zh2jAep-|A{rS%yO+)vR60hCx%aH2@?JZ$l+#SF_yyVg^}gvK3?*|__zwon7dZT_;J zdfcsO(ziS48G#~6EyS{LmLb-UNb@%eS4N;$H)OO##?}IDH39e#%@y(hcuvK5O5Y~~ zk}?bf(l~cMAoJ7JC5{`OQR&wBc%COTJOg_Pfi&`o>y41r@=!;3r}6swvJU9qzTa}J z8ncnbLScdwmsfwU>tR)%dN%FMQz997*sN1-LgBbowVfX(1xhTl`b`NSydc_PYSm<>hZB1oe~7F4n^f&It9k@g z-{#&$0k5md=0a6_@WTjvZQXzoT-TQ3{uz}oj8(r}+}q89%zlri4VnGEAhMRFoZUG} z=#r+bb!M<}Pz1S6l}Tun+ng&dz&})&JN|bX)M*>0_R>z>m3ZscyrL~i6>4g6QxF4& zh*9%`C1xXy1i-MoXxgy6%!sdL)#W{LOxlg8ph|p}J1mQ#00F31oAM>*&wh=E)uu)i z+T4h8z2VSnnaYnYJh8}%f1a(TVxdfgA~Sz1=O*T@Ez^e=7LD1M$zmWhV_n}cW`#dP zo_-CFh&ftp3o(O9U_>@W`oGD-b$MKNgoUs*H^KGtdSOAxprR==egC%ju=#BC#!774 zn61XWCEvOs%1ujie}h~CQfet{YaMfDtSTpuDvPK#iC`b7plpch@g*1V9#A6$f)u)Io1WFr-LR) z>pw`GwT;?{-G+b=TR#Tn_vTxX3v1qh4iN)K zwkH9UUp3f%KH!w?QbVE>J(9>=0nF~fx6%ymlo#KrH#MqUVos8ifkT;OQtu&e^G+!Y z-zW4*Yz|{_O8SJ{C&6y?74jiC(gjAaTu51GHNoKJn~BFTJOhJ9!vmJ@$M95FjKo;% z^d$(!7bKBtFf~;tQOArW3SQ>QownE^hiCU64%;Wh9{643{u}ZmbPA(+f{(DeFiraJ zV9aOUw$eK}$-xh_{*0~67M&40*bmWUXj$z!x0I+7dm;}c>YT78LGh_huRS@TI!d$~ zPKWVvlXT2TV!Xq%!0Dgk#XU`kiEt*vED6zAIE!Y#V=hOs56R2T`r_>IMAK)Q_T#Nj zD?*k#IWe+SS@}@E4xtv={|^W%FNFXA01XNUPg6}qVRT^_)P@5P*8Bw;%!UJaob0;^ zSd>?mH$LxHyu46sidM>KDkkYb6j{Wm*hzPY#w5m0TGR1ANq0L1lu#{DPEly$^nAk( zA|ksef`EvCEP{xDs3407$i6AKup_bvB0Ks&_pK@@L?<)f%=gUmJ)+-0hs- zIp^M6%)@_V{sI1RB#xp(!e1D5TW7J2=zzpY#;EO+NDm36HVo+`lN?~w?8LTqh=e|1 zFOjzSrFd{8W8IMxz- ziL;d9d|@wiu!0sNK@2T9Ad(7Y5-G!F8?skf59ug@ZfSrNYpM7E+ZuJ~s-q1w4o2fW z6O+wm>-RC38KbsCD3#esBn<5YV|Peszn`J4B@PGdMIbY9(}JbR>@U zzd&oGLeK`;UU)}2$wUW04eW)efy|d8sZ``>C6d}Q#1B90X4EA1Hha;;a~&iw%6kur z?8Iok87BuAemh507ilRlHD?Gb7$E4cbU*5_Q0#=L4v1`6b%3hX4vDOs#4^MxvXzQ# z*_qY&P9(NRy%nPwwE~F@*Iuj`GO0)e8m5rQ=zSxSID%>lw%I`o_esSfq}n2xjNu({ zwz3yn?}c7DIvtQmWfliTQYX~$_kVyP7THOij07H7soMkdwn0f|V z&!`^~?-xU3LC=y%N3jgRlN}xw>yULFM6)GF6_^|gRyoy? zHtv-I1~43!8dM&v-vN{nG}_4tX*bx@UTZ)a8KViq2-P^t!B7*R$`NTcs|FmIooK&Q z!l-{Pw6+tQnyoiS7;5W))Me@R!aRE!2x6PNTi#HH6xvHR0dj5FY-wh`9$i>(60Ns} zi;rzS-ee|Za3fO&GhqmmbsHGcWZlLM3}L$NV+MzbC18f(0^rHi3~tO!;KH0CP0iPD zfU6A*WxBz{1U>v19)7%TBV3`+CirY-7}L!rn~h9186jRX5Wd9B#MIo##MH=Sv#}Ez zZ5V&$L$d`BHOx%ROpJ_7HW-;~WEl2l!S%<$pmM!lc@5Q>&R>JA;OxhYq0G!op`q}# zdDBLQG~2umkjKnf!Vv4%0rz?nfrHo)0M?1Y%o*Gqx?}$F`t{5^s1$r(m}iCb$5HU5 z)joldKn3hb^GzR{Fc^%)29pg?4+i6g9&BWYjnHF!BhlYSLOVx8pxc0mKQ?FHhQdOnGQlUG&NoHzcD`xwQWblpE zq1c38Y%)a!%*)2j=yo$`0GR)#b!aky0Nc!vp9z3S@R_}pOlWU32Z-B6P5@2-y+SZ0 z2dgg`?av$pUrUfOIykXaYkub7Ad&(T>JP(w2Kp!!|7-@5wLWv+2gc?g*e(@XK^K-I z2O#)KrH4j>&jcU;g<%jHZIPp-o~01%(`Zj&o>V3iIj~jG@JwztZ(Bkob`mrzyTEtI z&{)i?zLw&x!e$vAOK=T#l7k{gg>N*HZfhwga!FVE;Rz2~ud4SR@1gClWg_ z+LmG`02VN01?5Uhd!e%k8Uacq5JEozUf?a!8SiSWmQH zuoaLCh895D1{$LZH^@`!E_{LpXZfN>0)y{&es0LTXX)%Dfa$jYnJ7v)un*PwijvF| zEmSg97KUr-EITZ6v@MKtVqLRkVHUKNRYBtO&-XCo=R*5KjPBRuwo(Zi zzwaTc5P^oig7rvCZNQh=2|ja{NswBDy+Nn|Etkoik>gr{Ltz%eE&(7IV2Mm%BNn02 z*rg25kdtwiIvtP}<~iCb{0mr>J<=8AAQAL|+;!k)!I+%DyFoKW@@4`e%w2;G&s}da zQMopqzXqGuUoh_m0yIkkLuQjg#YBQHpcWKKD*O1E6+8xiCR0%cBNdG|`_FN*TCw>* z$MfC3!Z?UVS>00KDwZ9VU#{FLF3dU%qbb-0N(QvfKsz6Xtpmjn?-$8K6UPPU`VHvP zeyK?8Bv8@X<#KwXSLqo7X$iFry<{}DL5u7lMndzTw$09}Fwf4O(c8w(n~g-eZw^z` zw>d(f%1YMS#)v8y=0S%K8pqb!L5Ea-l|MUd}DIx)YERcyI zma^RsQHm3r(ST+e9ClhSSpD&8LxIo{bkS*_5FlCp&XHBy?e?g%W^9bJUa$*!QxplQ z^!^{&8l;P=5F!mk*lA$DZ=+!KrqzaP=lSR7Xsy5slwmIU*e|x1IEauuut)7LM9#=6 zMD`*lTOmZB_FqUv&Nc{ag*GtJLgxcAD>)! zBs6OWJNt|ASZ%vV0uv`M*1JC5)~7vF4XnwnlRx%m;2cGlEF52=K}tq5@X6`6`kPc0$H9 z=VT3;w^iRkcf^q1Nax-zThOLIIPX)KlkS4&dyt9hsXS851M{23&w%^~i7mpG!ZUOJ zXsHSvI2$P%Ra@1kMxP<8Msvexzlvlky))WbnB{0~$6oFb{$jU4#C+?wsyzKs1W6VQ zw9@K9(H!Tj%L(R{okpSDhIb;UQEHN=TuNaa(q=~gOR>F;RD9S@xWLoueu*@L#YH6_ z|4RslBvN)o01<)#B9&y#Quj*~5y2Fr&hFa>=E{&?g5@zR_>I*Si4}m7xFvQnJ zUo!l!(QNG!%4|heg5CR_?GHeLi@X{r+|sW_3N1BKu|&-Uv8eXf5<8)zG@A!TfqrF3DzhAO%J{nxMLnyU(k~)LKQbjA(=ifWq+Y9GLdG5JXpp1|e>jcd9 zH^1AW5=}dRDk!4qd2gkPn+$gc)F*NTh}w^)irFESDp5{{y+-aC!af*KLq>B4Kq^uN zKxcdAE!8`t1tX)g!`WW8?t4hl*XPy!};UJ4Ii6BcK*jrn?K$Fda3uCEN0c>XVB>{KzBhaV6-*CJ`f_vbwZDgYakR? zu~L2L3Ysq)BLu$I`;ES1ah|&iEh`{T$+nN&g;sx7LUW-aw&_a8338Q=^COklYZp!f zJ5Jz>Q0R=>!{ECi%iL{h%6tgLt4YQ@V*CL#SI7>qg79^(UP)b6?_MNuku9(^uReV> zPu^{A3hmqk7Z5-j?%o6(ueEhSyH}7ZquFj%b2362E@Qo?z+fBnUm$^C9>NAEorlQ$C1=fnTbfc^fB>z5(0=^cCdk{ z9a^POp9dN4yxF2AtXOc1FdfEbtzeJ9%xojG^tIOi*4)!A^N`MtDYInml?pJf-2ID# zmBfDU?ma*3m1lDhxJ&o$5!teM#`}Kf=N8sto4p%0S;&M|);4=hEj|Z>c4Ah(=FK;s zf1_Off9G|tCDv>n|GC5l0;BIGj^b^M`uEOK2P5P|85;C~4MUh^DyxO6F$8zPDk5XJ z;C3x!Zs2tncs}_v@D@gMq(*Oyv5h}N(*^Tu$gF!k`72?!u_9D9WLCYF-c}x&2pt$* z<@@=8e}B;GA2S-L6d0h@L__q*d;@x9zCrnj_+j@> zMoqr7VWPY+S6pl|Q(SB|W#}J7fQ^y^c8tjns2hcGpvlt1&bBBeMIn|DnVdZesnNcG zgQ{kk{~#6$b_*S?WnzImo)FJP9>O<<9_!!C_2vk~U{U4)?_awxPwm<9E%cq)1K1>z zM-}uQw89=y7 zPWzRdgVCQ4)l@PVX1U5^)DU)q4C(7zii}k7nWK$VBorBK|HZ*k=p=S_U=IE0j1mdK zVP^*zj}40){{kfj^o0Z!avJFQL5YI^jFnAbk;}3|Yo=1gqNxMgumdNzRsiEl%#%?Q$ z0KFlA5IdPU2oEqC))JTw5xe2c?qfSRqm3B2ao#6A(&0%SX)y|z-}LCAm9(di0y*cna5i46<(z=fXjPCnM`GyR3n z=0;ojSouJGUZ*Hqp))8EX)BR{89=2rB2&fp26!n&-d%(J0s{ceV&9>L!8hxaC|f57 zBN!-zC}{tj(NP?5v9Ob`m{`L*+m>D7>K z9U!g)+E!p!Sr!Uc3HwCGw$37D+-IPahWcxxSjacpr?P+zaV!$R99E?i8Cjv#btxN< z896Cc9udNSq@~7k65}^e8rh++i9POOBz7>eSFVZ}SxH0+Cu?kKy57Xhbfc*;vro0| zg&l+cY{(1U0~YoYl=l$izxFVU#`DWgs!#vqfX)fX!w4fq{Ey;wL)Chm7OX_QGM>z4 zRZ`Spv6F){G#`zk@lJGD#2Cel(56LeFpSu7?g$Yrc0|Xmz%yxz(Q>W>;)Lgf7|faY z+qc^o&flDoy)(>yvAtO6h*r87_5FWyl-m4lzZK;32rT4BKK44uPs6YWc78$UJ>({e z%D>C(V6biN7(LayEpm)p@a*sMXN*022#B>Fl0|W@y#$?4P$G^xdn@;d4(R-gx`Xg% z$iWe_uJX{<{Gk+`xkt)#DcW;S6lX}(&;uDds0J5@TjTrpe-u(>aApRNQ z9(&%%S}0{0`4tQ4>JBn13nyzKLif2#3+DqUBxWc`8*C(u8fq}O5k^yaS_d79n?Jkb zAOfXielHJl0gmKRu0SXgKv=*YdJ$|`Yr1}|naNr+GXaV*%+_xan3+QmBNNC$R1DC} z(2kP5pmFF37F?n6m7m(-!W}!n@>|jodE&QNW_x?VK_NN~1sOa#oCE14^i+aQZlM!p zY7(RX4$kn%-bOy|TJnQ^b3RR*J;0?RURQYl&H^1_vJuLJ=u8~j6M%GylY{l#;WhaU zn`)thEMN^N@@KT?E?HL&s)Z02!u|sy;eJMM{s`Az`Id?kI@Tq15W_GWL`S^D_O|G7 zjx9PhV=wwyyxxq_R2q@uvP#d?*xi;NT4vq`E3$`Orn~zy!!@TL?wjC3Pp{8MJR|-cM_rV zTIet!J$Ld3t{`JVIHTbN@gg*vMG<}F8OMbomEOX~tXE&U@R{O-DSzQp<+vi%V3y6j zTNsEfdF8o0Xn5tJLjJ2Vl(EB#SDr7Zm;*1_GlJ;OLhuFTJD?--{WNVk^jORtGSz?w zGP}K2Y#717GojQ;;<#}WItq!_KN$8)aY{>JI~+wM&rz5UM`mVjw!wLoO# z6CUF3#o=8XEH@LJdF){DuBB#3?iK%4L|NRj^0;OEKhbwsR+3}MX!5Bg7DoxhN9y#R z8Fk_Cs;iG0EBMxgA-)q6-Z|Rok>@XK_VP<^X64X&_lVUT{3}hq8qKAbaJlkEhIO`ia(LI;v&<9%n9}2} zy;xCFR{Yz1GrC66wJ354pIqXBeF03EPE!1nEQ%LYGNCINOeGB7wp35JlO2V-$K?t5 z>*-oK&uWgoCZAljk~U{6>K>1Sik_ScRE{E(HGJ7~>9M%kZe1Auy1C)U9W?ng_nX7q zS+Wdul2|+e3_AhkY#WW}dX^RA@G&0Sx(lzij_K?V?IW+AxnGXMa5S*wIEUdApS0kw z=UB>e)Wlzl#$#i;&*INddR5_2m1k+$f~rpRBF3J^`Kl%^mP5;Gk_L5P^wP_fsEH$dg+ne=mKE6}t??J|x;y1mmn^eI$9+syWeIrEEs$%y}f+w;W_orwI%` zwp@}AvPUN~Qa`yo>OS#q|3qgR!T%>Zkgb3R72H>dx3ZqAz>nhdPrucL!!Pz^X|37=RpNUYYU zah;&M9ycgnO>O8>f!BBzW{^QxazZY+dcK1YVzBx8El^Pov;w+FucM89@N1+lJF~t| z>O45S`!n~IcF6cOhP2@MSshQ`;v3ULEGw!CUm`c5*^?bP6mNLuk)Bd>GIO7weCKX? zq}LMuR=TIJ92gj9&ZjGw5477zO+LfjXlQl`|L4DKMKyACBG?+o=WEn%^l*Pc! z;(PiUS#cqLX!&haWO>C^Y5J5$tMfmm%UF>l=W|UbJ|+H9_a(uKDqbv#dE}8Dr*$j! zQk*7#16?lXgO(t4TD4L>7$vV^D>@!`dF5sA?93H_8kH;?M^6r+9AMvDTiL-C5z1Nef=e$EE*n59<0qn?$xTTt~Z zR(QXH&o`$7gKAjSu>{qzfC0yKdi>h}^9L&Z=Vsom&@Tx0ib$)`tgk!Y%;B%0Lto({ z7ISf0(>t!}6QG@^VuDb({L!IjYCq=BO)9reuZy`zB<6i7J9N?`2|JhU2cx8tlA!*!;;M=L1a zh5qY2LH^Zu4NNTcJb=H6W@!ann z`#>Qq{~XE zemFR@7EPHxA74pBqsiAaTF32S&pm-xY(~s7+!K|cez%Wg)v6*F{u+Dtit=j_RAe5% zqH_lo$T(Rp3U&4?wy!jo}z(BO$cT3%}=+Q@;x1JJG%Ywe^_ZdyPaB z(M&ugW{DNdd$-BIHjs^E6WL6*klNGap+IVPIK_YeE)k3cV%+0cDRu|DL73LVY|x%$ zDR}`qftgki9mEI*s!8fpRnE9^c&#P3Oaz`aK^%h!(o%hJ?B(0>rSV1TU@J-C1NHKN z0O$S8g7ZMbLvRBg$!SX!&E({z2YJ6s0`Z0a0CAC=*qBcKbz-Ir5ql;?^sNAwl)^F) z9Of53`N_|z*c&RyLOdLxPyLV4=gS*pM5oQu6QBHS_y+P)M2_=4FnaR&?FLNGP-Q-n z$OSWU56#IqIX5eYbF-3(8LLiUC42-G3v#O}#QUJY#wA?EOG>FaiJ6 z$_5k?33(8kBk!@whZ6E=c?e7BqmRL$HL`s3L0^N{ zqnqHmBO~>duG_z&?!~(`u1y*5089;?e*)ypBZBqa5V!L+xYeYgSvUUA^j3M$eZ7A8 z1;wkY?>15fwE06`y(_#r2BL9jXmS1HR;chZKDHGs71QbVIaf&W9(i2U7hIkE#^5Ds zsaHDqvIm>0>bf8p*2fy|#*Mto|Aj7L`P7{`Y@CFEg1e433n8|0cgSOgfSN>z!Eet7 zDTDoOtem)$Dbt$Ij@+oyOgTQd1}}+ko7K#$y&bbuJu9NsUo$4eJJslV96Y>=ihA!L zQ}?XX?PC3~E7>BX*BCY^Z~TvH)SPI6F)##uLpDFd{J^(g(Q4GC0sT`eR8 z*nDyRTDpkkVKagy)i|pZkJ?c!@k8?sDv>wnd%EV7Pp3RUKSEj}M?}+0XbUd#V8GY# z1o+N$om!@hJKrlpLp)ZWHlTUrY0e|fzJ!@@%^p-dMa2LrAS}7)71oLJAL(;q=RrP2 z@1!;5keOUt+(7Z7B2!i<%v1gJXiw04;8ZyL-_oroj(34~*vz)(Q`RwR&@=>SrpAvk zg>wuUKHduYT3NPI5ph%XO|H-t6Z*-ZPd|bKTL(V%$pBGCtk#1O4ms{~o8k=(#4i_k z{ENY#E$QfcNex3){O@D6~%48TDYx#9u~2zB$2a2G)Wz(Ftz2p`f25sqV~ zrPy&i9Djltasd%&Km__CnSz;~Cj+rg!UG35AaweVx(!3kPiH?6R0K9*24lD-(;ZkE zjW-t+x~=9PqzBmQKUh%xd#~2Ln-EH~{%a%}aH0Cnys0TlN*6($cXD_Ic%sv?+2 z5jFSuqeuXMBb}NXKaS=GM`ObMfFHM$A$k#1k99hfN*R;_srXVy{hNdU`+g&gH;0BqXUvDZYLr7EEm1#pb8<8En;_K2kT9 zWU)9WBn?vepx&I11mopWgFh&IfIsU4zNh2TkxZSRRxc2_iN7=w3!*kc#@jtF5#?d( zI=&6asZ8FUb1K^Tjk(P$W~Os4;t&0O`c?wcRrUBzsZMQwcNdIP)uV2nav>*w%ph>rN{ChCL*7zA34|6=S-(M8;u|~RiZ3AUtI4dUcxUq)-cmrW zMo{33QvA{L+Dy>p?R3exlkPYqg#dZUB!He8y_0(kV_}b2413F4%fYEnbPk1npo!xY zimE5k4Sy4TTPdiTV9}F9?=<8p;4@_re0E<<4*rmZ&rP&nev%gsaVih3RZ;XB9xZp! zcgizfQoNG`e#!+9ZtPT0D315`H;e*cK}PG(a_V!uNp@M{gU__r^pgYRAUOn?jqb*c zXi);P^{s!|L9gQOm&fVTH_Bj!uGHQ!5>&4-NCidCNQrCE%Bjh_qQ(D=4rWDaup(KB zfaOS#T#nq0Ef&Ex>|FSTag1+CpI}8m1Q1EeS1HuF>$tR83${bvuYg612K^UZd$At3 zj-yyqXIMWM&?s~`4`j^i$n(3YwG=Nm@ho47MvdnZDnCRkB4I2ku^FY(vXzR}x~Z$_ zGZb&M_{3TTy>7+Tz9OygzH3L`lCMu8fV`H~F;@$+*r99Jp_UuP34B4>P!MilXQ`I) zxUud-661eFU;kBNP8K4@LGX=NXrC??oI58t|5u4Q*Oh{A{<`3sbApS0U2ut9@OrvL zg%+u7Rxk3`9c6R=TBi(=UM%T$CGj*=0%}HAASQ~lf&n#A>m|)n@xREm%q=W<8rZMr ztWnIdZsXYHc04J^`o zmBgx0S^6v*D~1ow{vAKleM=CFJ&lAfbRUaI&Y%JZ^SZ%Sf_k>{^*z&n(EAX2hUimR zrmoaR*AF1E$zQZY6ZP_OAxu=nk)*kaVlvvQ0@dk0*cpq^7xNAdYZLu4SAy3W*7JeT58 zzH(*dQFrZqJ`T}nRzhqLYal=_Q^4Yb=dO+5S3GX6P;n z5HQ*%kd|ouOhZf$5a%%qb`=dpHU;Ak?n02=_vmeTWW!`U3+?8PH26My8#0_uR)9JZ zpwIF%h@Y!u$9015bpm@wSi6Fq+xXD+yl(WRWos!t;QFlll@-A;|p@>MDmqv`pvoYE7|no zT$*@YF0G%L54rT3;M?&<>W@y9y5lXV03ADZwfj8iB}#;el~r;jyZoxja6B$vYgmJU zHAb-HN^5)#sfHUO)jL{`%xWFo^7wAzD=Hj!z8P31LOPTzbpNG-u{B8L|3HU7LG+8k z4AVKCfY{PP=h)s&bxrExUGp7vTh^JCcV}5}OgWRw=xg&eOBQ zop@YvS)U;`Fyzzx5#NG7GL(^p>v8lr`s|7%y`6r5apgFYv?zMi=iPZ`yh_UniU}Tg zivTdVoY0kv>uD;3FfS?u0$~Wm5+D%ca#`-C*^L{`fnBt0QHY!S|MQSf^Y4IxT_# zgil;Rjq9|=AnMth^VGVh5%ttPV)h>4(t=8G#7Vze35R9Ffj0TqX`|(|Yv~l0mR(?H z*Hd3h9Ve$=M;~z+Tp+nA646en&@v?fIj8uocgk{>LT7y!(BnEM&LH~M0+r6jK5(yJ zm;6rhzcie};@>&@4(hYq>8m zv~(eL^;7{+dqt-xsr73UpSN?555?W46MQ_IK01dA0s^{vm(JVZmGg-L;wDi@R1$pk z-z8y}F>ldBdzDJC#I6#rw|C#$9mK{~i&a;2^d z$?A=7#a)Q^Zx^?jDmPTP72sF(%aR{oT_F#|bNWUrRk3~1@;;q$CnCrc5wE9nSz?M4 zN@_~@oRzc(>4m+XOD(FF<$}ECD$9fx%$Bjsw=~)hP^{kKIvruJtlsM6UTW%Ep`)R- zTnj6{J9_OcP}V$F1awYjbffDEh5~a|Jq8p9_fOW~6jJ(K$La82J`V*2{wDAYWY% zdIZRCzlqNP_S1D3*ZhE7NLsYt!nQHp9WK3CZccoXG4P)mMEq(T{S=Zx8>-vsOV|vq z`vzVuzx1Ra)A8Ba*>%C*DQjUK?hcNzjv418rf!M6iboG|)B{%*6}BeE%DEvb5vfD?IU=_ow}gkKB14{2PdB8}5wH&&Ick z*$B>mwet2yw}MRFS)O%t;{qO^H}G(J5f9ok_4jkHLYB?JHC-t_#a%*wyGriMN&;}|pglvpYd_Ibiqi3^vHP3`Y0ako71ir7K z0`HF>Yd3j}3Y1hewib?P)}ev~ND5+WWaHJ9t|U7qj|)BeDPH$b>3d258$)eG*+j4k zwZ|K%)kaxr45l`*!@or}k1A=&^yKhH(j%11=Gq>km!2-GZ}7bGQ4K2iU(nt!qaMla zdxhm!OEme+4e|!L#MpxSg*=qXIZ-u0@s3U;zpvb%33}%Cp5K#*b6Tz0H8bxY={j-p zH~h_XY*<7azF>bh45#HFaiQkf7{#0J2{u$LmT3gE_pE%-nDr@EvjSAR_#|RgXIXEO zXx#(2eQ}zr*dYyh^?1fokB**|SbJyDBdh}zv1xabw7{@(QW8KAwHd8O@+lejlL7g$ z0>A4|p$|!|dG8DAOJ+!eNz#%NHq+9y9!1l+EEB#Na%tJhH-K2Sygb%)Lu=Ik`Yn96s@n_a@1)&c zZA2sX1~KoBcvk(C?J_gTt$RE@$;DGW>t?ynJOl84_?$&IkU89}_w7X!&khV~orZjE zl-}B@Xx<_?f6L=p_>&cWk6I$}h`VjiR>Y&?Vc+d;oc~9$NlZAZUl|5{PT*k6U z%!^=M1`>V-+X@aNn%u%Jsx4m0txZoI*T^jBd!`N1#z5ck0o7>Za%Rjh#Vb4J{~M*T zhPi~qZ9sX};2bIM^`si7@&~I0FK(QxTZ(djkIa!leEu8;V)4vt1D1!Q7k?v2-fD0X z;3_UGuV6!Y`|*_Z{C}d|CI_D3dU~zwaVq2%acxaM>KOGUf9Yv*<1SM}@QCdBLU$^L z43sbEEjnZPRy{ho7v)*nLv`e&m1Bt=fpNV6s#C(SjOOdwPJD|U4hYT z+al8Ao>9Euh`IpMo*aoM)w?jIy;}Pq1w{#RsdJk zaF&7K_@t&mJ^(^c!R?-nXj3Po{pgE%Krm+qJzoX;0HYCCT=GJ7m3O$H4cd3*SfPQ^ zDMa18H@cDCLF!7r^Acx|Ont>3nUW`WPoK^J$EiyV%5mV--}Lj@guF;gX01D+$6*hg zf;;7MmvH6AIDNJYa6G8#n4tozIP7s%bwJ)$mgm(<7BMh>h@qj>^#}vVTNKm*O!KzY z_53lAyOjqh4_GqESEuf1JsWnpsRrpnR`oP$m*NB;@-Y7G@( zm-47Tza$~5zp{V}Vb;IxkTW$UqqB_em2>ALxn4Cze|8;g{WSIX(=-ff%zh*9q6*bq zN}~Skb@Vsw zjOL9|pXpU#k7pywJr>HE#kt?IvK9kZcz03DriYF3FE{c(q2pLil07R$eM2`Y605RI zpK*#8TNthM#Y^JLj#X-%3!nVAqYo?U)sH8Z1fo^D?&2mNLY@q*G-vC(+FuP_zHlW< z*=45o_{mm{v2gcNBVg!JVUVj!XA0`M=)fNxgrxWuseu{AJw=?V zC-xTY2r=F0e4uaq^%_kWiLsfLesCs=Zv^9vvU#PIbDoGDf~|0g=)@e~lhAw|{Q&(6}~6!pFC z$oz7PlQ~TV>rbQNY)pST7LSUP*>IcKe%gcLThgb0ML1HlTjOIOJftpO**1+S6o+d* zx&ngud*}?eX^^4ijPkumQkn5_xopbERpfkViXO1%jSA<_8?9(Q4;fz>=9}zYj(MZv zR2-ywCr|;7;u0&E+Qx<(B&`fKO8e1R1-ZJ<4KACG51sGq!2qH&SuQo@!BCX-G+?wc zGGD~3*@*_Kr=U5-0vAD;a zcDOrY7?{pa$IXqF&x_~*3)iL?&&}unUQQWg{ub~2BnVROvP(qIu zwh%myrVGVas~~A1PgkyNec@-JQQaN+YXsf#OAY=GIME$dvB@{U=Nb-CNiEB(s|U>O z#A@C&MELq&At73;DoC#GDxofn2+hhOb;dGLLYtg)WG9}!v25~(9=~1sS4gbzm_jM4koc}c*ji{sJrz!kVR(7^J{~J1irRQ)g!MShZ zt9@f}3&K$OFGwOfN?kd;8x@7d3f5K4my$#9xHjL`rRRd7XzyujR4;4460Q62)UC5C z_#5d&R+<{xc|*&uG`*d^kI5HbU(*z|DNom4?4bew_jJ;m8F8HpRl&0m$fVUN8FeO} z4Fuzlyj>gch|_M}I67~3;XhY(bBRkY7W+HU)MPoMe9agbzoBU#f~)(6s??tXUmujwR}dH&v|{fJX5}>+GM0;d42`KO3EbPGyBqoD`DT#OHi- z35zA(z|VvrO?*B&oq!f_eXA!4C-a&)e*lP8g5hPAI0dSECW?c zpsg8--tJDw6VvJvH+K%N;#8T5B5eAn*1Lm&zg>zgCoZ~;HWDeQ;H}^zAiLa`P-d9y z9+6A=^c)L#f4ugtS>=V$V z3`EzQl>vAd`R(i3IC)9FXP}-9^fb#t-64MBAajkvmM9i+7V)T8qqdDzM{xO{(e2Of z0h^{XD3T&1-^ADAuA^z=Dr`py@eKP`zAn|w;@+tH9PV*Ee4_r`iUU@3up-{1NsDxm z?nk;9cjHrF?Ub{=PK#f|sh)W}>d*O|Rael`P#4QO_wySXcxM$$kc$|Zpt{uP-xd}J z6m5a%sGH>^Ru`9 z3&&=meJ}1HyRQ=zFt7$@=81w8I*7tT8uid3=uKVJmH*PVx&Gq<`1te$nDW1Zkwzla z^*G=aZ=#unxd|b*(p>X5G+ z5tp{;#J`oJ&7vQJEe)9*48~i-o<73kV$&b07hFBkrwKXCSyl!m?+-^V;`I`=Ysa7+ zQ;4tb>Wf^ISj4f+^De@t5w-nH*8IR%AfD4MM~!Ky>ztx4@DK=@kb{u7c9DP3ZuCjY z*2f!)BjcCkRCpQyJjJuj%HOcPwl?*9=of595_1R$Kr%f$6Ci9DC+2=$&F8M_CDHGz za|rGYT)x_JJwE#-glV$@%DhwC51oZC@OVDVWaiu$-OS%gUuGq#kF&q`V1Mobatu|a zq%4y1!vA^GCp~$kcekW?OvGSlL2uw5~ZhF=;n z|0{X=8^y2W@x?i*jz3zdJ39KHXhi@jaPXaX9tE)M(&D*aIneBeNz3u)Q0>z~KV!jz zDi@ByEy7ZL^pt1mr|j;wIUU!YlSV8!9uON+59S|!DQ#|N>f)*N2CeI1u@lDQZuJvp zfXQPC_tOdaF(;JSqP(Ibxe|0KyZx52;FR}TgHpnhaY;XyyF9CI`09tCvjeSF8LI0r zQ`dWe{_2tY#)_juj9+-D_wNEv-RyfGAnj!bB4>BqDX}Z*BFFC|`G_gmcaBLrFY|l_ z@o2O+cS}ufYnTBD}wb;U$J>~Sb8tKm2Z}W(YJ9U*g?HD@ z0;yY6sgjg&nG86IhsPvTYC_^zQ$CqZD1JkqE?;hmKNkjoPrNA>L2h8WnV_KL3}p~XS;iCC|NHyD;P2}wjnvYCw>&uYBgKP+yUUGi{Nbcm z7FJ74QqiZ&_6G--?H?^W=3HKJ%(;COUPLBN{=Tc=8n3ymds`iRe4^axTZwfx?u5$} zyp9U4SwHL2{204JT^qQLEhRKFUYMu7Fz+9&xsILehXM~n<@3sbOBh~${+b4U<2ga$ zxEfC3StB3e*WlAX*i7Tko;J|{JiQ$~omdwt@IF-bu$S;R@nqyY^&AyA?ylDrS-2#- z_@X`;terRYFblgf1I6=Ltf*x6I!=TR4_WA*&+zlWPd}V$$40}lTZreV7&sHtgWU}n z*N(e4u1#jOO|QbbN8?JdoRafhFgnDxxT=f&o5L3;9a{9N?o<)_ZLG~!o% z&lh4ZCM$nB*6vk}Rh}NaMW)?8-c4Q~t9po~7hG(>E@tL=5+~AA{#QfSncwtejkx0h z?b-TR%rv>t6|40wxlZ9R$uBF2OD!eC*b1-XH95pFRJdb*j;$@KrG{&6)KXadiTZoQ z$iuO($6b?yNxb-3lwP_A<>fndjlhl^h0^ySu-`yM1ooG2d)wgRTstae-}do4ah)ih z7|$gJ+rpk;9Z!M>vG6mOO)j;im|Sr!!SRcyTt=}YgMJ-YX_m|C^pQ@p(wm(+Zawhe zTwjbAjx~CD>0o6oH^PXfvJ7`3{q7YVGCuz7QtU$Y@P{6OBj;eCMvG#F0T(M~$)*C& zXwFz*PAuj+?GcIHKN7I>W^FvLvL@&*S&oXkBUSI6%Q>p&_u#fS9+gpGn+~6j`*0I| zEG!;ghEXwvikt_xnsF@kT4oJJQ-v{CYjOSOp%2dEE@4o-pLcFH62Fa# z!e=9m*eEJAn;&OoV_J7TAID;)PwQr}VpQBoaS0`oxqdMr@wiK8&yv1-Su6 zZ0JTm-WE_FM3o+o*ljwI{opEvNBG@3PIOJajKOlky!|jv(}fFv#N!9|aAF4cq&8RW z2@h~LOv~_;1ZLgQ#q&;fYUAB5-ELIOsj?udw>{)O(S?e>JC)ke;jZrZW7q1xP32$7 zB2q5ia>vhhpLmJY9c}&z9~mCHhYz7*3>D8%aohikeDvRiliM*=k8_@6T>ICQoQ(U5 z9}3_J==z-EK@|Fqkl$2J-oJNTqcsRN(y=JBrd1*Qw!``CGiZ7>Q8ta+&c|#sI|6T!~xeN1? zxubXSgzC1tI1z)&XjHgHr2ReOGHTVxg-3VsAyklfwRhEB{0=JGyOVzF8=lvQC7rA; z$FBzTdf-Doya@ozhQ5if4=ipE<{AFIaV&-MPu z;qttDd>xW2$8HhhY4^Hte0&n#pMB=NOY@F!#6LFy!-i4W=}MIxX|dh)|2*mc9e=Ci zx%AO6Tu(;;q0$vHoBZLu@3hsIK*Xf)hLz$^@jnh?J>>8IX7~jEQ$6uJbqKk;F`e{h z84zMC zqO;@J+4`90SU4)GO5ir`SoiGmOQBHIddKuFyB#oApr0^ZcW~T0a14u@Nh(-2e$(To zet2W4!};QlP`tV%`MBLqJ{uUM7Q zSt{dxX*pJtfi)%FD#u2~myadY2r};hFc!8pMPNBqvk$PVDU-U-e5^8hk6pqi3un~M z#06f!GxD20{(vE0oCgQ$nL8AWlbuJZWAX8d{098v@c5ootfX<`1Qr>YdJ=0(zIO$? z@7hI8-W=Z2=~2C=4L^v>9urAi>VRV4B3^Pl zDibHVPls>GZA;0*FQejdQq~ZT`3FX%lSkXn+{Zmrr|U59ne)Vp$>3AOG%9faBc&CX zcR=QCtp42Kp9_!l@0qygdk1ry8ZnJ?J*^pjBSlb^@^okZXiY0g<>U`sqDm@~hOmOp zOK0%Jv!#Fa_i4i8lgVr8WOm?@A2}sn(<7u`lx|{X=pCxP`j~S7Hl8}FReAMhBiZ$^ zC6CI>ypfDuh#UI$lsj2kG?|5+iMaPuII&Q zB4LgzM8auV5D0e?|NEh^TTK(?g96}4RMZBXZ^R?|JYHfqhTV4l&qutzDB^v2ZQvR~ zB%Jpf#C9FS{{jC8jQxkJ{8K)`RQ}1IE8oKR|1|vlANX4xOwt9Xa6Rr%zd=d;ciQbF z%IlY$#_nLh%f&8}e>2R%&SHN$O&}ljTb+vH-dTzlbJEX9ka9g1GpHpjcmB7!p06+A z@m-HxdFgladX^_YxY@k&e%r}V{ykLO&o8QZ`w=SA(2Lxp$_pm9QPF;5CV^XU)OUnQ zN~(@oaUK-`C$mSEUcHl)xV#n-7vG6*UKvzX7eI8yTn@%6DkHP-K-Z`&veKs{kf=gM z+=F+05(BY@i>Vs)y_V@Cc%bL7D}=_Vyj?OLgyZ$7T)}9oEqxq+G8lK8eGo@9_ZQdV zFK-_0Bq z@CByc+%(~Xsa02oJ1@Hng?b+pSFh*JVsWTAhl-o$W_}r;c`OaRU!)#Z`Hb{w@|q+s zMvRIsjZ8hDcI{lCx$M9HidO8Vi(&{pTJS!zTwDErY4I6@^)dLe2zaEvdMRzAQ~2D& zjl(-tI$|uCi7&@9QH((W8DhJf$&>8aWcR{MMyG-HY&f za;b>l3!mm|7Y4Z}&O*@>QN+)1E#h-H$=x1Qbx&^_(GYa(5#=A4Yc|_&o^;8vw{1dxrWDCj%sNrqLsW4r$P4-5YOyXSg!2u{cPka`?; z?NW=cK7sw-kI2WiA=NWnd^E@4KdeN7SZ!Www)tdpN`M(kto`ope1N5dji1Nn6{0FL z0=s@W*M~Ff6@P{^gNkRU@avoyqvkZJsi4#Ra`6o;H_4NVK6%=@vi*$Nlj*CWSY_pr z8T?Xp(O+W1TG}w2C_YyB{nP8u4-cI?ZH?Dzo`^oDcM3jUZl|qX;Y02+_>T_b>L$Rg zBPzo(js3n0FOm<-2aESOzM$lP|Fy}$z7EG*{ll|}Vvm+=EbHza?$r2Y%`AQicVzg% zjQVt9agBy&#{;v|@M(C~Wt`(O$tfFc&>zj|9>lB8P5Tl-FS=rgu8^b`7^mQNR2+u; zgiK}koekF+Em>C3J!{(CH7bs9ydz|5-R?I_^w4QyK$>&?H@8JkS=mo6Ola!_Xf3ep8)Zf=pM0j4sDAn@VJDj{5mspHlZz~w$44v!k=afx%_H&Yx zh@9fk9!}=fMB^*)Y3v`!#>Rb56<`4_UT1Oq>Y2pLIG$1J{a#VpS-WFFU3+4KFD+~J zXzRt#<;6r1%_kBEIMYF)Nt92iR|I+f!3+FZ%grw8=A{Q)T%QFl$2+2$-lDO4v#Grp z-gLhCIo6rp{er3+zxINfxt#qk{w~QEONATH2d?x2=x8F5TKLQ{%XlI_k`*?J_#*s6Aslua15WkdUww!oEWS zBxE50k`M?9OGwxg62iVD0g@1Q2w^Ar?xDKuuIlPh`?Vd%=~MOm!o9g$?pg0S&-q_Q zL~e@URNqdu9QhOaMJR`FxS~toU!5zKP9PLNlmEm%E=PkHJ-Ap!s1O&jc3j+^3D@Gn z_kb>iM{#l7j|*ly#uQ$hr%UZdy7VrhOYu=$oNpW(XC;sBA{LoUSkWCyfe^iqN4C0QMZSewurj@%a`^%3D-Bs zMeMIf1f(T3f0b=wMI4PBt})u$S~IAv$;@!_K-K@jrIv4;FNx15$QV>NbeT-ZE?c~& z+cyUZN!EJY`3|XMF-#X?iYN~*B@S_U5yUttaE9nw<)wndO*cVqE2T0j9Qxn46~b)| z@E}m(^PtXZiw!o}%Ura`QRva-+6D7E;$uvBs=n-zxA=+r@%lrmN3_d9jNL9_1FGS- z7BljBc|IcG4{(rr&5DAVOP@yewdPh>>63g)*X(AZR*dd6mJ97{GF^!BSdBkCOytrJ zpYtj?Lfd7tN{9yNSg#)@%owEC9k3By{cR&CtHAvJsfNu`1pJl?qM$`&CCinyEoi!S zkC^i1m!YoNxJsljb$u!$RJ%*a#P#hIr*KaJB`n1Pkv(qdLCI2QF%&=2eq4NS&PdmP zmia>t0R6}IlG!-~vbM3eNwGj`AwfQ;Pon0XgWnD+2GhyiRn97uR>l3bK(a4@?Kz1T z?P9QcTdN7?aQ!$Dhrz*%mW7sUER}PfN^LM!5~E%HpR%k~6cKe@lV$?xEeGd`u?lSw zF^jRv_I&@)nYxVBR@nzMy38SDS(dVilDg9ORo0ijZ-XC_GN^Jxrh>85OaJ}~wip;C2{!w|OhC>9^z8)kWHosd>--{bXugF!eOKa*ig=_O+;dJ(m zB~Mx!lnuWAJ)En*{mNE`@Q-xHmu1HPw{S-ZaB^t8{+VwJN*scW1;!j_kFZuiI@o-=1AI2sX8GfgHvt5O<5n{yxVD zMM{{m%K_ggHJV=158*z6(MVtEEdD^*sS_@6OHZ}X2jllIhR{jdCT`DqDV{dVQqM=# zblatLsQIHigGmy`Nz-Kjw_FA>hA>`ies;EYJ^I=nc20_!#>2v#Kd^+^z_6vD9rYC^xberz60du>HV_kT zPm73+v=Wgxqvum!yb@gqE^xqCz85izJb0$U{};p0Ukp;0TjQMXc+kK1;A1lMF~{rE z!FH+%C| zRFF7EGF2TTB(XnlfZ@F<;Mz=a)XFxq8iW?@CJC0^3?-2IU0)rzvsKR$)_LU*`{?bu zX_KbsDj~Jy*Z}I>0GG(#RlUpF$GJFDk-H1oZ0l7~9u2lO9ZxPt>m za9@WR1IW|$xDP~$5kF}66E+wBpZB%ZFD&-Y{A-jHcc=()*UAX)WU;BHzEy|2T@1Jb zrXF|2Y~W6rW86KX#QijPK4aMMGX^?yig;a~GNi?mmyna!c?c;Q);+l^M-{*y8=pg^ zGUW?BeaaV3igshE*#@)6j&obMJIh&rfFzS^3YZnizrSPf-cRuAnU5df{;kybZ43GO zwlYz*r|!YACHLcvV*N8PpS*C^lxL2z3DxgK#d_8=I&E(+Oz#h-Aaw@kLes`pc&dkf zI)b6LvI3MgQ~lXcd8Z3O*QmdUqN8rrl~zebOqvJYzla-g%XA|SthvMtwy~58hCMBVF81Pi$L&Ulx1 zh@H^z22p9yJptxEjuyNfWnPkGJ5=qUkTs`5#-#X*X`QOaGn!kk`7HGh?PV;~(3Sgg zk9L{Jkm?qptI6{8rn$a^*wIY53B(=I6Uq+Ho1u2VQBU%^OGQLTyC@{K+LtW!MK1NR z5a?>PVra$48!XL+Nxtx}7XySZKxCR0ghbqNbpqjcNfr=$X-`8Q>oeq#e)7eZ!;_TqC&P?V!7fT!_M2gZt6>sw z;5_~}5q(1adNGsmP8J8rEy=u)5K8joP=nFiGF^m<@fA#u&=(NDxqe^rkaiBXmOCAA z<8`XE-UzqCHRgrz^1-OsGY?*=A|o$7^X4w{^Cx`+7nxu{0&*J}(I)B#7kYdPefgt! zeB#-dK2ZsqaXF)U;>la%6KdkMds5u(-`!W=?0xv-bdkOhaJb$dJ+7?leXFV_CG8Pq zVoF1Ja=^=v9JPm1A6ErZvK|#5u$G9_G*@0&OO{lBHaTCi8dFnktBrIktW8n8!HKL> zs?LJZC{G3VC^En9C^3o`>~ImyO?1`MDKGB3Ac#v!I?rR9Bp0+rD+3pJg(KOQGgI8z zmw7486K4~ZG>#f+bmW?>G3^+tfokztO|Pf|m5!FsHFK!lQKg>4XrCi4l4+nRzGzQ-WXqu!W(3)3NO^AiA3jw8SXKnv&C10rZ9pbaXFaF1Lj!SRCW5fEYrbr2Xg%twRa`$ zQhg|6;e16=p#Exybv|^_V(Ts?OhuB?D^@POPLMg&i^j!8v&7DPm*t#%V59v!H@~ec zW;r46;0#;SU2=XmpkZE6`)G8VY|ZCQ7}aY>_KPWL^ZfhDCrUf+EyKvd5Nn(K7m_ig z`u3r-qJp}mYmLYA?N1zpc-{A`mnkam-PTMeQytot9dh0*@BnvZdHFs0I+fz8D5Yuf zvCZ~@Lvpt;N%O&qa?!b_;_edYk}Q2+?P^*<)~fCWRJb{F|9ooK+v2=aeAK&H;)5Ox z$szms#GTw5}S0tgyR0bGN`~n zuDIPI$lo0Kw4;CLjJ;j>wVvhiZ0KBA(4sc3TSN--n!mw1Zu@tU-7JB0{RrI_N8^Ip zEijhR7*MA@$L{LpqFfB0cbgJ6dY?rB@%uIU29PTopp~}ZTEpj4*Bd@xbGTauDmQx* z!cRp^@wgOUQ?Uw$YVIPmZ*+`Ofn1&nZD|ePrv&y@D^Y_%>{q!vN!!rw^PLU4Z<E{4n|< zy-#05>XO>3$W>WE&@C= z^vS#!HjJ);nLLy)p3b780M_s^AXw%R!!kbf5Z>ag$C&iKyhQ)c(Tcdw0*Hh}h0cgP zB0n)ynNh)TC(gZh^(ZR{W4?@^Uq}~e4$(^e2mZ-6A78})7|GFAF*ahkWgpm8d|(tW z3}YZWrZtt{K|ubvV-BUc(l?0l>Dor*)-?$jP7K4^hJc|EuLu}q@nM62QH0TypcE04 zD+#rPYgb&sh)WgiqV5CwDxGQS1!8KfJAkNBBR35V?$axYE#*cd;m446jb25{ZbuKX zywejvriS7))DZ0X@tf*tjLZt>(7}4Bh*;9JSqWiJ@fL%=ZAVtc5TFny9Vi%89C%Sk z(4L5zEM?E3wVm95680@bon1cceAesG#-Oa>o_m&>Q)9*DwNbOl+`mn5$P=i|B;O>p zaFmq+lm7B@LTlQy6VbKKeg$qG6&bW*qyy_hJutG{R=b zpc?EKACEocyE_+R8nzam>{iEJOTj+$a_mhHns^h+n^%1)Nzy{yP!q3o)VRax=)eB{ zPA8AMu0HAU{j=)&{huDLbyDFQ3sJFQH(g?|N6ZG)qKm~;EFMbzKQYbi-bBV_y}#wj z2geq2smGMW$Nr7DYk2)Ga*w<&B94M=?FSo~tSQi7RZ)h@~WM35+%bCJ0}?dHMB3wU6_2 z*(T@bM(;V;J}CY2|n7?^xRQ1J?k6QzsCyaWrAM|A-p|^%b>6Itr zBqv{=ti9fbP8?;t>w=;u>-72{Y8F%mQNyq#jwnq})uIv%QEOhoh5mpocyV8uRsKTw;z^LMV%cW86ctdLZ) zvJP3ZS)V2hmo_fl_?tgsI=Plagb${i#i`-p&z}@&yYRG|d_r^6_vgVFS86Yo#!{JX zNn?HRwLR2EB~a9LCj>3tgb%`)BzyRM*27={fr^rbhQLrMoP@5)hB(xrPkCI@E)3JB zcAcr^BsGB)V}-B3p6lI7Kr`0R>Fr9zdC(5rYf?P5(#AAVfg$rQX_zT_zO`Y31@^l6 zkC)cJb$+#3576R}4WL7V&kaG0b$RmNH(S-IAdsk-FnP3`fl9~s-ch)(`+BH{_p7wd zp>fFCu#Ll_Gd}W?mBvxBIRf<*&QU;SO$}X2SlE8LML%8vS;Hj~7~Iczr(QL8rMfTo z?D&R#oaovPzwv?B7%Ojj(={m>KCHTY5)~(7qfz_tw1-S1I&Q|r`(Bwbzx-Ox^gF}# z0kq+2A17wUW=xPA_TAmn(wO=4(GpaCrZgBhP0UI75AP1Sc*wjfM}a;`beymz_L1BB zgXdgIA$l&4U zckD`U>RWBq9r6=DnZxbXcXr_M$~v5{OK}cQ#d$peV^dMMN!(FzxUoM*#&p>eZtQ7s zcQ?=*Uw&Om_G_VY;g+hau|y=DRLFGnJ=_FoMlr^udKTkydu{g<55{@XV;1sb(m$xR z@oK|gZ-hThxW0F)&eHf8SG1aiT=SZB@HlA%MylvEJ5Vmpgt>z9T<9?*HWU3RIU}e> znrnLv{%ph8pDm%ia)AMI?96uNwK=oBegjP#*xUK`k)n89QrteA#B3XXi^Ny*kvGPV z{!GS?4)FL9!2h`MBWrf214N^|7tY%_?jHm5Xw3t8$pf2IeobaO5v85lZiKnMj0D1g zk*iAhU>$dN6-eL&;^Z*4s!agf)>HyC45S{R&*QYBvi{^k z6yXUhJhxue@{pC+PIUCD))0HRX9RIDTJfSiWxgE|?vP1H1#R^)+tS>83hB&)HPonK z$&H3FmN1r&eQva{emqL%446OXs^`^Guof_)q#R}%N--(BAr)hOU_S&|x$H{^u23Fw zgd8&v(}!uq2!+Z%a1ct&M=sa;XM2L{pPfyb`}PEF__La0tA{EK;i&7|VM-bS!TJNR zRIQ2bZp;y*PV00Da_ls}4;czQWvCsaiq~HOQ*lMLXsS&QJ!vTqz+qWy1#Hx3CR4{N zoOFb0T}^CMgRz@9w1j#FS{p%2L)PAkQ5R##AiF7x1@p!!vSr^OO(X2s7ppAl3267&^3?k@|tVJ+}+t@1P z<(W=1>TClBB*TlB&L6Ji#WI$b$A8e*-ZcaL7`pgP4kUD{+$hSOyHtdV65_W|preXU z>|vbzWg1EEM{8fS_~`GQc%yQ^&49`s3Ux|}?p8FTAR*Tj{39=Luw*e<)!BA@R=#)c^-xtj&^ zvhiY6y08?FY9yPooAb+)R_coHOK;hg-y2wJ`f^z_*8p{81Je8Ac^1|EMUA1-`!d^x zm+x~pV)t|#|JfkOIU)ONhLbB9M^2qQ=_LjS+=6+oHj*0 zIv!d*A`W!6Ug(}z=R(%LD!R8e=@K%r1I<9JtGsovf?=pOJvB>PSW2O>5}NXR_K`T4 zFb>BUNkuyfKvV6_(R3ZX@)4v<459P{y8@ChNFZL>^>3M#-zd%MV#3m_WO7yH}z$#R~*O+IH zT1Z|vijDL4ShEufNyLMzQtO6v-KFu^!*>k5dp_^oT%uy*};)ifTOD#N8BEc zLaG}_X9JE{H;&>-I<6~mq>C{wd2v#Z`bs{6XZCdBnLQgg-JFc)dGluD{rl;^-{@TEaX3r?Y1IIYH&_AsK)ML2L8X5rNA#_4*NPT+Ysr7zP-UV?Li z7(+*A4k?{UNJMaOp2@(_)7c1i=?n$abk+h6&Sj-g6~`Qhayy-43tIB6fJRbNKa_55 z{6%)#DpYVQ|3g-vmJ4~qbQQ$G$QaqGgQ9@-(Ryp84Q_n<^-p%!${>8f@txSn2jB8Y z{@n%KKDvIpep?T^sfzMS{kD`>(QgZ*{`cv(T^EKoBu*U?&5HG1qE5JWe92Xp z8+UnUyrLtjer2mBinF%@30vKllvwBN5G$SyFm<*oBpa$;I%g zX&O62_Qi1Btj!Z%mDd)yIM43bz1TG|xEOBK4K0Qb^sAOGo+!@%SwN=0m6aE?*AVP^huBgV?C_TtEUt(*oDr_V z+T3(95$3{FN~?{CU)CIMT&ypFf{^R%zE$u5%O-fBRYO0AU&%fk{=1Lp^qu&L3~BW`-NE^HT17TL-DE-=`a|{s?ets zYtqm11xE+SC2KoCg<;OsCN6U6nJOq7-pEJv0uQHl8Hn?rx9TRwG6{!YvWF%yv|G)4XnL`UMO5w_M+LFa zU@ev$9K+&+C0K^AKb2ofu1{^oOs()@$N{WWID<6{d04&hMhbel?Al8rZ1AQ#yd-az za67U8?qcqb9j>)ThW94^=%laq?{2EDM}JgIeBtQ{q=!$8o#(wwe&3s#igZVb)%A6{^cz(DoY;3)X4I+_s)#on)O4OxgTN+iv!@V&|E6X&v-U}eoKQA3M)ehg;IP< zRHe>+A34)~biK2w`AbByqe%nFa%xhldhp<=`df#z?XhPwkhCq{hBC)0v!HO%(GIcpP*%%`>7p$Xbqc6h)sxp}u*?QF+WLmYq)xS3>gyR$s3vw##zj6=c{@CN;UTDoW z%1{%AmtGP{WD0}?UUn5kv5fLGNFQYVhD%#2ZRs1UB@kCVTSCF^a4y^baT4jTbE+~PPl6ONCX+f%HJtdGs~6~p^~HKT|#D> zTc#Q?bh3^zMk~guzbS+8s*x1REJvI~*7uKl4cV?hJ* zVI-8-WI|T)QV!8EcB-UM%10!SCREhLLe*B?@(h_KN)9Ci>Z$@#eit{$N}bJ;S<{ZARpx^M&Ii0-ngu&EX+bF6`ir=T*q&Q zcj;;yZHya|;q-Xv&~I+o?yep3KiY@<+HhDnfAmMOr|9R5H+Pc}LGpASb>)nns6yY2 zC;Ex6PD4a4ex>}oVv;0|M8;*EP4$u*mJsgl!~JJBSbDvaR41l6BNyyWU^vCz3;nbW ztQMYsd(_`9Qq|N){W>*-_Yo;2?FH{%FQ>6Pk(CnGb71Aa|EvM8Ec8xz8Ooa0G~|-E z6;i0XOz^Aiq2-ILJhvYWx_Y+|R-`+2Y=1YUqph0SXL|({O7LdGIQ=Z;r=O{E=x3|4 z_38q&7;I@K4p*I4DrG8UA)!_)StYv~hQ5%Md~HH1xS{KZxr3H`#>)zd=G`p=7CZ%CbQychO!Tb09dSZOd>Hb%_z+4|6_Bt#eel+brGd+k!|fud)>QRZ-xU+a zs2m>p*<8Ww&ze=OpEMS8Vu$GU@f&rYRM5-y#^|uqq`h~vliI}K8KYObqR-uXPkNl{ zi-BD>gRSfR+HO}gry&1to7$TnSu8a;839XWFA1_)hXB^=l5U9PNjKIN)eo=G7Da(X zZ2p9THdjK%S2}5H={b~8T8t)k*+1v8xMyeSwQ;lSDU5WCI7{+AHNnpKTOHm8oqvD? zK3<=S5z`~P*Dt$w(!wuCgBY#~{acr4yCHnF0^q+pdEXhXWf9?mil5w>t98T@`ep** zB){rI5i|J3HgA9BTc`f&_dm_;dH(twjHlEek^QxmsW4EiWkO;NYnEKg)BIvN%YM3t z-`Pl7Mw1)Knpwf;m?CBqC@XwLlrbxn{Y7~RwH z|JXR`>7uH})CEwM!TwIWRZ>k7hU()=hN)*jzzFdz(1Dl09uk6uwghGGFHT%lCD>K9 z!d8)w6)f0nNL&_o6R5O%v;{`Jf$s`i75^|7uQfev%9>t2wZUIj5+MwPkaFfB0NKn# z(wT=g*N&G@wRv5sS8x0Qx91M^HX9kv_x}1NB}obKbQ)o}>B0Q|nC*2JUAh`q)wf;` z_0>C6+y3B0@M_`uI$FT8YvgLSicPL}Zd5%mJlww*OKf=jyOD=+jdEgjfVmRZbsTJe zQfLi_Mo&ZOlj_Pk?vu+H;!XSMFd1`NisJH?PTR52kj>rq>txZGk;*3QHDfVlx$z~a z`M|gc+I{&0i7k5EG162)6z>Lk7K)z_=MxW>F1KD@^S>>7XO(TLNZz7o}sX32IG9UGC2 zUlPtlF?kC(W$EUAo3fhzES#80@>QIvU(pSIm({oW#s7rY`#&?>oP>1iF1|`@?e`7W zr_RQ)I^r4e-obcET7R$x#)j!RSZZc7Z)R3UK==b!l`PtHG#p*CGI+JMv33z9o9h41 zkZnLjB*vNi6bpmdd9Y3;`3w1sa#tCju9Ho~+*DB#C9%<%w3`UE^@Z(BaV0~w!EYrB zwz854mv56rwjC9P>1nf@TmFSq*~`Y!^pf66xiLN@mF4`fjILU=z61?8S2_k5qNmpL z?0ppJWsRmm26PhW3&jN~BSsHKlj1Oiq3a)kqF#E7 z;+2wS$g34leHn>go)TNX5?Qxod_Y-{8ibhyYbmNJ%1uHLsBOtXi0$6`E@zqZft-BP z5W}c;ZZR}1#%nc+?KX01an?(U^0TBco>0_CL5IC%nX#B7i2{Gg=4G|ec&bfVbtO7eJQ-7b&f}{bPunMwO?GOt!iQJT6&uOS^{k)Ey+Ja zm8O|oF!z;Tw9qEj9F$Ym#{Eig*>e_AR!!aWz-!kYkScZeGO5An-)R{k(>zV1Wb^v3 zt3!rk5_AXMQ6SCJ8i{~g_OG&%1saJc~^gtec{uPgzRmqMNF+P(xOI3T4pK>v{D0*7k-|sF_B* z`8ZnSaGdHqP8%l;7%dnK#)_%)&DDwJ`Gj6i*FU?w3I+MeZ?W3*6=a}6IZMKTvtgF> zt>!Ao4u8ozN3}(f?UJD*kYl9f^d6zSg!(b4*U$*1*QXLmxl45*9_*fm{qp!MVru7s zn$&zY)wa2xPpSQz1ypI*AeZ8|X+LwOtzG+>gtD}&NZ=kgyTv{1gxvi$83=Q=7xH|q z=Lvp_rV1Pse=$Xl2W70qHA5snN8XJb3kfzPO3G7{2&o2#2S~Nr@gCP!=UZ9w!SfF7 z912q{sH2p$)ze07XHGXj43D*@hx)mecb3*ExPana{w7!`?tL5L8^vi*pDoISX089x zZU5LQ0c$miPn)A_>{Pd9A2sacv_S4gN;xowC;5+h>MHioX6?!@Dywc}5xWceRiu;_ z%b_bxluLjSb9FMV3^eJIMv?%MqD_MTrqZ0v%V<|*8HClqzg&p<@B!gRHL%acrK%K`#agVBUxOq=*h zRl@in+PB!o(3;#FJJfNI1=jKEDi{rzwS?_`$4TA-Vp`JHOO|^Jr%^}z!V$~?c-HDq zE1Nfspe+~JVae%i0O2Z)!yWOthQ(A9WN}0$$ivv&7n;D9G@DFV8rzeJ6lal>6l%Fe zgg0yFiv#njr!BNCE)K>MFmD-?p*taFX!YfH9*7us))3%bp~PCTIV3l8v9q= zBZD8$cV6l5=HJ_TobsHpYM3#iulicf{O&iAuTc5bZQ6Qu(vPj?SAwp~bDnJ7RX4!c zOv2xD*cIP*wqEmmMu4mx@D~nmN=? zrsiFxtd57JYxVmmq$^{hc&_8~nnvR@x)IGYDNUgk##Ezmp3#03{DItO`)Rwc@SRrq z+}p^`Qpu&YnU#UqlTz&d`WI%JHlM04>HTPxvG)Gjso+B? zqx&LQ&Nvi8YRORKWVe+uW*KLg>PD|0&mMog&N)0rTCGwIS<@_QWz4u#Q;e#_DG6*C zkMGO$a{GW%RA)<}bE*E{T&jK%QkpIAHjf?df-9IMMtx%@fawycfIz9)b=8EUDtx7B zM?eV0_d#m2ECC93;%lKrmn#Ei%0xApfKj|?3y_`zsR$Nx(*9#kGtUE^quPxQ`L-`~ ziYITFQ|i8=^5%%a)Oo-oX2e_>+F_2SBdb!<3v)a#kIEDTcvNbj=B>-UfqR6R%m2y% z)zr^NYme2i$}1LwIGe_ry=xsL`3GErv9uI_h|E2GQHi`!Henx6a)8)}ptdb@u*$X8H%m;o>JQH`ooWur1oJzdvc=I5W&^ zu_ITod@Zbh#$zTZPWjeb7yxyALn17Tw#T5ot>6oTDNI|K?Z7e_rWo3N$B`)i^OX8$ zle@&6MDaIhZKmow(oz^ukf#eez>-^Nyc8MONISJSnYbOz-*AZ|J^swMC`F%~_AF3* z(axvL26+zTq?M!~&DNHVu@{)X)Ty4xqipT%z%??2QzL0-xK+k7*svONVyFxq$!fWL zZc^PwItMt9!jLY9suFHT_8dhUn%ib#I*c0o)wx}N!`pe{nJ8ZI#Z;zb`)$*CyuEvF z{CTvC-w1#D@M`_Kzh}7i?eN(V7To?q82(EygrV-@t8vSJ;re|wTt(zoQe1?_-x~nP zoGjUdg=yOcxG`?r+U{Bl74EqGBvFrTLM8FyS*r z0ilXG2s$F|#`GvMMZFlo%*YPUpv_q>%L2EfJr|x3&T5Gi+N|oSi+iPx0tjY1<{*Rt znTeALuq#|aK(YnppzfNBhenLV{Ww(YQNdcGktj$RPXW}_Cn<+Kf2$njT8!TvtI5(p zPDM1-H)2RjaTxoIVKZ=0zTq7p2Ui-pOKUs;myD;`VDeWbIIf z2d%I3FBDYgk5ElBMkhm!F|X|#VL0;|;{dJYZa?boZxn%Pv-2MCc5mN*pe^VovUD^i z78qs83UbA?EhfO9vDa4-jWw&9ughv z35{ehm6uQC$bE5eIAwH@>7#S!GS=F%$eljLMPDE_hiuTaMW?q(A7WRCbD&XoiJdU} z%ouz4-#zAD144m>ybM%iPANvd3|SBctMvC)Oz1ZsJT6ddLhW>hf`IW6X;$ zEkJa(2-i!Jk!mpk-9QLk`%w7YZTnZ;+8@vC5$JZ}sLP!P@r(zDYN8%1AWsfJ;RC?Q z-v1|D+mj3@(}B>3&Y!%EJ-)cDhPv!x4Mo2=AWU3&;GXNzTjR;^YUj)k!&Xl2Q#S~B z6P`-cdi%~~%vSI4AayxtcU*okpPlS{CdccXMSORGjLcqP>KQk8pNt7_tg=GvDJ=G! zN8}R)#OW_yIWu=6_q>R@K@JfolFv`yMe=#}FK)R0&nsM?iX2L+yz#uwuYnV> zBpMR1z6pA9qT-gtLzs>JGCFmTv_<4$8Tr!FK$z+pVeV` z=wmDtEyR-1VhmJ+g`{;@Q~IW|^dQFX5TRZaR(<{tleKo&7wAzSFZY}Vt7MO0y=)m) z&bDI>?G6k$@4?#I>sV!5kM*`ku<|z2o~Ur=;Vn2=n|nO&Bl``ks1~f*orBf8RanP6 z4J&#}u(o&4-W2rGW}8?3ko07$p4DL;aAF8+f+G>u2VcfY;h9)7+)i(IFmC{oh4sV_ zv9fp#))c`sL68^u-PCLA6f)qZ+Im(18^MZoE-_y}sd^ z?akqB10xU{X2nwzaKoqwhTz%nh2#0jqYzWq!%Spc&gC3D@nBI4hw)e<^|3t){jUhO z?$PUanHIS&Xilwg+od+H(*b?=_x(a<)yCgqTL}JZ#{NH|u))h;Tl0=gq~m z`S6#vwV;hE+@LYkJ!hlbnd3|nCGXdo0p)OQF~r(|e2-f1ol9nHVVs%A^kDcfg3a^E z3`u>akE+9H@~ICfB?br=t1`%p>FNw})NmQBhRY_yWi(Q*&madd3>Z|eXE_CwD=R6m zyj(R7VT+i;$+klnl}_AuSYf%Zb;T}1s^#`dl;n?pFK@IXp2h5M`CAt;)zh= zqMAsJ&j}kzjTGTipYr3YK*qJAH!Jb-Y9fT2@a48)bYXO3^kJZh{>u;!xeeSx%9BE) z^jR6c6AFyBt!fX|iGe!mGYctEM@AvFjPKhL2I<2$cN+t3Vx~_voZahvF1S`=qe2*a z7-&j%Y@_OyT{bFSEbSviblla_=2_t;kuw-g0%;r)mL!Mw(2))z9REf-Ay{#Wn8R@M zivv*6JDH1ehCSa*oJw0jBSo6C2YqpQNX|>CMT#MF7pdE2+ljs5?$3rz;gqS*Tiqob zCUYO6{9{Q{NF6jpuW>h{ryG)nQNLl|i5^dra|fyZxq&u{oh8`=MU!q5$uPDD+*gmB z79I-hOw^FlxH%sg$dT@nqajB*+6ZvpwOm%+f*hrNj6R-04i)vmQix}zotlu8T9CIh zn^Ca4dJbeogj3sULd|vYBPdV2r$GG+%Ev56h9so3U*lOlNvMD`^@Rxk!O#@!VAyqK zYH)Y$bC4uw?`tU&B|p)0sy_^te$JPBTRV1uEiBQXg#!0O4*Oyg%I-LLU@v!m3?VYi zP`dLCZleC3>nPJZQ-gp=!5`7giZKZq7%MymVtYRDjai%52=VK4b)-#uoPs904>C|0 zEY)_O?WWCSK4l6ddvHQ@gpUaw=ax-wb^rQ6hBi zsfwVlWN-}iH|=i{vck6i8ywR3NWb54Kd!a=#SQG?V=OaF8**JA^QIIztp z3o-V+vJrr_c;lxCt|f;BAx}yPI>IQKC>(=hLF+TM9in!kb8!4KE$cI1E6;SP04gxs zN^=#^g%O^j^S*MR;TH;btSreMC5Ep)TkK4SFEuR3mD}NcQ0XE zr=hB#x2a^u{hY2#dmF|hj|xj4*#>*u_&17#o&Z7-R(&yvx+i^IME<1y!Lqb2A3;J^ zb^ig9RdM5>NPBQ?y(K;p<*W(TC?F8gwxP*j=rI69${J(S*xJ}_WRwrZFN^|W$1WbJ-W z3d3gGm_?rX3JWUN5B{W#HW}VQiZM2@!lp(sz>DcQ`O~5uSSQUGmC0=iYL6jGpg%Tq zc-w?_RoV(tya-T^KguwUb%Q@ado+No#LFco+vAmMcxAP9WRfC8n1xwKPHLf^25i>s zZ;_iR%%A92=B5F5)vc3oU(3GvMqQ{rELGnVze+?|Rvlt%7dIQ-!CKZtRC0quNHc{MR23dWS zl}c8P_5Z3|XZ)uTxA=lHP_v9i+7gx!i9A`nWO5#(g;OCVw7M=CQ8$%MvRnELjNMLA zJtK3sGPbWR?zuzC?fp`L!9=$}}-2O8ybw-rb5Uaebv=89pup&e28+ zz+zh88g(ohT&C)n20oR#Y%U`ALgVlEaO0$;7h^TiUJEg-s;yyE<+S`gu#~}X?UzmP zh~m2ANS%!Hv(YKmZ?={u6^N4-tR-?P1;5*xiftf?#}5|@fLO$iWq(`4G&cfj&UG4y zgTC4Xs?pZLBl~>e%WGZ^6b+2jl7i8EKaswpdtVZ5i5t52*}KYB;LGmF15?h9lQF31 z1BYUAnK4jZvp`T23I5We0UERH3aX%IM2&Eb ziC=574coYabmQu>sA+2)x`V6~RHR8vLB0*e=RLGBHZ2+_fV5ZKNgV68*NFn?gq)nL zI0l+u?p>qmM;$Tk=F~34>36pyHpcw8yb+aesiMnPjoZXnS(=aNQ43~aY>Z`wNsNP7 ze_zHVOr}iz`t2KSjbBGpW=`oU^sMcOktMrVin_8A1*q+jtxA~>{$JG(oC_nzm!%SQfE(x_PEwlH%FX48orgtLO1tseQ|%WJ->>&JU_~czHR?AntW+K zirc66_2Hx3Jitf7{l7f`zJR-rmpyRL6?%WJZt?WVu0zZrDY`+t=|p^Q`_}&Gxtr&g z-2MD7UH{8YZ$aTKSqF-xA}?Ww<}fY#=2s(lBz+lYUpZUheXs2%UvGfXE>AP#wap>O z8aXT_54t;^5a0<3N<1}TxLc4;jD)Oq$e-eWTQN2mfKh+3l+0hrrt3{kQXG)=cxrilexhcx*K`F=or#Q&r6Nv8I4+XVQ7ZFaNT`Pd( zv3GY*_;9#GXt*OnUm;s^GeR^CGnX1BduO8!Q_KBfHzQ=@Ot`4F8^uOGa^+-#Eq-Hd zOcucqY8;r+=p?HE>!y&)3|$;A7~Xl=KN z58e1RQ%G7=TSZo4%zGQDtbFY=8^N_tb`ADA2qT8+ZI8yrGTxDGQc~k}{wiv>B5w}4 z=1tiIic3Qki~R(vbLa-onreOTJ*+Uoi(JcY0@VhuQ5_B z^N^Zdnsm=iC&@R_O_gK_uPwjZd79D1=HFg=#9c1^)U7$|Cw`1iZTTg>t26nf=Bvp{ zJ+3KzZd z{O@`1#n5kW3wf9GBRXY8*wYk|lY?T%)fo7z z#x0%jlzXeH?!r3V{;$IYU=(FitM{P1k-MF2*N`z$dwRjtG2?TW2_Zmor4Z zbgQmEu{1K=|ETtG&lM$6I0i5O?#Xcfe|uj7*VuKRn{&>+Lck0I%mTxV?J?fQsn>Cm z*2(0#b?r1IO;Xc1PHH!aYp40uec9Ujy{2hjBqXs3gxJI;5Fi8sf!KuD#Et}rO`wfc z?2AAMu?i5NcMr^N>^QL-yJ;@J^D*}h|9j8f&b{}X@Be+FUbhcca2ud&Z*2ay4%~NT z@InT>Q33xGDsbUs5r&ZO^`!Qx{$#hYVaZ42u39{8QRaM}prz>>7?-2R9o=wcs`!a& zmA(vSG4hK6qxjGb)QrPiq*T@tNoE~VNgB6EGFjp5hURQ@IWovw$`NqMQzkUnZm&I~P7)zeDxfr&JHQj;ddv!CessaXw;X%*tLM3qv@DQ1Zxp zGWZ8{(}`%1cx_BunZDZilm*#Pn<0yu@Nv62X(>EM8j~l&U0iesYC$+wH=t3R(p#434>ta1R>o3HV#@a5msj|5j z3l{Z4SQbC>PZ5)=DZnN#EuqEsG$oVu*`pq;jcy(&%4-?Cw3|G@z37^*>4cHnM%{76 zZqy1_j0@=gIA3k^rOzWBwI!dB@~M zQCnOYZKhF`yrA6EqsTj;hg+g~RZMJ_8X$6O2K%K^7f$Zsrb**9E?b~rq z=$SXxr@6?i?AW>3Q=JeU1VHV6(gnFz5htu{v#XB!j~B(62It(kzRNtccQ|OjA!@OSH0oVf-DD0 ziyN;LQY9lZsTk&^n=cG7lD%`&hH9W4Md%BaP`(r{g~jF^t>02j#D)i$&Tcv%`slAu zxj%Yfe~oS{(^CiG_HE=vwJ8#K`}L$n!?%)wj-}$@D~rkSYRGYu;&Uk6_?02Ds_)Tc zY#n;ss{EL~f>-vQ`@S4~P!mPJbvr&J)D>GVZ8hy%E}fJdlC&fY4+^E@tvU!&Li#H#RZ_?NL$8TIP z$L%t;@_u-s|7Ocfp0IKcaP#5FH@gG0JI0C2qEz$PWddwYTKl1;BG-k#2yQ$bGwAfS zYzy>1_oxj8c_|63hto%#@jN6h`g}DgNCV)X67f2ey|GZ#vF~^6O+T5;CGU!Vz@QM_!5erlDGke9Zx2%$$ug%XE z4U$!(>K?LzpLD-q*x-hxX%S5H7@F-jIAKfk><-lC#)JC8=~ITbHJU1_uL*Q?H)%k0 zUB)_DsiMG;t9>^kQ~Pf7)cDhF;_`>)UGe`2(y9*sGTV~!quGpJ6DV2FTnDSR=I>ov zFecJZ#kI41*eipPXlhY+2sd?>ZRlL?{uf(v-@kOlFF0X9pIJ8y52{k<;5LQ^S&k8s zI*j9EIXcMhT^od>^Wr%u-_}pV+9LJmYN!&`**py+*?pkxv^v^2D*06e5Ri&Fz^%2srvSb$&4d zAbI)V@3##$Z7^g?l2zA2KdraR&5@B>f}YZ_0{wkoe>BJ|;Gw@hGfiL>>j(ZO=wb%w z1wvF0s)%NZ}Qsw~}*tns2;>z+=@YOWp#aBLdk?N36$XIJ@ z3lJGtg#l$MUSU9eZ&6`@!O}W(UqO*B=Tb8_dflr9Lmr-O!~fBYp%jB0!+3N1z~yNS z<5OwLL6#E9;uFI{bqVe6rAs;fNnPLL;^2fe*7W09Ai?)gE$PMJ0?`4 z+x}66U}Oqvk{EF)s;B75NUB8P|JJW%guaQ-4SiaK z(N7+KzFw!Za#!z;jFK9tNc-;kU6DREwOa{~RQW3%+#4Ui&(jT6_e86;!&G#A)#hE% z-h-mx4S3^m>xsyR87pQ<^8q^(XKePgR%rkm~#g}z%Yy{lwkHKX*A8+n`P z;sZe;XTotPV|36K=e4cLpkXw$%;e_MPEf0OT&@)-(6Bc;noIE1S?+&`T z&%+;b%WS-5&~xXONABI{eGN|iOt~ZV^Jt$LIJ6H(md5TS2OaP8b^7S9KV(OzS%@o+ z*_e85b27Ynk&N9Nz4@NJ(+7eshWIp_>V_^gHEs?EVqcEqG5G8B@ejAxK8Mr8Q*feu z52i{+cLQ%8Gs}lOW|4Tuk>U0+yH20DH?-sA4d>#W#(vj5cScWs$mp7U&LqT+*;%)% z-AkkQJE-uxw4e)*w4hZtwV?gps|6hf{!4WBexoTQhDaEkiiZl(YQp&zz1#~fl#bbm z8jtGC4z6#cu}%rv`b-*Nf_#QW19Dc(8o*nh*uOL$U$}Jn9(i1c3t(N`o%D4+(T!Dg z5kwhSWw+IQDgmqRF6OW2f+Hnfk41U&gL7D+w}3xN`>=Yytr#^Y=>1O5Y-K^9UNL-j z0Bb5MvAVLeQ(%O5>+?!nBWJd9lKeliMla(~mIIH}oICVcOrXUI&*@m}nU7VUWmx~& ziIt%D_a$%cQf0Wf>PmDe$-|1#E!DN(vUKc+Ah9y}3k5LTKOPkp> z0URtt%}>6*GH|^Wi&N_z>pl&!;$Nw#;@yq~tY@&6bw5_M-mo2wzuO7ys|Rw@jq1@a`r*KU5P?Y)n^$DKwWzxB^i75~5ze7`BA$J@|Zum{T1 zEew#i%j!kWnz&3!M)PP zAkEs8{%pypB=&5dV1|2sWu&6_+>E5xAuo;z0@*7+#*i?gdPQ*9XaVd6E=fq}n;=-{h|v5+y2g^>2g2&OpBa0 zVH&RW7&6Rf_;DSsT;Ei&F2`5AT-4|{5*^zJt0aznC{L1FLB&DN*_l4^JmA}wm!C^% zs4BZ8jjmq2y4Kv|BAjWFHD4TL_PjgV>uD_8TY!)TA!H1e=>TvIP)IG1c4n;8tTjyo zXm{0}j&coZCs>S>4S~-rV9deud-#9odlklKDmx}R5sjJGS6NjB*xr?okWf(NZUzb{1vRhHYVms3uGyMQN@IF-?d8l@{5=w065gOq*z^ z7SrZ2n8yk$A$Ka)fl`MirXd{U=C&YxQhgo`7%&-yI6kgV#h7f_}B5gka@th5us&F_?)$;Lv zjJEnR2Rg`_F9L8ysHOfCo=PnmiziQ|CD?NbJ_hTdUI5j-o^$x`q1SQdU{-%yB?;0n z!f1BWy9~h`2Qt(1T##Od*Heyx2J-Rh$A>7;zHXmg{jrk0EGB9(NK>1`Q0?qG8~JJG zW4{f<^_8_?YKJj@a)$%QVVILnAvebN3jKRsN zVxzsv+E-3RNfZsh6kTKmwONZ1fYFv(Mo3Dj)h*VQKWwFnM^B#er-J#7Uo7B1vS$tQ zNtPft8!QM+W@InkaT2lC#(bhn*76cRuGk2*978g+7V%|hX=*ANbgLe5#Y>GadMWQK zb;>|hjpC20ixf`^GPpb_!=O}3pCl|1;omThr?COKS+^~Kq?GUf`Inw6T(KvB!U^>^ zDm&7sH@X6m@~ecZXl(>OIC-e3LEX9Ke45G34OI;3p5V6Wp4gZn!^=hl;9#fe6;5^f z%hE$R7_3?T^Ym8Xt1Bs%ck-&mGO#?t7zF~Evz}zum+{E`RQ3I7mN+wj_U+^!7HDU^ z{gz#=FD*>e`@(pA=f$O3u78oJ(AyHl|9aR&X&AW{X&oVF_v3kBWJ4rE^_m?6Iz3%9 zkWP!kgsYAA+&3X)VH#GkZtXEB$rqnP6~ooOLzt}9g=t&R7#{8m^D&u=iD_LOt+T!r z>$BH7l#JkLO#CXv6fjW3)Ih;Z)wlBIbm@1{lG=*vZA9oEJn0MeuCLi&_Xl_Vz`Oqa zNxo)$>Zi{HFZt7#xO?y}LvRkNx@8C+hdhJN(_cJ^|0+Yj*REy1UsH(QyJEkCbY#sh zAA5TrW{O!4h_T;yi8}U+M;73-%UP|LxY!k3ZptbLl1P&f@&}cv$L`_#4(&gE98Tax z+4{G|>yjVAq=`gewIl)q0heo(K6a|`FiFmNMB24u>KqG`=pbyUQ~HE+!p;y+40nAE z!bn3SKuiiCROoD;g(H;#m-IbWQaMz1HU<;*qA;{ja~e$IOT=`(Y0c8vQ(b9dk4;SV zOT(nUR(l)h!!$q_CIt3lYG4($2u@C_3ED;_uvYc?3own4XjohQ;$;~o7s@fsa10X; z3o-TZhiAw}+i(z!#H7S&Oi$d!L`6>&K(=D);sPcy9_;qLZpO4mUAxq)6%!q+G37A^ zlOIbk4N{K@k<-;>#O_+nc^D1g-VtRo6VoS`F_ChJTSFIOYGo#dGWCL|X^{6&hMxCO zAEsgUV`ws$`t4wf<^(2d=CxIy2dg>r=NSrB3j%sf>Xcx5CyOmX{@+t#(&yadBW6qj z6}GRRC6?61-|N8yQ6;8|rs39Eh+zs7NqJJEk58c@$yck)`gSxXo5o{cwdz7ei`~f0 zg9C4i&Lc z?>zN~3>Eb${|8U-|JI)1CpQez5KtmU4M zPTyw2-)9qW8G?6M;(gK&nLkV)I0ZXA=@Ak8(BJn_8@e%IkB94hz8Z8v4<(=!>2D0y zAC1-Hk@|D=%f6Dmd=@QmDrWkbD@{?pS4+@~U7z%xe0);~pC+8kjzc8(P-+Lmz0`X| zLV9)@9G4flfwnT|oH}Omv4-5H%ZyQRG+NExZimn^z6(g~BAKf9kd zWJNRSeO45qHqTzsOBv1zz@|zJUL1=-fuRaR(xd`JW&JEMdWIfG6@xu;8x9YsoUmk; zZfVGlfzy$)DmXXV(TK{1w;K_x#Hf#|=Y!7Gnn*C9WBzhCtNOGcX?T&=J0={Zjb%1F z;R2(@3E3F9dq|AB3Ye5n}}cEykw$YA39V;w69veP=XKBk+)yL%|ydaZ>dU_Tshz z8k?50P|d_ur#z|}O}eBTU@M}>3DbAEq+Bw2ecD=CMyB-VCy>L_j_(V`_6U;2(Gt$3 zhl|!TP+whN5}4nuu%nr5sg4+T=6qpWK8R+UXWza_{V3}0NCrh%aM!()A3@M+awK%^ zNCwEpvbwArS_C8jhy^m-PbI(6W)+J_cEYq7t_r#bp+ofmXx#2WH8YkzI5}U(hT|Btb&YITyRh6sWLoT}nj@$E z4H2qpn4Q|nhHYY36``##eYaaByfPD&^A|i(b`u1fXBZjnipwRN1Ba+$Cx%bU*$UHw zK4(~l7j#QtYa0x&i&J4 zJHMR5mIyW%x9W*r&8qupIktsp{#G0|iC|;+1pI5Jv-q{ZEbq(*tNU;KK6%#|Z2Mi1 zRZ3lm2J>t)Am`I9LrC9Q{}Z^}H@^&IYekJvjsY4Q#b%hdxO|0eALf(p6$-MM16H_N zsNH}qk+YXoRB32Uxg73TX-18SFIqeHTaYw)^ly2~t_3igKJ)(y!zbopOr2XyYcYu$ zP#UXThBlR)f1XlyzZIjgti$!?w$E1Ohe?oRYx;6FRm3-bH^xUu5CQb2&d)Lzr4IPy zVaLwbS~Tr%etcG4oh6!D^8H-0VDkK~6eLtc`0rIsz>+T6BkkIM+D-cb+}Td51zaZu z9kWKy_GRW0%pT7OxLgtI{O+jPLVcXp*;(F7REQVLTzDND-@>^Ek>aWqHR-#3+Ak^n1Gx~L3B0XR0A*-%^N4$%W^ZI zK(N#TdCjzm0lNtdV(>zM1T=FY_0LWWTPrPY6j?NOfR+m>NNU$hp{QQ52$Dv5GT1(` z5D$!X+bWci<}Lz*-S!1SY5)3YmoA*#OO%F_4I7nIP*tV_3~|Za&hxP~j3a#aS73c0{o!708>7lBcueQWk0RQ# z3+9RFaTP7AG+hXKlNVmzZI65nkwu58ubn#Uxo38X;9-6EDR)moF^n>|eH)%Z6^s4b za8A@1ha8CuCYUB*v_UwM)<v=1dl)+#J-7kY6rH({8V+XPt{A_2e`Kj<+qJ0iBhzJ>xfhMXqL zo9mU8-GE!uPhD>57>Swe?mu3n%E7=&4X|KEo=~AEx(C!44XI0Kd|E1>qd4 z<9GTqFC0?u^jzq_clPAoL40_sHFZQj@c z>L{Hx5_>*q2eI5(mI{!jib7dY!7nqpQTKU( zboTxdaMk540$APaWu`w4&i<=n@&+!#E*<<8O1`sOTiS+u}ThVGdw z&NA|98hr7skMkS$eZKKRZ15=MTsE&^(jbgy7{QTSPuH-phbVpngfUb96Rf-3z9utK zHzs(63bC2T{@Mc!CevDqi`@5RMwJmGv$Olm z?iAP{A9oExM$*JIIKX{{PTX}ks2sfzWvNF}s$84L-HQ{rqcN73NM}EhG!KRUYo$nKo8z8T>J!*c-(yh zLl_>Ach?s)VILl({~ar9)hA?GzzUZ2n#7>TQeRw_8BPlS zhgGx%Cc#UQ1Ww_3*mq+lGMpA@oA1ANLx-W_liumyVhYJ4r)9eHi~WrNY&AWbQ{UzMACHq@*=$VGGjGNYWIE8%C40D|zOBYFAKE+{XpYJ`l; zX+3PI<%&_!C=>0?M`a@zVeJ>Ay0(>X9!$7i+NR3+`y`o)A2=jx-+Z0`?rCa0Ed$I{ zR9%)A2cqX^|ES|x8jSM@VyMM5Xn9U6_Ch7Krdv%d>EPpx*E6{wgK*G$WF z&Q2jKC-XJNddq{_zQ`C*I+9QYYpaSLRg?=jaKltH29{e}&*obkD?m44_wQY1SwP%& z-cR!!oWH7eF26CtH-3pR*?*sBv38LSQ(6mcpn4|r+wyE~BqDY?s#SnFBFupd3_96* zDF7XuxB=83#VdgfQ{#()X%q}LZp0&RdhL2rE|Sh2jDk__!729YL<}jRt9=U^x@!@# zjP1-oX1;BU498F?a&TyjhT3%6nl6b)6PJv5D`uVd8+wE~qE>E9gj@Rg4PYs3X#fVs zTm*G>oAIxV+3;^e#a2Nfkz+dSf++v0$G~!K6zIVKE34Kk0rhY-?tn22#yIJd9+MPU zVhiJd59$yeMxb6-3Nm!fXQOy|e6Uxoqjv=sW1xp+kK16TG%SUfOLFQ#>ZWXq24XSt z3Yvpwa(9E_;9*4z%ni?cb6PLbBUW?HHVx(v=)Co+b(%5RLKvu$oH$NY%$MDturd=8 z`rq;13jR}iWUdzZ6Pt)?g$Vz;8>+X$gB&Lr7@t=B_!VdOKVSXE%~Zs=vPgL3x4UU; ze(AU!`d{>@lh&~6!fV)0xqA(#MqSIgKH0SmoTz*>;)ttwx5*O^{>O;D0zB9A2%S}SYIf^knw z=&h*Fd?xri^cuXyyQIpVyDe1)ebpywmQXEGz>!K>#7Jbu^0+ z-LW}`{E-*c8WX+nA28h+`cCIsX30QqhW^Txg4}I%H3`1>XCL|0h3tcbG2W%g{k^jT zwb=n-D-u4;%iHj&+)Wa}ghmRga7vxW7|yYSD>&b7({^xHhcoczaV{Qc!)bZ`a*UN2 z>SM*fYNU$6vF_7?llbfd*W;3}hvU?~jqI8LVG8f16rA)IhSUF`JKCy%dCKH35aS*D zA+6Q5-$GG1Uofha94W54vbQgJ(?yjXoLA_Mswn|enW`V#7Z@X`$X1aAQragHz(Jg0 z6h)@1I1s@ogFONY^b{a~(khFpfDl6!w@w077#c7**b`s0QYG}GAHm=fI$c^6N%;HX zyuE6ohWPeFH(2ZEnIHk#;VX8qK>X=DkK5p@8|Vdmah&rl->6{F0%O-Fp1QIqfmch3 z7xTe~%)89#c_VJZt<#b;~Zzx+g+0+@Q%jN zWa;I3Zw=-AG;(jPhuo^x50aaug;6kTVYAD#pp}Bc2>lW$XxM$aY@uHWnX^k7ut&On z5U>!&u?9q^Mwi00w1##l#ZZ{zXoLIQ<%587S&S|qx08a7_Jz5Cys2ImtnYSZLS94r z#o?;VfX$q~CBTup^dMrt03GforXv>y$cyUi0TFyf7cfZdZ?r|(UfUUo?ge3c>(Ayf zg{M-RO0M*mIKJMzIWR_$waqJ)q^7566|~G&ieRC*^LxBVGt4h08p?^9_^7|7X~JFW zL`;@np(44LoYuv@%Lh*RrcYmetBIJK7*&xfMN=`5<*j}*okeuE?<#>PW8`(y3Ud$; z@w4KuSPhIYu-Z1)2n_A45^y-y?jQ~d_^AX#N`Y)=xr_int{V-U?{@vFs#=X)9Scg@gY0S0NFP17}*l-s8_Oj1m17HGDM&DCL?5m-;rm+0svDx-xWb2#Ln;!&(%EPxbl8PV$QfGBfVnMkLfG5BDTLV*x^P%F=Q=w; zmC0t;*~%8z*^-EZQ-TR?)Fti5v4(giDS z#a7$OSU}uf;xj#y_Qt15je1v7(2B~o`zJusSfh8YDh-idzo82Ca!aipFtb3oIhGB{ z$J%Y`7-J>|DMAbx;7 zD1JayL$ww`i7x%07p#}?07tn|4U#&xt_{t1{i31P{Y^%uh)e8t>0*iOlAYJJtkML) z%x?MPg7|qQ=w1aC87m6dfPofO<^C!sKh`f-yuSyhN}Bdyn#bfyH6KnjvZ_GV(Dn>D z;c9H9=@jFYFsQ;={g2jarT}Dg?z|dN5-ta``z>66+66;@x0S*R5W0B(t8F`-51aBv zPI-rl?z->qXa&Hhgqa>Z?L#-WiHM`|lZVcDK8Xd={24F|S_l^8K>sA75_$5mFxoS| z6%kerEwH9sb(4!&0~Y}P`5!cWOiam;3QvA`Yp3?82 zO<`p>Nfd2xFV4xf*rYOnvrg8gE-`J8j{6%qadOr-$@m{v1}2{sZ;wW+RC2Wv3-CNV{$qkTGZgNiXPX$muxiks#nv|Fmq^F}9lKPhiF zKf!~`i9=?6>e*B*Fb)%0iER+3h9$g~U%)N`Gy2g&P%2xqgD}fT6DaI4sKD67>;tp3 z2s1Ody(AMFMB5rDVlc+wWkt{SCBiLTp`}%3!o7-2XtK>cFh1oDfT>)|IPj>gIa~ly z1uFlM|xDw0@mO%jT5J7poL*rQ&0EP92l(m9o@9Vgr)~US!H!2tScFR zp`@dy159r2^GN$*7TC>=sYdQabsQRxL&nFUBDX6JRk!!Wp~HhJCF-hOc(gRVBM#}Z zO9KX|vMTc`&ZNOv1{y9w6*6lpG2Ua(gB1?L&&B2L6;P62_v68&j9G%&Qt z;b%(@<{oG_>`s#Q_!+O!vbJS%=ZvaEy-c>5>*vVS=-E~jxjU2#(^w62WV~oDs4-`n zNgM8NTqaWrGJXDgaY`q*llT{p*qj+-wiLhLh{PGs?sXIGSVdkv6jyk@S$H^kKC+u+ z_T)GCdjMu!?B997E|#Skti`!F0OqsyIbh6*z(U9P>mtla?ZfQULrp4xkmSml0BW26 zvUh2A&?B`rr;Z$qU#z-+BJ8jtkRVGd22R^LH})U2Vt_+f#*n0Qyg&(UXeTH;1eN(Amn z!TwnDVMz;Qpjy=b;FWY0?dmXik$P1nrOeJmGL(8UCm?#|a1Y_{8I(_W-4M3QL|Ld9 ztNwI?HP{4c^W_FnBfN5dt?E+QV>)DAZ71vgARk9S3=4auq zaW>-FeGKk{g~n^zY)#{}1xI(|HIu`hPJ#qn)q!1oHksm~niz~3QOf7cw= zNWU?2r2jlj&}Ub~q(2?&$Q|8n-uqw%yz-|F;N=%^_bu_>WBlEZaxHz}6tW82Cv9P{ z#8gVIM`Znj_A}}@}Q@!o>VByh{KMR9N5{&!$bXP zC6i2IFkym8oIcf*Qo$tlw$^@ZlGO}>ZbrHm8ZeCF*_k@#PbH^Ud`g+RrB&`LHIUT7IXX0@vE*_Bz!?0GCwuTz3>LO_e;rSxkK86hpBL^$~q2K*4 z;|v9VnXWA_B5%7h;=xhbz{WNi=vqj;J*I%Y_-xtE2->o7NJi>+W=|h$N`9H%{b9rH zIDDbMLPl0w;zMtS?-!Plx2zd)v{4beLk3EeGml>pS$}->!5hwy`3z$WQJA%2M@1NJ zh9BGvzhNZ!e(^rH<1_WuXK#k5^s30a2ZmR5rMqNcT|wPv@2Y$qvm#)Y<6QKX&6n?x zH^VzK8}8Zn3TGP?K339pcerD%!b_gKz2!ZF*Kr)a1?g?B7Si9tE8vlbu(PCh4E(+r zMWOF_#^8@yulG`MKmXg^um;dvwi@}x7De+m9Ui9m48^n`dKnK!)WCoN0MEUe9}gc^ zYk;-dSA!QVPnr+C6j_6ZUSMpH>l4Kf_vvjVAptCgM4!S0o=*#ZW9Lgw>f1#p6y(ud zwh<9+4+CbpZH#crX>|#;ryaA*@Y{(QOVk(7Ks*B^?3qP0VyRG}qTOlPw28XTa8g{k|DU0sf8V#754_@A5WO8A z_fz<|Kjyhl_JKosDD z7{CL3kN}cEG7x~A-{idPfW;AI_ZJb^R%_K@0fVMruLr;;%>%>Dr9R9?jRru|;g$iM z225jYC9p2c%E&$QwuJ^ja>I1=b*WK<1_%DND&Ylrx;cjy7nzzvQ?glLnL66HzCTSf z+DyTXC%g*PRE z^~Ec9H=v86O$IGcr{g|-R|A^vOmM^f*1Wh&E(|xDFvEuoS6cHTXp_2xh`UH-E_REF8XrBb~RyuhWM3HFS%1UuCwD?{@IuqXOxHW$T04xXUA^uc792}yu|wQ lAchnh1&M_vJkTtq;AuAqJ$3VP!4L`F=7U9#{}1}!@#$o0zo!5I literal 0 HcmV?d00001 From d316b220c70947b17daa068d2099050589d4bd64 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 25 Sep 2023 13:16:17 +0200 Subject: [PATCH 48/95] Move more files --- .../opentripplanner/ConstantsForTests.java | 12 ++----- .../datastore/file/ZipFileDataSourceTest.java | 33 +++++++++++------- .../datastore/file}/umlaut-cp437.zip | Bin .../datastore/file}/umlaut-utf8-no-efs.zip | Bin .../datastore/file}/umlaut-utf8.zip | Bin .../oslo-east-filtered.osm.pbf | Bin 6 files changed, 22 insertions(+), 23 deletions(-) rename src/test/resources/{ => org/opentripplanner/datastore/file}/umlaut-cp437.zip (100%) rename src/test/resources/{ => org/opentripplanner/datastore/file}/umlaut-utf8-no-efs.zip (100%) rename src/test/resources/{ => org/opentripplanner/datastore/file}/umlaut-utf8.zip (100%) rename src/test/resources/{osm => org/opentripplanner}/oslo-east-filtered.osm.pbf (100%) diff --git a/src/test/java/org/opentripplanner/ConstantsForTests.java b/src/test/java/org/opentripplanner/ConstantsForTests.java index a9a7f4a3e13..b7bc4ccf7ac 100644 --- a/src/test/java/org/opentripplanner/ConstantsForTests.java +++ b/src/test/java/org/opentripplanner/ConstantsForTests.java @@ -63,7 +63,7 @@ public class ConstantsForTests { private static final String PORTLAND_NED_WITH_NODATA = "src/test/resources/portland/portland-ned-nodata.tif"; - private static final String OSLO_EAST_OSM = "src/test/resources/osm/oslo-east-filtered.osm.pbf"; + private static final File OSLO_EAST_OSM = RES.file("oslo-east-filtered.osm.pbf"); public static final File SIMPLE_GTFS = RES.file("/gtfs/simple/"); @@ -75,12 +75,6 @@ public class ConstantsForTests { private static final String NETEX_EPIP_DIR = "src/test/resources/netex/epip/"; private static final String NETEX_EPIP_DATA_DIR = NETEX_EPIP_DIR + "netex_epip_minimal/"; - /* filenames encoded with cp437 and utf8 */ - public static final String UMLAUT_CP437_ZIP = "src/test/resources/umlaut-cp437.zip"; - public static final String UMLAUT_TXT = "ümläüt.txt"; - public static final String UMLAUT_UTF8_ZIP = "src/test/resources/umlaut-utf8.zip"; - public static final String UMLAUT_UTF8_ZIP_NO_EFS = "src/test/resources/umlaut-utf8-no-efs.zip"; - private static final CompositeDataSource NETEX_MINIMAL_DATA_SOURCE = new ZipFileDataSource( new File(NETEX_NORDIC_DIR, NETEX_NORDIC_FILENAME), FileType.NETEX @@ -219,9 +213,7 @@ public static TestOtpModel buildNewMinimalNetexGraph() { var transitModel = new TransitModel(stopModel, deduplicator); // Add street data from OSM { - File osmFile = new File(OSLO_EAST_OSM); - - OsmProvider osmProvider = new OsmProvider(osmFile, false); + OsmProvider osmProvider = new OsmProvider(OSLO_EAST_OSM, false); OsmModule osmModule = OsmModule.of(osmProvider, graph).build(); osmModule.buildGraph(); } diff --git a/src/test/java/org/opentripplanner/datastore/file/ZipFileDataSourceTest.java b/src/test/java/org/opentripplanner/datastore/file/ZipFileDataSourceTest.java index 1ad90ed7fc4..3f55f0188b7 100644 --- a/src/test/java/org/opentripplanner/datastore/file/ZipFileDataSourceTest.java +++ b/src/test/java/org/opentripplanner/datastore/file/ZipFileDataSourceTest.java @@ -22,12 +22,19 @@ import org.opentripplanner.ConstantsForTests; import org.opentripplanner.datastore.api.CompositeDataSource; import org.opentripplanner.datastore.api.DataSource; +import org.opentripplanner.test.support.ResourceLoader; public class ZipFileDataSourceTest { // Sometime close to 2000-01-01 private static final long TIME = 30 * 365 * 24 * 60 * 60 * 1000L; private static final String FILENAME = ConstantsForTests.CALTRAIN_GTFS.getAbsolutePath(); + private static final ResourceLoader RES = ResourceLoader.of(ZipFileDataSourceTest.class); + /* filenames encoded with cp437 and utf8 */ + public static final File UMLAUT_CP437_ZIP = RES.file("umlaut-cp437.zip"); + public static final String UMLAUT_TXT = "ümläüt.txt"; + public static final File UMLAUT_UTF8_ZIP = RES.file("umlaut-utf8.zip"); + public static final File UMLAUT_UTF8_ZIP_NO_EFS = RES.file("umlaut-utf8-no-efs.zip"); @Test public void testAccessorsForNoneExistingFile() throws IOException { @@ -108,7 +115,7 @@ public void testEntryProperties() { @Test public void testUnsupportedDelete() { // Given: - File target = new File(FILENAME); + var target = new File(FILENAME); CompositeDataSource subject = new ZipFileDataSource(target, GTFS); // When: delete entry is not implemented @@ -118,25 +125,25 @@ public void testUnsupportedDelete() { @Test public void testEntryEncoding() { // has worked before #4835, for verification remove the attempt to set to code page to cp437 - File target = new File(ConstantsForTests.UMLAUT_UTF8_ZIP); + File target = UMLAUT_UTF8_ZIP; CompositeDataSource subject = new ZipFileDataSource(target, GTFS); DataSource entry = subject.content().iterator().next(); - assertEquals(ConstantsForTests.UMLAUT_TXT, entry.name()); + assertEquals(UMLAUT_TXT, entry.name()); // has worked before #4835, for verification remove the attempt to set to code page to cp437 - target = new File(ConstantsForTests.UMLAUT_UTF8_ZIP_NO_EFS); + target = UMLAUT_UTF8_ZIP_NO_EFS; subject = new ZipFileDataSource(target, GTFS); entry = subject.content().iterator().next(); - assertEquals(ConstantsForTests.UMLAUT_TXT, entry.name()); + assertEquals(UMLAUT_TXT, entry.name()); // only works after #4835, will fail with "Invalid CEN header (bad entry name)" when verifying - target = new File(ConstantsForTests.UMLAUT_CP437_ZIP); + target = UMLAUT_CP437_ZIP; subject = new ZipFileDataSource(target, GTFS); entry = subject.content().iterator().next(); - assertEquals(ConstantsForTests.UMLAUT_TXT, entry.name()); + assertEquals(UMLAUT_TXT, entry.name()); } /* @@ -147,14 +154,14 @@ public void testEntryEncoding() { public static void main(String[] args) throws FileNotFoundException, IOException { /* cp437 encoded file names in zip */ final ZipArchiveOutputStream zos = new ZipArchiveOutputStream( - new FileOutputStream(ConstantsForTests.UMLAUT_CP437_ZIP) + new FileOutputStream(UMLAUT_CP437_ZIP) ); /* set original ZIP character encoding aka OEM-US or DOS-US */ zos.setEncoding("Cp437"); final byte[] data = {}; - ZipArchiveEntry entry = new ZipArchiveEntry(ConstantsForTests.UMLAUT_TXT); + ZipArchiveEntry entry = new ZipArchiveEntry(UMLAUT_TXT); entry.setSize(data.length); entry.setTime(FileTime.fromMillis(0)); zos.putArchiveEntry(entry); @@ -165,12 +172,12 @@ public static void main(String[] args) throws FileNotFoundException, IOException /* utf-8 encoded file names in zip */ final ZipArchiveOutputStream zos2 = new ZipArchiveOutputStream( - new FileOutputStream(ConstantsForTests.UMLAUT_UTF8_ZIP) + new FileOutputStream(UMLAUT_UTF8_ZIP) ); /* explicitely set Apache Commons default for documentation */ zos2.setEncoding("utf-8"); - ZipArchiveEntry entry2 = new ZipArchiveEntry(ConstantsForTests.UMLAUT_TXT); + ZipArchiveEntry entry2 = new ZipArchiveEntry(UMLAUT_TXT); entry2.setSize(data.length); entry2.setTime(FileTime.fromMillis(0)); zos2.putArchiveEntry(entry2); @@ -185,14 +192,14 @@ public static void main(String[] args) throws FileNotFoundException, IOException * http://web.archive.org/web/20150718122844/https://blogs.oracle.com/xuemingshen/entry/non_utf_8_encoding_in */ final ZipArchiveOutputStream zos3 = new ZipArchiveOutputStream( - new FileOutputStream(ConstantsForTests.UMLAUT_UTF8_ZIP_NO_EFS) + new FileOutputStream(UMLAUT_UTF8_ZIP_NO_EFS) ); /* explicitely set Apache Commons default for documentation */ zos3.setEncoding("utf-8"); /* no EFS flag! */ zos3.setUseLanguageEncodingFlag(false); - ZipArchiveEntry entry3 = new ZipArchiveEntry(ConstantsForTests.UMLAUT_TXT); + ZipArchiveEntry entry3 = new ZipArchiveEntry(UMLAUT_TXT); entry3.setSize(data.length); entry3.setTime(FileTime.fromMillis(0)); zos3.putArchiveEntry(entry3); diff --git a/src/test/resources/umlaut-cp437.zip b/src/test/resources/org/opentripplanner/datastore/file/umlaut-cp437.zip similarity index 100% rename from src/test/resources/umlaut-cp437.zip rename to src/test/resources/org/opentripplanner/datastore/file/umlaut-cp437.zip diff --git a/src/test/resources/umlaut-utf8-no-efs.zip b/src/test/resources/org/opentripplanner/datastore/file/umlaut-utf8-no-efs.zip similarity index 100% rename from src/test/resources/umlaut-utf8-no-efs.zip rename to src/test/resources/org/opentripplanner/datastore/file/umlaut-utf8-no-efs.zip diff --git a/src/test/resources/umlaut-utf8.zip b/src/test/resources/org/opentripplanner/datastore/file/umlaut-utf8.zip similarity index 100% rename from src/test/resources/umlaut-utf8.zip rename to src/test/resources/org/opentripplanner/datastore/file/umlaut-utf8.zip diff --git a/src/test/resources/osm/oslo-east-filtered.osm.pbf b/src/test/resources/org/opentripplanner/oslo-east-filtered.osm.pbf similarity index 100% rename from src/test/resources/osm/oslo-east-filtered.osm.pbf rename to src/test/resources/org/opentripplanner/oslo-east-filtered.osm.pbf From c6a0dac017daaa23fedcf97f99164953bcab2762 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 25 Sep 2023 13:29:59 +0200 Subject: [PATCH 49/95] Resolve merge artifacts --- .../org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java | 4 ++-- .../org/opentripplanner/apis/gtfs/datafetchers/TripImpl.java | 2 ++ .../apis/gtfs/generated/GraphQLDataFetchers.java | 2 +- .../opentripplanner/apis/gtfs/mapping/BikesAllowedMapper.java | 4 ++-- .../apis/gtfs/mapping/BikesAllowedMapperTest.java | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java index 4eb3b15146e..56775024e0b 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java @@ -11,9 +11,9 @@ import org.opentripplanner.apis.gtfs.GraphQLUtils; import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLBikesAllowed; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLBikesAllowed; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLTransitMode; -import org.opentripplanner.ext.gtfsgraphqlapi.mapping.BikesAllowedMapper; +import org.opentripplanner.apis.gtfs.mapping.BikesAllowedMapper; import org.opentripplanner.routing.alertpatch.EntitySelector; import org.opentripplanner.routing.alertpatch.TransitAlert; import org.opentripplanner.routing.services.TransitAlertService; diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripImpl.java index 5eb11c38973..3be84cb37de 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripImpl.java @@ -20,6 +20,8 @@ import org.opentripplanner.apis.gtfs.GraphQLUtils; import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLBikesAllowed; +import org.opentripplanner.apis.gtfs.mapping.BikesAllowedMapper; import org.opentripplanner.framework.time.ServiceDateUtils; import org.opentripplanner.model.Timetable; import org.opentripplanner.model.TripTimeOnDate; diff --git a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java index b0a4d5f8f93..d0562215f70 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -14,6 +14,7 @@ import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAlertCauseType; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAlertEffectType; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAlertSeverityLevelType; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLBikesAllowed; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLInputField; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRelativeDirection; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRoutingErrorCode; @@ -21,7 +22,6 @@ import org.opentripplanner.apis.gtfs.model.RideHailingProvider; import org.opentripplanner.apis.gtfs.model.StopPosition; import org.opentripplanner.ext.fares.model.FareRuleSet; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLBikesAllowed; import org.opentripplanner.ext.ridehailing.model.RideEstimate; import org.opentripplanner.model.StopTimesInPattern; import org.opentripplanner.model.SystemNotice; diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/BikesAllowedMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/BikesAllowedMapper.java index ad7019ac343..cd04601d599 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/mapping/BikesAllowedMapper.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/BikesAllowedMapper.java @@ -1,7 +1,7 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.mapping; +package org.opentripplanner.apis.gtfs.mapping; import javax.annotation.Nonnull; -import org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLBikesAllowed; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLBikesAllowed; import org.opentripplanner.transit.model.network.BikeAccess; public class BikesAllowedMapper { diff --git a/src/test/java/org/opentripplanner/apis/gtfs/mapping/BikesAllowedMapperTest.java b/src/test/java/org/opentripplanner/apis/gtfs/mapping/BikesAllowedMapperTest.java index ba1b6a2a5f8..5ed267f1f4f 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/mapping/BikesAllowedMapperTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/mapping/BikesAllowedMapperTest.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.gtfsgraphqlapi.mapping; +package org.opentripplanner.apis.gtfs.mapping; import static org.junit.jupiter.api.Assertions.assertEquals; From 153a5083c9297418d20e97e8d75836794b847042 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 25 Sep 2023 13:35:41 +0200 Subject: [PATCH 50/95] Use correct pacakge name --- .../org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml b/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml index 66966dd941c..14d00ec1bd4 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml @@ -40,7 +40,7 @@ config: VehicleRentalStation: org.opentripplanner.service.vehiclerental.model.VehicleRentalStation#VehicleRentalStation RentalVehicle: org.opentripplanner.service.vehiclerental.model.VehicleRentalVehicle#VehicleRentalVehicle VehicleRentalUris: org.opentripplanner.service.vehiclerental.model.VehicleRentalStationUris#VehicleRentalStationUris - BikesAllowed: org.opentripplanner.ext.gtfsgraphqlapi.generated.GraphQLTypes.GraphQLBikesAllowed#GraphQLBikesAllowed + BikesAllowed: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLBikesAllowed#GraphQLBikesAllowed BookingInfo: org.opentripplanner.model.BookingInfo BookingTime: org.opentripplanner.model.BookingTime CarPark: org.opentripplanner.routing.vehicle_parking.VehicleParking#VehicleParking From ec1025dbcff4f34b33da239ec160b616dd465bcb Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 25 Sep 2023 15:06:52 +0200 Subject: [PATCH 51/95] Move alert entity to the other typr resolvers --- .../java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java index a8c31766b6f..d7b936a6198 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java @@ -116,6 +116,7 @@ protected static GraphQLSchema buildSchema() { .type("PlaceInterface", type -> type.typeResolver(new PlaceInterfaceTypeResolver())) .type("StopPosition", type -> type.typeResolver(new StopPosition() {})) .type("FareProduct", type -> type.typeResolver(new FareProductTypeResolver())) + .type("AlertEntity", type -> type.typeResolver(new AlertEntityTypeResolver())) .type(typeWiring.build(AgencyImpl.class)) .type(typeWiring.build(AlertImpl.class)) .type(typeWiring.build(BikeParkImpl.class)) @@ -155,7 +156,6 @@ protected static GraphQLSchema buildSchema() { .type(typeWiring.build(VehicleRentalStationImpl.class)) .type(typeWiring.build(RentalVehicleImpl.class)) .type(typeWiring.build(RentalVehicleTypeImpl.class)) - .type("AlertEntity", type -> type.typeResolver(new AlertEntityTypeResolver())) .type(typeWiring.build(StopOnRouteImpl.class)) .type(typeWiring.build(StopOnTripImpl.class)) .type(typeWiring.build(UnknownImpl.class)) From 699214a687dadafa395a2d709de2d72c01fa6ca2 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 25 Sep 2023 15:08:14 +0200 Subject: [PATCH 52/95] Correct comment --- .../org/opentripplanner/apis/gtfs/mapping/SeverityMapper.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/SeverityMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/SeverityMapper.java index f8abe861992..27bcca99e57 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/mapping/SeverityMapper.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/SeverityMapper.java @@ -1,6 +1,5 @@ package org.opentripplanner.apis.gtfs.mapping; -import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAlertSeverityLevelType; import org.opentripplanner.routing.alertpatch.AlertSeverity; @@ -10,7 +9,7 @@ public class SeverityMapper { /** - * Returns GraphQL API string counter part for internal {@link AlertSeverity} enum. Defaults + * Returns GraphQL API counterpart for internal {@link AlertSeverity} enum. Defaults * to returning UNKNOWN_SEVERITY. */ public static GraphQLAlertSeverityLevelType getGraphQLSeverity(AlertSeverity severity) { From ee01679ea562ac7b1ae28b783d87fae4908a121a Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 25 Sep 2023 15:31:03 +0200 Subject: [PATCH 53/95] Rename FakeGraph and make it package-private --- .../graph_builder/module/linking/LinkingTest.java | 9 ++++----- .../module/{FakeGraph.java => linking/TestGraph.java} | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) rename src/test/java/org/opentripplanner/graph_builder/module/{FakeGraph.java => linking/TestGraph.java} (97%) diff --git a/src/test/java/org/opentripplanner/graph_builder/module/linking/LinkingTest.java b/src/test/java/org/opentripplanner/graph_builder/module/linking/LinkingTest.java index c4576f0e9ca..41a74fb2dd3 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/linking/LinkingTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/linking/LinkingTest.java @@ -3,12 +3,11 @@ 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.graph_builder.module.FakeGraph.addExtraStops; -import static org.opentripplanner.graph_builder.module.FakeGraph.addRegularStopGrid; -import static org.opentripplanner.graph_builder.module.FakeGraph.link; +import static org.opentripplanner.graph_builder.module.linking.TestGraph.addExtraStops; +import static org.opentripplanner.graph_builder.module.linking.TestGraph.addRegularStopGrid; +import static org.opentripplanner.graph_builder.module.linking.TestGraph.link; import java.io.File; -import java.net.URISyntaxException; import java.util.Comparator; import java.util.List; import org.junit.jupiter.api.Test; @@ -111,7 +110,7 @@ public void testSplitting() { * We do this by building the graphs and then comparing the stop tree caches. */ @Test - public void testStopsLinkedIdentically() throws URISyntaxException { + public void testStopsLinkedIdentically() { // build the graph without the added stops TestOtpModel model = buildGraphNoTransit(); Graph g1 = model.graph(); diff --git a/src/test/java/org/opentripplanner/graph_builder/module/FakeGraph.java b/src/test/java/org/opentripplanner/graph_builder/module/linking/TestGraph.java similarity index 97% rename from src/test/java/org/opentripplanner/graph_builder/module/FakeGraph.java rename to src/test/java/org/opentripplanner/graph_builder/module/linking/TestGraph.java index c1bc5a30fae..f4de2291397 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/FakeGraph.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/linking/TestGraph.java @@ -1,4 +1,4 @@ -package org.opentripplanner.graph_builder.module; +package org.opentripplanner.graph_builder.module.linking; import java.util.List; import org.opentripplanner.routing.graph.Graph; @@ -17,7 +17,7 @@ * Get graphs of Columbus Ohio with real OSM streets and a synthetic transit system for use in * testing. */ -public class FakeGraph { +class TestGraph { /** Add a regular grid of stops to the graph */ public static void addRegularStopGrid(Graph graph) { From 883baff1cf88877506eee8db3ea5f4963f2730cb Mon Sep 17 00:00:00 2001 From: Vesa Meskanen Date: Mon, 25 Sep 2023 16:31:38 +0300 Subject: [PATCH 54/95] Change bollard property to more general and useful motor vehicle barrier --- .../openstreetmap/model/OSMNode.java | 16 ++++++---- .../model/vertex/BarrierVertexTest.java | 32 +++++++++---------- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMNode.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMNode.java index 42a590d1e24..092fdc2a6cf 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMNode.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMNode.java @@ -1,5 +1,6 @@ package org.opentripplanner.openstreetmap.model; +import java.util.Set; import org.locationtech.jts.geom.Coordinate; import org.opentripplanner.street.model.StreetTraversalPermission; @@ -35,13 +36,15 @@ public boolean hasCrossingTrafficLight() { return hasTag("crossing") && "traffic_signals".equals(getTag("crossing")); } - /** - * Checks if this node is bollard + static final Set motorVehicleBarriers = Set.of("bollard", "bar", "chain"); + + /* Checks if this node is a barrier which prevents motor vehicle traffic * * @return true if it is */ - public boolean isBollard() { - return isTag("barrier", "bollard"); + public boolean isMotorVehicleBarrier() { + var barrier = this.getTag("barrier"); + return barrier != null && motorVehicleBarriers.contains(barrier); } /** @@ -51,7 +54,7 @@ public boolean isBollard() { */ public boolean isBarrier() { return ( - isBollard() || + isMotorVehicleBarrier() || isPedestrianExplicitlyDenied() || isBicycleExplicitlyDenied() || isMotorcarExplicitlyDenied() || @@ -66,8 +69,7 @@ public boolean isBarrier() { @Override public StreetTraversalPermission overridePermissions(StreetTraversalPermission def) { StreetTraversalPermission permission = def; - if (isBollard()) { - //According to OSM default permissions are access=no, foot=yes, bicycle=yes + if (isMotorVehicleBarrier()) { permission = permission.remove(StreetTraversalPermission.CAR); } return super.overridePermissions(permission); diff --git a/src/test/java/org/opentripplanner/street/model/vertex/BarrierVertexTest.java b/src/test/java/org/opentripplanner/street/model/vertex/BarrierVertexTest.java index 4e2b9872625..a92d69b39f0 100644 --- a/src/test/java/org/opentripplanner/street/model/vertex/BarrierVertexTest.java +++ b/src/test/java/org/opentripplanner/street/model/vertex/BarrierVertexTest.java @@ -24,42 +24,42 @@ public class BarrierVertexTest { @Test public void testBarrierPermissions() { - OSMNode simpleBarier = new OSMNode(); - assertFalse(simpleBarier.isBollard()); - simpleBarier.addTag("barrier", "bollard"); - assertTrue(simpleBarier.isBollard()); + OSMNode simpleBarrier = new OSMNode(); + assertFalse(simpleBarrier.isMotorVehicleBarrier()); + simpleBarrier.addTag("barrier", "bollard"); + assertTrue(simpleBarrier.isMotorVehicleBarrier()); String label = "simpleBarrier"; - BarrierVertex bv = new BarrierVertex(simpleBarier.lon, simpleBarier.lat, 0); + BarrierVertex bv = new BarrierVertex(simpleBarrier.lon, simpleBarrier.lat, 0); bv.setBarrierPermissions( - simpleBarier.overridePermissions(BarrierVertex.defaultBarrierPermissions) + simpleBarrier.overridePermissions(BarrierVertex.defaultBarrierPermissions) ); assertEquals(StreetTraversalPermission.PEDESTRIAN_AND_BICYCLE, bv.getBarrierPermissions()); - simpleBarier.addTag("foot", "yes"); + simpleBarrier.addTag("foot", "yes"); bv.setBarrierPermissions( - simpleBarier.overridePermissions(BarrierVertex.defaultBarrierPermissions) + simpleBarrier.overridePermissions(BarrierVertex.defaultBarrierPermissions) ); assertEquals(StreetTraversalPermission.PEDESTRIAN_AND_BICYCLE, bv.getBarrierPermissions()); - simpleBarier.addTag("bicycle", "yes"); + simpleBarrier.addTag("bicycle", "yes"); bv.setBarrierPermissions( - simpleBarier.overridePermissions(BarrierVertex.defaultBarrierPermissions) + simpleBarrier.overridePermissions(BarrierVertex.defaultBarrierPermissions) ); assertEquals(StreetTraversalPermission.PEDESTRIAN_AND_BICYCLE, bv.getBarrierPermissions()); - simpleBarier.addTag("access", "no"); + simpleBarrier.addTag("access", "no"); bv.setBarrierPermissions( - simpleBarier.overridePermissions(BarrierVertex.defaultBarrierPermissions) + simpleBarrier.overridePermissions(BarrierVertex.defaultBarrierPermissions) ); assertEquals(StreetTraversalPermission.PEDESTRIAN_AND_BICYCLE, bv.getBarrierPermissions()); - simpleBarier.addTag("motor_vehicle", "no"); + simpleBarrier.addTag("motor_vehicle", "no"); bv.setBarrierPermissions( - simpleBarier.overridePermissions(BarrierVertex.defaultBarrierPermissions) + simpleBarrier.overridePermissions(BarrierVertex.defaultBarrierPermissions) ); assertEquals(StreetTraversalPermission.PEDESTRIAN_AND_BICYCLE, bv.getBarrierPermissions()); - simpleBarier.addTag("bicycle", "no"); + simpleBarrier.addTag("bicycle", "no"); bv.setBarrierPermissions( - simpleBarier.overridePermissions(BarrierVertex.defaultBarrierPermissions) + simpleBarrier.overridePermissions(BarrierVertex.defaultBarrierPermissions) ); assertEquals(StreetTraversalPermission.PEDESTRIAN, bv.getBarrierPermissions()); From b5ead2480017070216079fe920726daae4bd8db5 Mon Sep 17 00:00:00 2001 From: Vesa Meskanen Date: Mon, 25 Sep 2023 16:40:41 +0300 Subject: [PATCH 55/95] Add comments about barrier pruning logic --- .../opentripplanner/street/model/vertex/BarrierVertex.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/street/model/vertex/BarrierVertex.java b/src/main/java/org/opentripplanner/street/model/vertex/BarrierVertex.java index 9132bc13291..a7ad6d3830b 100644 --- a/src/main/java/org/opentripplanner/street/model/vertex/BarrierVertex.java +++ b/src/main/java/org/opentripplanner/street/model/vertex/BarrierVertex.java @@ -44,14 +44,18 @@ public void makeBarrierAtEndReachable() { var edgeCount = this.getDegreeOut() + this.getDegreeIn(); var needsFix = false; if (edgeCount == 1) { + // only one edge connects the vertex, must be end point needsFix = true; } else if (edgeCount == 2) { var out = this.getOutgoing(); var in = this.getIncoming(); if ( + // if only outgoing edges or incoming edges -> vertex does not act as a pass-through point and barrier makes no sense out.isEmpty() || in.isEmpty() || - out.iterator().next().getToVertex() == in.iterator().next().getFromVertex() + // in+out edge pair connects the vertex to a single adjacent vertex -> must be street end point + out.iterator().next().getToVertex() == + in.iterator().next().getFromVertex() ) { needsFix = true; } From 5e424de3fbc9567cf5340618c918100a924f7f13 Mon Sep 17 00:00:00 2001 From: Vesa Meskanen Date: Mon, 25 Sep 2023 20:23:40 +0300 Subject: [PATCH 56/95] Follow code style conventions --- .../org/opentripplanner/openstreetmap/model/OSMNode.java | 6 +++--- .../opentripplanner/openstreetmap/model/OSMWithTags.java | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMNode.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMNode.java index 092fdc2a6cf..ff775c98335 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMNode.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMNode.java @@ -6,6 +6,8 @@ public class OSMNode extends OSMWithTags { + static final Set MOTOR_VEHICLE_BARRIERS = Set.of("bollard", "bar", "chain"); + public double lat; public double lon; @@ -36,15 +38,13 @@ public boolean hasCrossingTrafficLight() { return hasTag("crossing") && "traffic_signals".equals(getTag("crossing")); } - static final Set motorVehicleBarriers = Set.of("bollard", "bar", "chain"); - /* Checks if this node is a barrier which prevents motor vehicle traffic * * @return true if it is */ public boolean isMotorVehicleBarrier() { var barrier = this.getTag("barrier"); - return barrier != null && motorVehicleBarriers.contains(barrier); + return barrier != null && MOTOR_VEHICLE_BARRIERS.contains(barrier); } /** diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java index ed8819303d3..4b4883544d4 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java @@ -46,6 +46,8 @@ public class OSMWithTags { "escape" ); + static final Set LEVEL_TAGS = Set.of("level", "layer"); + /* To save memory this is only created when an entity actually has tags. */ private Map tags; @@ -55,8 +57,6 @@ public class OSMWithTags { private OsmProvider osmProvider; - static final Set levelTags = Set.of("level", "layer"); - public static boolean isFalse(String tagValue) { return ("no".equals(tagValue) || "0".equals(tagValue) || "false".equals(tagValue)); } @@ -589,7 +589,7 @@ private boolean isTagDeniedAccess(String tagName) { * Some entities can have a semicolon separated list of levels (e.g. elevators) */ public Set getLevels() { - var levels = getMultiTagValues(levelTags); + var levels = getMultiTagValues(LEVEL_TAGS); if (levels.isEmpty()) { // default return Set.of("0"); From 1816d1b4452490edb2474be05cfeee821cdf9295 Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Tue, 26 Sep 2023 07:51:15 +0000 Subject: [PATCH 57/95] Add changelog entry for #5369 [ci skip] --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Changelog.md b/docs/Changelog.md index 5240802ac9e..d823c626077 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -11,6 +11,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Fix rental scooter access [#5361](https://github.com/opentripplanner/OpenTripPlanner/pull/5361) - De-duplicate stops returned by `stopsByRadius` [#5366](https://github.com/opentripplanner/OpenTripPlanner/pull/5366) - Fix value mapping for bikesAllowed in GTFS GraphQL API [#5368](https://github.com/opentripplanner/OpenTripPlanner/pull/5368) +- Apply correct traversal permissions to barrier vertex [#5369](https://github.com/opentripplanner/OpenTripPlanner/pull/5369) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.4.0 (2023-09-13) From 3bcdf54d598c6237dec485f4b936a65bd50c5e2a Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Tue, 26 Sep 2023 11:19:40 +0200 Subject: [PATCH 58/95] Update docs/ReleaseChecklist.md Co-authored-by: Leonard Ehrenfried --- docs/ReleaseChecklist.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ReleaseChecklist.md b/docs/ReleaseChecklist.md index 65ce8eb383e..7702a60e51c 100644 --- a/docs/ReleaseChecklist.md +++ b/docs/ReleaseChecklist.md @@ -92,7 +92,7 @@ manually is more tedious, but keeps eyes on each step and is less prone to failu milestone. * Close open PRs older than 2 years, make sure the milestone is set to `Rejected`. * Rename the old milestone from `x.y (Next Release)` to `x.y`. All issues and PRs assigned to - this milestone is updated. + this milestone are automatically updated. * Create a new milestone: `x.y+1 (Next Release)` * All PullRequests SHOULD have a milestone (except some very old ones) * Assign all *open* PRs to this new milestone `x.y+1 (Next Release)`. From dd85e851af06732420d5230d5cef1ec916c35ecc Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 26 Sep 2023 12:15:18 +0200 Subject: [PATCH 59/95] Automatically upgrade jib and shade maven plugins [ci skip] --- renovate.json5 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/renovate.json5 b/renovate.json5 index fe941f8f550..934001eae04 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -87,7 +87,9 @@ "org.apache.maven.plugins:maven-gpg-plugin", "org.apache.maven.plugins:maven-source-plugin", "io.github.git-commit-id:git-commit-id-maven-plugin", - "com.hubspot.maven.plugins:prettier-maven-plugin" + "com.hubspot.maven.plugins:prettier-maven-plugin", + "com.google.cloud.tools:jib-maven-plugin", + "org.apache.maven.plugins:maven-shade-plugin" ], "matchPackagePrefixes": [ "org.junit.jupiter:", From 5535ca60fec3c4fadaa86d16a0f7879b72cbfe23 Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Tue, 26 Sep 2023 14:29:41 +0000 Subject: [PATCH 60/95] Add changelog entry for #5339 [ci skip] --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Changelog.md b/docs/Changelog.md index d823c626077..55e24f2dd68 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -12,6 +12,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - De-duplicate stops returned by `stopsByRadius` [#5366](https://github.com/opentripplanner/OpenTripPlanner/pull/5366) - Fix value mapping for bikesAllowed in GTFS GraphQL API [#5368](https://github.com/opentripplanner/OpenTripPlanner/pull/5368) - Apply correct traversal permissions to barrier vertex [#5369](https://github.com/opentripplanner/OpenTripPlanner/pull/5369) +- Move GTFS GraphQL API out of the sandbox [#5339](https://github.com/opentripplanner/OpenTripPlanner/pull/5339) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.4.0 (2023-09-13) From d8022ae8c1d494ed758eec2dde35521cb3ef92e1 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 27 Sep 2023 10:00:10 +0200 Subject: [PATCH 61/95] Use new API for loading resource --- .../graph_builder/module/osm/OsmModuleTest.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java index a342b9a3c83..e92a4035d86 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java @@ -306,12 +306,7 @@ private void testBarrierAtEnd() { var deduplicator = new Deduplicator(); var graph = new Graph(deduplicator); - File file = new File( - URLDecoder.decode( - getClass().getResource("noaccess-at-end.pbf").getFile(), - StandardCharsets.UTF_8 - ) - ); + File file = RES.file("accessno-at-end.pbf"); OsmProvider provider = new OsmProvider(file, false); OsmModule loader = OsmModule.of(provider, graph).build(); loader.buildGraph(); From 7033e5e23450008dfe013d84388fe9994fd8f70f Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 27 Sep 2023 15:10:03 +0200 Subject: [PATCH 62/95] Apply suggestions from code review Co-authored-by: Johan Torin --- .../datastore/file/ZipFileDataSourceTest.java | 9 +++------ .../org/opentripplanner/test/support/ResourceLoader.java | 4 ++-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/test/java/org/opentripplanner/datastore/file/ZipFileDataSourceTest.java b/src/test/java/org/opentripplanner/datastore/file/ZipFileDataSourceTest.java index 3f55f0188b7..f5aac05180a 100644 --- a/src/test/java/org/opentripplanner/datastore/file/ZipFileDataSourceTest.java +++ b/src/test/java/org/opentripplanner/datastore/file/ZipFileDataSourceTest.java @@ -125,22 +125,19 @@ public void testUnsupportedDelete() { @Test public void testEntryEncoding() { // has worked before #4835, for verification remove the attempt to set to code page to cp437 - File target = UMLAUT_UTF8_ZIP; - CompositeDataSource subject = new ZipFileDataSource(target, GTFS); + CompositeDataSource subject = new ZipFileDataSource(UMLAUT_UTF8_ZIP, GTFS); DataSource entry = subject.content().iterator().next(); assertEquals(UMLAUT_TXT, entry.name()); // has worked before #4835, for verification remove the attempt to set to code page to cp437 - target = UMLAUT_UTF8_ZIP_NO_EFS; - subject = new ZipFileDataSource(target, GTFS); + subject = new ZipFileDataSource(UMLAUT_UTF8_ZIP_NO_EFS, GTFS); entry = subject.content().iterator().next(); assertEquals(UMLAUT_TXT, entry.name()); // only works after #4835, will fail with "Invalid CEN header (bad entry name)" when verifying - target = UMLAUT_CP437_ZIP; - subject = new ZipFileDataSource(target, GTFS); + subject = new ZipFileDataSource(UMLAUT_CP437_ZIP, GTFS); entry = subject.content().iterator().next(); assertEquals(UMLAUT_TXT, entry.name()); diff --git a/src/test/java/org/opentripplanner/test/support/ResourceLoader.java b/src/test/java/org/opentripplanner/test/support/ResourceLoader.java index 4412af25bb9..40df14e2928 100644 --- a/src/test/java/org/opentripplanner/test/support/ResourceLoader.java +++ b/src/test/java/org/opentripplanner/test/support/ResourceLoader.java @@ -13,7 +13,7 @@ */ public class ResourceLoader { - final Class clazz; + private final Class clazz; private ResourceLoader(Class clazz) { this.clazz = clazz; @@ -33,7 +33,7 @@ public static ResourceLoader of(Object object) { public File file(String path) { URL resource = url(path); var file = new File(resource.getFile()); - assertTrue(file.exists(), "File %s not found on file system".formatted(file.getAbsolutePath())); + assertTrue(file.exists(), "File '%s' not found on file system.".formatted(file.getAbsolutePath())); return file; } From 311f16f64b89b0a436fe17abda5b22f5c32549a9 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 27 Sep 2023 15:12:46 +0200 Subject: [PATCH 63/95] Fix formatting --- .../datastore/file/ZipFileDataSourceTest.java | 11 +++++------ .../opentripplanner/test/support/ResourceLoader.java | 7 +++++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/test/java/org/opentripplanner/datastore/file/ZipFileDataSourceTest.java b/src/test/java/org/opentripplanner/datastore/file/ZipFileDataSourceTest.java index f5aac05180a..995bc2540b7 100644 --- a/src/test/java/org/opentripplanner/datastore/file/ZipFileDataSourceTest.java +++ b/src/test/java/org/opentripplanner/datastore/file/ZipFileDataSourceTest.java @@ -28,7 +28,6 @@ public class ZipFileDataSourceTest { // Sometime close to 2000-01-01 private static final long TIME = 30 * 365 * 24 * 60 * 60 * 1000L; - private static final String FILENAME = ConstantsForTests.CALTRAIN_GTFS.getAbsolutePath(); private static final ResourceLoader RES = ResourceLoader.of(ZipFileDataSourceTest.class); /* filenames encoded with cp437 and utf8 */ public static final File UMLAUT_CP437_ZIP = RES.file("umlaut-cp437.zip"); @@ -39,8 +38,8 @@ public class ZipFileDataSourceTest { @Test public void testAccessorsForNoneExistingFile() throws IOException { // Given: - File target = new File(FILENAME); - File copyTarget = new File(FILENAME); + File target = new File(ConstantsForTests.CALTRAIN_GTFS.getAbsolutePath()); + File copyTarget = new File(ConstantsForTests.CALTRAIN_GTFS.getAbsolutePath()); CompositeDataSource subject = new ZipFileDataSource(target, GTFS); CompositeDataSource copySubject = new ZipFileDataSource(copyTarget, GTFS); String expectedPath = target.getPath(); @@ -69,7 +68,7 @@ public void testAccessorsForNoneExistingFile() throws IOException { @Test public void testIO() throws IOException { // Given: - File target = new File(FILENAME); + File target = new File(ConstantsForTests.CALTRAIN_GTFS.getAbsolutePath()); CompositeDataSource subject = new ZipFileDataSource(target, GTFS); Collection content = subject.content(); @@ -98,7 +97,7 @@ public void testIO() throws IOException { @Test public void testEntryProperties() { // Given: - File target = new File(FILENAME); + File target = new File(ConstantsForTests.CALTRAIN_GTFS.getAbsolutePath()); CompositeDataSource subject = new ZipFileDataSource(target, GTFS); DataSource entry = subject.entry("trips.txt"); @@ -115,7 +114,7 @@ public void testEntryProperties() { @Test public void testUnsupportedDelete() { // Given: - var target = new File(FILENAME); + var target = new File(ConstantsForTests.CALTRAIN_GTFS.getAbsolutePath()); CompositeDataSource subject = new ZipFileDataSource(target, GTFS); // When: delete entry is not implemented diff --git a/src/test/java/org/opentripplanner/test/support/ResourceLoader.java b/src/test/java/org/opentripplanner/test/support/ResourceLoader.java index 40df14e2928..1eb876a921e 100644 --- a/src/test/java/org/opentripplanner/test/support/ResourceLoader.java +++ b/src/test/java/org/opentripplanner/test/support/ResourceLoader.java @@ -33,7 +33,10 @@ public static ResourceLoader of(Object object) { public File file(String path) { URL resource = url(path); var file = new File(resource.getFile()); - assertTrue(file.exists(), "File '%s' not found on file system.".formatted(file.getAbsolutePath())); + assertTrue( + file.exists(), + "File '%s' not found on file system.".formatted(file.getAbsolutePath()) + ); return file; } @@ -42,7 +45,7 @@ public File file(String path) { */ public URL url(String name) { var resource = clazz.getResource(name); - var msg = "Resource '%s' not found on in package '%s'".formatted(name, clazz.getPackageName()); + var msg = "Resource '%s' not found in package '%s'".formatted(name, clazz.getPackageName()); assertNotNull(resource, msg); return resource; } From 7e6d5ede9fed6029caecdc8731a1bbbb5ca26d06 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 27 Sep 2023 15:21:55 +0200 Subject: [PATCH 64/95] Rename constant and add documentation --- .../graph_builder/module/osm/OsmModuleTest.java | 12 ++++++------ .../opentripplanner/test/support/ResourceLoader.java | 12 +++++++++++- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java index e92a4035d86..e883a9cc4e5 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java @@ -45,14 +45,14 @@ public class OsmModuleTest { - private static final ResourceLoader RES = ResourceLoader.of(OsmModuleTest.class); + private static final ResourceLoader RESOURCE_LOADER = ResourceLoader.of(OsmModuleTest.class); @Test public void testGraphBuilder() { var deduplicator = new Deduplicator(); var gg = new Graph(deduplicator); - File file = RES.file("map.osm.pbf"); + File file = RESOURCE_LOADER.file("map.osm.pbf"); OsmProvider provider = new OsmProvider(file, true); @@ -110,7 +110,7 @@ public void testBuildGraphDetailed() { var deduplicator = new Deduplicator(); var gg = new Graph(deduplicator); - File file = RES.file("NYC_small.osm.pbf"); + File file = RESOURCE_LOADER.file("NYC_small.osm.pbf"); OsmProvider provider = new OsmProvider(file, true); OsmModule osmModule = OsmModule.of(provider, gg).withAreaVisibility(true).build(); @@ -306,7 +306,7 @@ private void testBarrierAtEnd() { var deduplicator = new Deduplicator(); var graph = new Graph(deduplicator); - File file = RES.file("accessno-at-end.pbf"); + File file = RESOURCE_LOADER.file("accessno-at-end.pbf"); OsmProvider provider = new OsmProvider(file, false); OsmModule loader = OsmModule.of(provider, graph).build(); loader.buildGraph(); @@ -332,7 +332,7 @@ private Graph buildParkingLots() { var graph = new Graph(); var providers = Stream .of("B+R.osm.pbf", "P+R.osm.pbf") - .map(RES::file) + .map(RESOURCE_LOADER::file) .map(f -> new OsmProvider(f, false)) .toList(); var module = OsmModule @@ -357,7 +357,7 @@ private void testBuildingAreas(boolean skipVisibility) { var deduplicator = new Deduplicator(); var graph = new Graph(deduplicator); - File file = RES.file("usf_area.osm.pbf"); + File file = RESOURCE_LOADER.file("usf_area.osm.pbf"); OsmProvider provider = new OsmProvider(file, false); OsmModule loader = OsmModule.of(provider, graph).withAreaVisibility(!skipVisibility).build(); diff --git a/src/test/java/org/opentripplanner/test/support/ResourceLoader.java b/src/test/java/org/opentripplanner/test/support/ResourceLoader.java index 1eb876a921e..74d34c573ef 100644 --- a/src/test/java/org/opentripplanner/test/support/ResourceLoader.java +++ b/src/test/java/org/opentripplanner/test/support/ResourceLoader.java @@ -9,7 +9,11 @@ import java.net.URL; /** - * Loads files from the resources folder. + * Loads files from the resources folder relative to the package name of the class/instances + * passed to initializers. + *

+ * So if your class' package is org.opentripplanner.foo, then the corresponding resources + * must be placed in src/test/resources/org/opentripplanner/foo. */ public class ResourceLoader { @@ -19,10 +23,16 @@ private ResourceLoader(Class clazz) { this.clazz = clazz; } + /** + * Initialize a loader with the given class' package. + */ public static ResourceLoader of(Class clazz) { return new ResourceLoader(clazz); } + /** + * Initialize a loader with the given instances' class' package. + */ public static ResourceLoader of(Object object) { return new ResourceLoader(object.getClass()); } From 0666f3bbdeac40544c4c3de28c827b7d5645e349 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 27 Sep 2023 15:25:57 +0200 Subject: [PATCH 65/95] Remove unnecessary File constructors --- .../datastore/file/ZipFileDataSourceTest.java | 10 +++++----- .../file/ZipStreamDataSourceDecoratorTest.java | 11 +++++------ 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/test/java/org/opentripplanner/datastore/file/ZipFileDataSourceTest.java b/src/test/java/org/opentripplanner/datastore/file/ZipFileDataSourceTest.java index 995bc2540b7..da46fc430b6 100644 --- a/src/test/java/org/opentripplanner/datastore/file/ZipFileDataSourceTest.java +++ b/src/test/java/org/opentripplanner/datastore/file/ZipFileDataSourceTest.java @@ -38,8 +38,8 @@ public class ZipFileDataSourceTest { @Test public void testAccessorsForNoneExistingFile() throws IOException { // Given: - File target = new File(ConstantsForTests.CALTRAIN_GTFS.getAbsolutePath()); - File copyTarget = new File(ConstantsForTests.CALTRAIN_GTFS.getAbsolutePath()); + File target = ConstantsForTests.CALTRAIN_GTFS; + File copyTarget = ConstantsForTests.CALTRAIN_GTFS; CompositeDataSource subject = new ZipFileDataSource(target, GTFS); CompositeDataSource copySubject = new ZipFileDataSource(copyTarget, GTFS); String expectedPath = target.getPath(); @@ -68,7 +68,7 @@ public void testAccessorsForNoneExistingFile() throws IOException { @Test public void testIO() throws IOException { // Given: - File target = new File(ConstantsForTests.CALTRAIN_GTFS.getAbsolutePath()); + File target = ConstantsForTests.CALTRAIN_GTFS; CompositeDataSource subject = new ZipFileDataSource(target, GTFS); Collection content = subject.content(); @@ -97,7 +97,7 @@ public void testIO() throws IOException { @Test public void testEntryProperties() { // Given: - File target = new File(ConstantsForTests.CALTRAIN_GTFS.getAbsolutePath()); + File target = ConstantsForTests.CALTRAIN_GTFS; CompositeDataSource subject = new ZipFileDataSource(target, GTFS); DataSource entry = subject.entry("trips.txt"); @@ -114,7 +114,7 @@ public void testEntryProperties() { @Test public void testUnsupportedDelete() { // Given: - var target = new File(ConstantsForTests.CALTRAIN_GTFS.getAbsolutePath()); + var target = ConstantsForTests.CALTRAIN_GTFS; CompositeDataSource subject = new ZipFileDataSource(target, GTFS); // When: delete entry is not implemented diff --git a/src/test/java/org/opentripplanner/datastore/file/ZipStreamDataSourceDecoratorTest.java b/src/test/java/org/opentripplanner/datastore/file/ZipStreamDataSourceDecoratorTest.java index a165b05ed03..09479d85478 100644 --- a/src/test/java/org/opentripplanner/datastore/file/ZipStreamDataSourceDecoratorTest.java +++ b/src/test/java/org/opentripplanner/datastore/file/ZipStreamDataSourceDecoratorTest.java @@ -21,7 +21,6 @@ class ZipStreamDataSourceDecoratorTest { private static final long TIME = 30 * 365 * 24 * 60 * 60 * 1000L; - private static final String FILENAME = ConstantsForTests.CALTRAIN_GTFS.getAbsolutePath(); static final List EXPECTED_ZIP_ENTRIES = List.of( "trips.txt", "agency.txt", @@ -61,8 +60,8 @@ class ZipStreamDataSourceDecoratorTest { @Test void testAccessorsForNoneExistingFile() throws IOException { // Given: - File target = new File(FILENAME); - File copyTarget = new File(FILENAME); + File target = ConstantsForTests.CALTRAIN_GTFS; + File copyTarget = ConstantsForTests.CALTRAIN_GTFS; CompositeDataSource subject = new ZipStreamDataSourceDecorator( new FileDataSource(target, GTFS) ); @@ -93,7 +92,7 @@ void testAccessorsForNoneExistingFile() throws IOException { @Test void testIO() throws IOException { // Given: - File target = new File(FILENAME); + File target = ConstantsForTests.CALTRAIN_GTFS; CompositeDataSource subject = new ZipStreamDataSourceDecorator( new FileDataSource(target, GTFS) ); @@ -119,7 +118,7 @@ void testIO() throws IOException { @Test void testMaxZipEntrySizeInMemory() throws IOException { - File target = new File(FILENAME); + File target = ConstantsForTests.CALTRAIN_GTFS; // force offloading to disk by setting maxZipEntrySizeInMemory=1 CompositeDataSource subject = new ZipStreamDataSourceDecorator( new FileDataSource(target, GTFS), @@ -142,7 +141,7 @@ void testMaxZipEntrySizeInMemory() throws IOException { @Test void testEntryProperties() { // Given: - File target = new File(FILENAME); + File target = ConstantsForTests.CALTRAIN_GTFS; CompositeDataSource subject = new ZipStreamDataSourceDecorator( new FileDataSource(target, GTFS) ); From be1be398f988a7eadb6f8be07882ab383a5a2ca8 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 27 Sep 2023 15:38:02 +0200 Subject: [PATCH 66/95] Move information into speed test package.md --- .../transit/speed_test/package.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/opentripplanner/transit/speed_test/package.md b/src/test/java/org/opentripplanner/transit/speed_test/package.md index ea4f1dcf620..d5e596d339f 100644 --- a/src/test/java/org/opentripplanner/transit/speed_test/package.md +++ b/src/test/java/org/opentripplanner/transit/speed_test/package.md @@ -10,17 +10,26 @@ To run the SpeedTest use the {@code --help} option to se the documentation. Ther documentation on this tool, hopefully with time, we will add more doc and maybe automate part of this test. -Example input files and setup is included in the resource test folder: +Example input files, setup and documentation is included in the resource test folder [test/ci-performance-test/](test/performance/README.md). -- {@code test/ci-performance-test/}. +## Download GTFS data: -## Running +- https://storage.googleapis.com/marduk-production/outbound/gtfs/rb_norway-aggregated-gtfs.zip +If the link above do not work you should be able to find it on the ENTUR web: + +- https://www.entur.org/ + +## Configure the test + +- Set the testDate in the speed-test-config.json + +## Run ``` mvn compiler:testCompile exec:java -Dexec.mainClass="org.opentripplanner.transit.speed_test.SpeedTest" -Dexec.classpathScope=test -Dexec.args="--dir=test/ci-performance-test/ -p md -n 4" ``` ## CI -The test is run after every merge to master. Its Github Actions workflow is defined +The test is run after every merge to dev-2.x. Its Github Actions workflow is defined in [performance-test.yml](../../../../../../../../.github/workflows/performance-test.yml). \ No newline at end of file From 46a347c5339e79a0e7d3123f8118bf8bbe64db42 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 27 Sep 2023 17:26:04 +0200 Subject: [PATCH 67/95] Use correct CARPOOL mode range in docs --- docs/RoutingModes.md | 2 +- .../org/opentripplanner/transit/model/basic/TransitMode.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/RoutingModes.md b/docs/RoutingModes.md index 88148d370e5..6267b4b0321 100644 --- a/docs/RoutingModes.md +++ b/docs/RoutingModes.md @@ -106,7 +106,7 @@ Used for street-level cable cars where the cable runs beneath the car. Private car trips shared with others. -This is currently not specified in GTFS so we use the mode type values 1500-1560 which are in the range of private taxis. +This is currently not specified in GTFS so we use the mode type values 1550-1560 which are in the range of private taxis.

COACH

diff --git a/src/main/java/org/opentripplanner/transit/model/basic/TransitMode.java b/src/main/java/org/opentripplanner/transit/model/basic/TransitMode.java index 1dfc87e03b9..e25cb435887 100644 --- a/src/main/java/org/opentripplanner/transit/model/basic/TransitMode.java +++ b/src/main/java/org/opentripplanner/transit/model/basic/TransitMode.java @@ -77,7 +77,7 @@ public String enumValueDescription() { case CARPOOL -> """ Private car trips shared with others. - This is currently not specified in GTFS so we use the mode type values 1500-1560 which are in the range of private taxis. + This is currently not specified in GTFS so we use the mode type values 1550-1560 which are in the range of private taxis. """; case TAXI -> "Using a taxi service"; }; From d9de2005f60c6b8f6c4c2bee4452e02b7ae0cd6e Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 28 Sep 2023 09:39:22 +0200 Subject: [PATCH 68/95] Update Readme [ci skip] --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 20bb5c45fb1..582511ce7f2 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,10 @@ GTFS and OpenStreetMap). It applies real-time updates and alerts with immediate clients, finding itineraries that account for disruptions and service changes. Note that this branch contains **OpenTripPlanner 2**, the second major version of OTP, which has -been under development since Q2 2018. The latest version of OTP is v2.2.0, released in November 2022. +been under development since 2018. The latest version of OTP is v2.4.0, released in September 2023. -If you do not want to test or explore this version, please switch to the final 1.x release -tag `v1.5.0` or the `dev-1.x` branch for any patches and bugfixes applied to the v1.5.0 release. +If you do not want to use this version, please switch to the final 1.x release +tag `v1.5.0` or the `dev-1.x` branch. ## Performance Test From 154a98f83d57fffeb5aaa9a12ae812369a2040c3 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 28 Sep 2023 11:34:37 +0200 Subject: [PATCH 69/95] Update version comparison [ci skip] --- docs/Version-Comparison.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/Version-Comparison.md b/docs/Version-Comparison.md index 17b38dffb9b..e92e2330b18 100644 --- a/docs/Version-Comparison.md +++ b/docs/Version-Comparison.md @@ -52,7 +52,8 @@ the EU profile, and generalization to the EU profile should be feasible once it | SIRI Realtime | no | yes | | Elevation data | TIFF and NED | TIFF and NED | | One-to-many routing,
isochrones and scripting | yes | no | -| Java version | 8+ | 11+ | +| Isochrones | yes | yes | +| Java version | 8+ | 17+ | | Multiple regions per server | yes | no | | Hot reloading of graphs | yes | no | | Street (OSM) routing algorithm | Generalized cost A* | Generalized cost A* | From 748cf90b77a96771706a330c3d8de9f0548f5604 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 28 Sep 2023 11:37:51 +0200 Subject: [PATCH 70/95] Fix link [ci skip] --- docs/Version-Comparison.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Version-Comparison.md b/docs/Version-Comparison.md index e92e2330b18..ce04016417b 100644 --- a/docs/Version-Comparison.md +++ b/docs/Version-Comparison.md @@ -126,7 +126,7 @@ OTP2 to simplify the code base and make it easier to reason about security. Less parameters are available on the OTP2 REST API than in OTP1. Often there is no practical loss of functionality, just a different way of expressing things due to the new routing algorithms. A summary of parameters that have been removed and their replacements can be found in the [migration -guide](Version-Comparison.md#Migration guide) +guide](Version-Comparison.md#migration-guide) ## OTP Trip planning and Transit index APIs From 5a7aa40b2ff8ffcba6b744e1b6c6617b99e140d8 Mon Sep 17 00:00:00 2001 From: Vesa Meskanen Date: Thu, 28 Sep 2023 12:53:21 +0300 Subject: [PATCH 71/95] Fix barrier vertex test --- .../module/osm/OsmModuleTest.java | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java index 14511258ae3..0f54be1007c 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java @@ -37,6 +37,7 @@ import org.opentripplanner.routing.impl.GraphPathFinder; import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.model.edge.StreetEdge; +import org.opentripplanner.street.model.vertex.BarrierVertex; import org.opentripplanner.street.model.vertex.IntersectionVertex; import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex; import org.opentripplanner.street.model.vertex.Vertex; @@ -304,17 +305,16 @@ void createArtificalEntrancesToUnlikedParkingLots() { } /** - * Test that a barrier vertex created when street ends to an access restriction - * will not prevent routing to that vertex + * Test that a barrier vertex at ending street will get no access limit */ @Test - private void testBarrierAtEnd() { + void testBarrierAtEnd() { var deduplicator = new Deduplicator(); var graph = new Graph(deduplicator); File file = new File( URLDecoder.decode( - getClass().getResource("noaccess-at-end.pbf").getFile(), + getClass().getResource("accessno-at-end.pbf").getFile(), StandardCharsets.UTF_8 ) ); @@ -322,20 +322,16 @@ private void testBarrierAtEnd() { OsmModule loader = OsmModule.of(provider, graph).build(); loader.buildGraph(); - RouteRequest request = new RouteRequest(); - - // Route along a simple 3 vertex highway which ends to a 'access=none' node Vertex start = graph.getVertex(VertexLabel.osm(1)); Vertex end = graph.getVertex(VertexLabel.osm(3)); - GraphPathFinder graphPathFinder = new GraphPathFinder(null); - List> pathList = graphPathFinder.graphPathFinderEntryPoint( - request, - Set.of(start), - Set.of(end) - ); - assertNotNull(pathList); - assertFalse(pathList.isEmpty()); + assertNotNull(start); + assertNotNull(end); + assertEquals(end.getClass(), BarrierVertex.class); + var barrier = (BarrierVertex) end; + + // assert that pruning removed traversal restrictions + assertEquals(barrier.getBarrierPermissions(), ALL); } @Nonnull From a9e72548b0b22883556ac8d3d7c1f87a6a0c126b Mon Sep 17 00:00:00 2001 From: Vesa Meskanen Date: Thu, 28 Sep 2023 13:00:12 +0300 Subject: [PATCH 72/95] Simplify barrier check by using existing super class method --- .../java/org/opentripplanner/openstreetmap/model/OSMNode.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMNode.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMNode.java index ff775c98335..a1897d87124 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMNode.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMNode.java @@ -43,8 +43,7 @@ public boolean hasCrossingTrafficLight() { * @return true if it is */ public boolean isMotorVehicleBarrier() { - var barrier = this.getTag("barrier"); - return barrier != null && MOTOR_VEHICLE_BARRIERS.contains(barrier); + return isOneOfTags("barrier", MOTOR_VEHICLE_BARRIERS); } /** From 78ca61de5d4accdde05ff35c26858f4a4e75209d Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 28 Sep 2023 15:09:18 +0200 Subject: [PATCH 73/95] Remove empty file --- .../org/opentripplanner/graph_builder/module/osm/OsmFilter.java | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/main/java/org/opentripplanner/graph_builder/module/osm/OsmFilter.java diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmFilter.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmFilter.java deleted file mode 100644 index e69de29bb2d..00000000000 From accc4dbe5b4b22587618b40e2a826304e2488fcf Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 28 Sep 2023 22:58:56 +0200 Subject: [PATCH 74/95] Fix spelling [ci skip] --- docs/apis/GraphQL-Tutorial.md | 4 ++-- .../org/opentripplanner/apis/gtfs/queries/plan.graphql | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/apis/GraphQL-Tutorial.md b/docs/apis/GraphQL-Tutorial.md index 0a88be5f9a7..8df6d6f38ee 100644 --- a/docs/apis/GraphQL-Tutorial.md +++ b/docs/apis/GraphQL-Tutorial.md @@ -71,9 +71,9 @@ Most people want to get routing results out of OTP, so lets see the query for th ```graphql { plan( - # these coordinate are in Portland, change this to YOUR origin + # these coordinates are in Portland, change this to YOUR origin from: { lat: 45.5552, lon: -122.6534 } - # these coordinate are in Portland, change this to YOUR destination + # these coordinates are in Portland, change this to YOUR destination to: { lat: 45.4908, lon: -122.5519 } # use the correct date and time of your request date: "2023-02-15", diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/queries/plan.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/plan.graphql index ea5c6eb3407..d71c991234d 100644 --- a/src/test/resources/org/opentripplanner/apis/gtfs/queries/plan.graphql +++ b/src/test/resources/org/opentripplanner/apis/gtfs/queries/plan.graphql @@ -1,8 +1,8 @@ { plan( - # these coordinate are in Portland, change this to YOUR origin + # these coordinates are in Portland, change this to YOUR origin from: { lat: 45.5552, lon: -122.6534 } - # these coordinate are in Portland, change this to YOUR destination + # these coordinates are in Portland, change this to YOUR destination to: { lat: 45.4908, lon: -122.5519 } # use the correct date and time of your request date: "2023-02-15", From bcf953fc8e50c02949f4ee6b17d472c18a9f89e7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 1 Oct 2023 01:11:46 +0000 Subject: [PATCH 75/95] Update dependency mkdocs to v1.5.3 --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 5cd0396ae85..1da1d032b91 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,4 @@ -mkdocs==1.5.2 +mkdocs==1.5.3 mkdocs-material==9.1.17 mike@git+https://github.com/jimporter/mike.git@f0522f245e64687dd18384fbd86b721175711474 mkdocs-no-sitemap-plugin==0.0.1 From a6344acdcc14d3282ca4dba92bb9803493112ebe Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 1 Oct 2023 04:23:21 +0000 Subject: [PATCH 76/95] Update lucene.version to v9.8.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fadcf31e056..a8dad8b6c57 100644 --- a/pom.xml +++ b/pom.xml @@ -66,7 +66,7 @@ 1.11.4 5.5.3 1.4.11 - 9.7.0 + 9.8.0 2.0.9 2.0.14 1.22 From 9363ab3c4592e6f68b8ea9d01efcd4b23179b5d2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 1 Oct 2023 04:23:17 +0000 Subject: [PATCH 77/95] Update dependency mkdocs-material to v9.4.2 --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 1da1d032b91..1474504e154 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,4 @@ mkdocs==1.5.3 -mkdocs-material==9.1.17 +mkdocs-material==9.4.2 mike@git+https://github.com/jimporter/mike.git@f0522f245e64687dd18384fbd86b721175711474 mkdocs-no-sitemap-plugin==0.0.1 From a60e6ddb85e499a4994d75bba35ae01df0aa5338 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Mon, 2 Oct 2023 13:17:52 +0300 Subject: [PATCH 78/95] Don't crash interline processing if no service data --- .../module/interlining/InterlineProcessor.java | 10 ++++++++-- .../model/calendar/CalendarServiceData.java | 16 ++++------------ 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/interlining/InterlineProcessor.java b/src/main/java/org/opentripplanner/graph_builder/module/interlining/InterlineProcessor.java index dae2f54f1d5..25aaa8413b6 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/interlining/InterlineProcessor.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/interlining/InterlineProcessor.java @@ -53,13 +53,19 @@ public InterlineProcessor( this.staySeatedNotAllowed = staySeatedNotAllowed; this.maxInterlineDistance = maxInterlineDistance > 0 ? maxInterlineDistance : 200; this.issueStore = issueStore; - this.transitServiceStart = calendarServiceData.getFirstDate(); + this.transitServiceStart = calendarServiceData.getFirstDate().orElse(null); this.daysInTransitService = - (int) ChronoUnit.DAYS.between(transitServiceStart, calendarServiceData.getLastDate()) + 1; + calendarServiceData + .getLastDate() + .map(lastDate -> (int) ChronoUnit.DAYS.between(transitServiceStart, lastDate) + 1) + .orElse(0); this.calendarServiceData = calendarServiceData; } public List run(Collection tripPatterns) { + if (daysInTransitService == 0) { + return List.of(); + } var interlinedTrips = this.getInterlinedTrips(tripPatterns); var transfers = interlinedTrips .entries() diff --git a/src/main/java/org/opentripplanner/model/calendar/CalendarServiceData.java b/src/main/java/org/opentripplanner/model/calendar/CalendarServiceData.java index 374993b1433..b007fa08ea1 100644 --- a/src/main/java/org/opentripplanner/model/calendar/CalendarServiceData.java +++ b/src/main/java/org/opentripplanner/model/calendar/CalendarServiceData.java @@ -73,20 +73,12 @@ public void add(CalendarServiceData other) { } } - public LocalDate getFirstDate() { - return serviceIdsByDate - .keySet() - .stream() - .min(LocalDate::compareTo) - .orElseThrow(() -> new IllegalStateException("No service data is available")); + public Optional getFirstDate() { + return serviceIdsByDate.keySet().stream().min(LocalDate::compareTo); } - public LocalDate getLastDate() { - return serviceIdsByDate - .keySet() - .stream() - .max(LocalDate::compareTo) - .orElseThrow(() -> new IllegalStateException("No service data is available")); + public Optional getLastDate() { + return serviceIdsByDate.keySet().stream().max(LocalDate::compareTo); } /* private methods */ From 1565ec5538a1de70635a0a39fe50ad8f754b4352 Mon Sep 17 00:00:00 2001 From: Johan Torin Date: Wed, 20 Sep 2023 15:53:24 +0200 Subject: [PATCH 79/95] Fix documentation formatting. --- .../ext/transmodelapi/model/plan/ViaLocationInputType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/plan/ViaLocationInputType.java b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/plan/ViaLocationInputType.java index 278a0dbeabd..fe5a3cf19e4 100644 --- a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/plan/ViaLocationInputType.java +++ b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/plan/ViaLocationInputType.java @@ -26,7 +26,7 @@ public static GraphQLInputObjectType create(GqlUtil gqlUtil) { .name("name") .description( "The name of the location. This is pass-through information" + - "and is not used in routing." + " and is not used in routing." ) .type(Scalars.GraphQLString) .build() From 06ba7aded776b6bd3d1f365123b72cfdcc306d1f Mon Sep 17 00:00:00 2001 From: Johan Torin Date: Fri, 29 Sep 2023 14:07:45 +0200 Subject: [PATCH 80/95] Provide a way to reset the global feed id back to uninitialized. For use in tests only. --- .../ext/transmodelapi/mapping/TransitIdMapper.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/ext/java/org/opentripplanner/ext/transmodelapi/mapping/TransitIdMapper.java b/src/ext/java/org/opentripplanner/ext/transmodelapi/mapping/TransitIdMapper.java index 42976bd2a2f..615439e7957 100644 --- a/src/ext/java/org/opentripplanner/ext/transmodelapi/mapping/TransitIdMapper.java +++ b/src/ext/java/org/opentripplanner/ext/transmodelapi/mapping/TransitIdMapper.java @@ -98,4 +98,15 @@ public static String setupFixedFeedId(Collection + * For use from tests only. + * + * @see #setupFixedFeedId(Collection) + */ + public static void clearFixedFeedId() { + fixedFeedId = null; + } } From 038d07981fb6dc52d8af7cf1963a63f69605c4f9 Mon Sep 17 00:00:00 2001 From: Johan Torin Date: Tue, 20 Jun 2023 16:11:56 +0200 Subject: [PATCH 81/95] Introduce a transmodel graphql API for specifying pass-through points. * Each pass-through point can consist of multiple stopplaces, with an optional name for logging and debugging. * Each stopplace is translated to a complex object instance from the given id. With contributions from Bartosz Kruba. --- .../mapping/TripRequestMapperTest.java | 101 +++++++++++++++++- src/ext/graphql/transmodelapi/schema.graphql | 16 ++- .../mapping/PassThroughLocationMapper.java | 48 +++++++++ .../mapping/TripRequestMapper.java | 8 ++ .../framework/PassThroughPointInputType.java | 39 +++++++ .../transmodelapi/model/plan/TripQuery.java | 9 ++ .../raptor/api/request/PassThroughPoint.java | 9 +- .../routing/api/request/PassThroughPoint.java | 28 +++++ .../routing/api/request/RouteRequest.java | 11 ++ .../service/DefaultTransitService.java | 5 + .../transit/service/TransitService.java | 3 +- .../api/request/PassThroughPointTest.java | 9 +- .../api/request/PassThroughPointsTest.java | 14 +-- .../moduletests/J01_PassThroughTest.java | 16 +-- .../BitSetPassThroughPointsServiceTest.java | 4 +- 15 files changed, 293 insertions(+), 27 deletions(-) create mode 100644 src/ext/java/org/opentripplanner/ext/transmodelapi/mapping/PassThroughLocationMapper.java create mode 100644 src/ext/java/org/opentripplanner/ext/transmodelapi/model/framework/PassThroughPointInputType.java create mode 100644 src/main/java/org/opentripplanner/routing/api/request/PassThroughPoint.java diff --git a/src/ext-test/java/org/opentripplanner/ext/transmodelapi/mapping/TripRequestMapperTest.java b/src/ext-test/java/org/opentripplanner/ext/transmodelapi/mapping/TripRequestMapperTest.java index 2d8a03c5ce4..1549a1feab9 100644 --- a/src/ext-test/java/org/opentripplanner/ext/transmodelapi/mapping/TripRequestMapperTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/transmodelapi/mapping/TripRequestMapperTest.java @@ -1,9 +1,12 @@ package org.opentripplanner.ext.transmodelapi.mapping; import static graphql.execution.ExecutionContextBuilder.newExecutionContextBuilder; +import static java.util.stream.Collectors.toList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.opentripplanner.framework.time.TimeUtils.time; +import static org.opentripplanner.model.plan.TestItineraryBuilder.newItinerary; import graphql.ExecutionInput; import graphql.execution.ExecutionId; @@ -11,18 +14,26 @@ import graphql.schema.DataFetchingEnvironmentImpl; import io.micrometer.core.instrument.Metrics; import java.time.Duration; -import java.util.Arrays; +import java.time.LocalDate; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.function.Function; import java.util.stream.Stream; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.opentripplanner._support.time.ZoneIds; import org.opentripplanner.ext.transmodelapi.TransmodelRequestContext; +import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; +import org.opentripplanner.model.calendar.CalendarServiceData; +import org.opentripplanner.model.plan.Itinerary; +import org.opentripplanner.model.plan.Leg; +import org.opentripplanner.model.plan.Place; import org.opentripplanner.model.plan.PlanTestConstants; +import org.opentripplanner.model.plan.ScheduledTransitLeg; import org.opentripplanner.raptor.configure.RaptorConfig; +import org.opentripplanner.routing.api.request.PassThroughPoint; import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.routing.api.request.StreetMode; import org.opentripplanner.routing.api.request.preference.StreetPreferences; @@ -36,7 +47,14 @@ import org.opentripplanner.standalone.config.RouterConfig; import org.opentripplanner.standalone.server.DefaultServerRequestContext; import org.opentripplanner.test.support.VariableSource; +import org.opentripplanner.transit.model._data.TransitModelForTest; +import org.opentripplanner.transit.model.framework.Deduplicator; +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.site.StopLocation; import org.opentripplanner.transit.service.DefaultTransitService; +import org.opentripplanner.transit.service.StopModel; import org.opentripplanner.transit.service.TransitModel; public class TripRequestMapperTest implements PlanTestConstants { @@ -44,12 +62,43 @@ public class TripRequestMapperTest implements PlanTestConstants { static final TransmodelRequestContext context; private static final Duration MAX_FLEXIBLE = Duration.ofMinutes(20); + private static final Function STOP_TO_ID = s -> s.getId().toString(); + + private static final Route route1 = TransitModelForTest.route("route1").build(); + private static final Route route2 = TransitModelForTest.route("route2").build(); + + private static final RegularStop stop1 = TransitModelForTest.stopForTest("ST:stop1", 1, 1); + private static final RegularStop stop2 = TransitModelForTest.stopForTest("ST:stop2", 2, 1); + private static final RegularStop stop3 = TransitModelForTest.stopForTest("ST:stop3", 3, 1); + static { var graph = new Graph(); - var transitModel = new TransitModel(); + var itinerary = newItinerary(Place.forStop(stop1), time("11:00")) + .bus(route1, 1, time("11:05"), time("11:20"), Place.forStop(stop2)) + .bus(route2, 2, time("11:20"), time("11:40"), Place.forStop(stop3)) + .build(); + var patterns = itineraryPatterns(itinerary); + var stopModel = StopModel + .of() + .withRegularStop(stop1) + .withRegularStop(stop2) + .withRegularStop(stop3) + .build(); + + var transitModel = new TransitModel(stopModel, new Deduplicator()); transitModel.initTimeZone(ZoneIds.STOCKHOLM); + var calendarServiceData = new CalendarServiceData(); + LocalDate serviceDate = itinerary.startTime().toLocalDate(); + patterns.forEach(pattern -> { + transitModel.addTripPattern(pattern.getId(), pattern); + final int serviceCode = pattern.getScheduledTimetable().getTripTimes(0).getServiceCode(); + transitModel.getServiceCodes().put(pattern.getId(), serviceCode); + calendarServiceData.putServiceDatesForServiceId(pattern.getId(), List.of(serviceDate)); + }); + + transitModel.updateCalendarServiceData(true, calendarServiceData, DataImportIssueStore.NOOP); + transitModel.index(); final var transitService = new DefaultTransitService(transitModel); - var defaultRequest = new RouteRequest(); // Change defaults for FLEXIBLE to a lower value than the default 45m. This should restrict the @@ -243,6 +292,42 @@ public void testBikeTriangleFactorsHasNoEffect(BicycleOptimizeType bot) { assertEquals(TimeSlopeSafetyTriangle.DEFAULT, req1.preferences().bike().optimizeTriangle()); } + @Test + void testPassThroughPoints() { + TransitIdMapper.clearFixedFeedId(); + + final List PTP1 = List.of(stop1, stop2, stop3).stream().map(STOP_TO_ID).toList(); + final List PTP2 = List.of(stop2, stop3, stop1).stream().map(STOP_TO_ID).toList(); + final Map arguments = Map.of( + "passThroughPoints", + List.of(Map.of("name", "PTP1", "placeIds", PTP1), Map.of("placeIds", PTP2, "name", "PTP2")) + ); + + final List points = TripRequestMapper + .createRequest(executionContext(arguments)) + .getPassThroughPoints(); + assertEquals(PTP1, points.get(0).stopLocations().stream().map(STOP_TO_ID).toList()); + assertEquals("PTP1", points.get(0).name()); + assertEquals(PTP2, points.get(1).stopLocations().stream().map(STOP_TO_ID).toList()); + assertEquals("PTP2", points.get(1).name()); + } + + @Test + void testPassThroughPointsNoMatch() { + TransitIdMapper.clearFixedFeedId(); + + final Map arguments = Map.of( + "passThroughPoints", + List.of(Map.of("placeIds", List.of("F:XX:NonExisting"))) + ); + + final RuntimeException ex = assertThrows( + RuntimeException.class, + () -> TripRequestMapper.createRequest(executionContext(arguments)) + ); + assertEquals("No match for F:XX:NonExisting.", ex.getMessage()); + } + private DataFetchingEnvironment executionContext(Map arguments) { ExecutionInput executionInput = ExecutionInput .newExecutionInput() @@ -265,4 +350,14 @@ private DataFetchingEnvironment executionContext(Map arguments) return env; } + + private static List itineraryPatterns(final Itinerary itinerary) { + return itinerary + .getLegs() + .stream() + .filter(Leg::isScheduledTransitLeg) + .map(Leg::asScheduledTransitLeg) + .map(ScheduledTransitLeg::getTripPattern) + .collect(toList()); + } } diff --git a/src/ext/graphql/transmodelapi/schema.graphql b/src/ext/graphql/transmodelapi/schema.graphql index f568c46f7fc..28ca9cd129b 100644 --- a/src/ext/graphql/transmodelapi/schema.graphql +++ b/src/ext/graphql/transmodelapi/schema.graphql @@ -818,6 +818,8 @@ type QueryType { numTripPatterns: Int = 50, "Use the cursor to go to the next \"page\" of itineraries. Copy the cursor from the last response and keep the original request as is. This will enable you to search for itineraries in the next or previous time-window." pageCursor: String, + "The list of points the journey is required to pass through." + passThroughPoints: [PassThroughPoint!], """ Whether non-optimal transit paths at the destination should be returned. Let c be the existing minimum pareto optimal generalized-cost to beat. Then a trip with cost c' is @@ -1935,6 +1937,18 @@ input Modes { transportModes: [TransportModes] } +"Defines one point which the journey must pass through." +input PassThroughPoint { + "Optional name of the pass-through point for debugging and logging. It is not used in routing." + name: String + """ + The list of *stop location ids* which define the pass-through point. At least one id is required. + Quay, StopPlace, multimodal StopPlace, and GroupOfStopPlaces are supported location types. + The journey must pass through at least one of these entities - not all of them. + """ + placeIds: [String!] +} + "A combination of street mode and penalty for time and cost." input PenaltyForStreetMode { """ @@ -2028,7 +2042,7 @@ input ViaLocationInput { maxSlack: Duration = "PT1H" "The minimum time the user wants to stay in the via location before continuing his journey" minSlack: Duration = "PT5M" - "The name of the location. This is pass-through informationand is not used in routing." + "The name of the location. This is pass-through information and is not used in routing." name: String "The id of an element in the OTP model. Currently supports Quay, StopPlace, multimodal StopPlace, and GroupOfStopPlaces." place: String diff --git a/src/ext/java/org/opentripplanner/ext/transmodelapi/mapping/PassThroughLocationMapper.java b/src/ext/java/org/opentripplanner/ext/transmodelapi/mapping/PassThroughLocationMapper.java new file mode 100644 index 00000000000..76bd1464e0f --- /dev/null +++ b/src/ext/java/org/opentripplanner/ext/transmodelapi/mapping/PassThroughLocationMapper.java @@ -0,0 +1,48 @@ +package org.opentripplanner.ext.transmodelapi.mapping; + +import static java.util.stream.Collectors.collectingAndThen; +import static java.util.stream.Collectors.toList; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import org.opentripplanner.routing.api.request.PassThroughPoint; +import org.opentripplanner.transit.service.TransitService; + +class PassThroughLocationMapper { + + static List toLocations( + final TransitService transitService, + final List> passThroughPoints + ) { + return passThroughPoints + .stream() + .map(p -> handlePoint(transitService, p)) + .filter(Objects::nonNull) + .collect(toList()); + // TODO Propagate an error if a stopplace is unknown and fails lookup. + } + + private static PassThroughPoint handlePoint( + final TransitService transitService, + Map map + ) { + final List stops = (List) map.get("placeIds"); + final String name = (String) map.get("name"); + if (stops == null) { + return null; + } + + return stops + .stream() + .map(TransitIdMapper::mapIDToDomain) + .flatMap(id -> { + var stopLocations = transitService.getStopOrChildStops(id); + if (stopLocations.isEmpty()) { + throw new RuntimeException("No match for %s.".formatted(id)); + } + return stopLocations.stream(); + }) + .collect(collectingAndThen(toList(), sls -> new PassThroughPoint(sls, name))); + } +} diff --git a/src/ext/java/org/opentripplanner/ext/transmodelapi/mapping/TripRequestMapper.java b/src/ext/java/org/opentripplanner/ext/transmodelapi/mapping/TripRequestMapper.java index e1c7d750f26..2ee2d5b2eda 100644 --- a/src/ext/java/org/opentripplanner/ext/transmodelapi/mapping/TripRequestMapper.java +++ b/src/ext/java/org/opentripplanner/ext/transmodelapi/mapping/TripRequestMapper.java @@ -17,6 +17,7 @@ import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.standalone.api.OtpServerRequestContext; import org.opentripplanner.transit.model.framework.FeedScopedId; +import org.opentripplanner.transit.service.TransitService; public class TripRequestMapper { @@ -40,6 +41,13 @@ public static RouteRequest createRequest(DataFetchingEnvironment environment) { "to", (Map v) -> request.setTo(GenericLocationMapper.toGenericLocation(v)) ); + final TransitService transitService = context.getTransitService(); + callWith.argument( + "passThroughPoints", + (List> v) -> { + request.setPassThroughPoints(PassThroughLocationMapper.toLocations(transitService, v)); + } + ); callWith.argument( "dateTime", diff --git a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/framework/PassThroughPointInputType.java b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/framework/PassThroughPointInputType.java new file mode 100644 index 00000000000..55873844a5f --- /dev/null +++ b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/framework/PassThroughPointInputType.java @@ -0,0 +1,39 @@ +package org.opentripplanner.ext.transmodelapi.model.framework; + +import graphql.Scalars; +import graphql.schema.GraphQLInputObjectField; +import graphql.schema.GraphQLInputObjectType; +import graphql.schema.GraphQLList; +import graphql.schema.GraphQLNonNull; + +public class PassThroughPointInputType { + + public static final GraphQLInputObjectType INPUT_TYPE = GraphQLInputObjectType + .newInputObject() + .name("PassThroughPoint") + .description("Defines one point which the journey must pass through.") + .field( + GraphQLInputObjectField + .newInputObjectField() + .name("name") + .description( + "Optional name of the pass-through point for debugging and logging. It is not used in routing." + ) + .type(Scalars.GraphQLString) + .build() + ) + .field( + GraphQLInputObjectField + .newInputObjectField() + .name("placeIds") + .description( + """ + The list of *stop location ids* which define the pass-through point. At least one id is required. + Quay, StopPlace, multimodal StopPlace, and GroupOfStopPlaces are supported location types. + The journey must pass through at least one of these entities - not all of them.""" + ) + .type(new GraphQLList(new GraphQLNonNull(Scalars.GraphQLString))) + .build() + ) + .build(); +} diff --git a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/plan/TripQuery.java b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/plan/TripQuery.java index d4c4e424c43..8fa4fa31512 100644 --- a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/plan/TripQuery.java +++ b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/plan/TripQuery.java @@ -15,6 +15,7 @@ import org.opentripplanner.ext.transmodelapi.model.EnumTypes; import org.opentripplanner.ext.transmodelapi.model.TransportModeSlack; import org.opentripplanner.ext.transmodelapi.model.framework.LocationInputType; +import org.opentripplanner.ext.transmodelapi.model.framework.PassThroughPointInputType; import org.opentripplanner.ext.transmodelapi.model.framework.PenaltyForStreetModeType; import org.opentripplanner.ext.transmodelapi.support.GqlUtil; import org.opentripplanner.routing.api.request.preference.RoutingPreferences; @@ -150,6 +151,14 @@ public static GraphQLFieldDefinition create( .type(new GraphQLNonNull(LocationInputType.INPUT_TYPE)) .build() ) + .argument( + GraphQLArgument + .newArgument() + .name("passThroughPoints") + .description("The list of points the journey is required to pass through.") + .type(new GraphQLList(new GraphQLNonNull(PassThroughPointInputType.INPUT_TYPE))) + .build() + ) .argument( GraphQLArgument .newArgument() diff --git a/src/main/java/org/opentripplanner/raptor/api/request/PassThroughPoint.java b/src/main/java/org/opentripplanner/raptor/api/request/PassThroughPoint.java index 5b0b95c7671..18e2966e9c5 100644 --- a/src/main/java/org/opentripplanner/raptor/api/request/PassThroughPoint.java +++ b/src/main/java/org/opentripplanner/raptor/api/request/PassThroughPoint.java @@ -4,6 +4,7 @@ import java.util.BitSet; import java.util.Objects; import java.util.stream.IntStream; +import javax.annotation.Nullable; /** * A collection of stop indexes used to define a pass through-point. @@ -11,13 +12,15 @@ public class PassThroughPoint { private final int[] stops; + private final String name; - public PassThroughPoint(int[] stops) { + public PassThroughPoint(int[] stops, @Nullable String name) { Objects.requireNonNull(stops); if (stops.length == 0) { throw new IllegalArgumentException("At least one stop is required"); } this.stops = Arrays.copyOf(stops, stops.length); + this.name = name; } /** @@ -43,6 +46,8 @@ public int hashCode() { @Override public String toString() { - return "(stops: " + Arrays.toString(stops) + ")"; + return ( + (name == null ? "(" : "(name: '" + name + "', ") + "stops: " + Arrays.toString(stops) + ")" + ); } } diff --git a/src/main/java/org/opentripplanner/routing/api/request/PassThroughPoint.java b/src/main/java/org/opentripplanner/routing/api/request/PassThroughPoint.java new file mode 100644 index 00000000000..fa63c2e5668 --- /dev/null +++ b/src/main/java/org/opentripplanner/routing/api/request/PassThroughPoint.java @@ -0,0 +1,28 @@ +package org.opentripplanner.routing.api.request; + +import java.util.List; +import javax.annotation.Nullable; +import org.opentripplanner.transit.model.site.StopLocation; + +/** + * Defines one pass-through point which the journey must pass through. + */ +public record PassThroughPoint(List stopLocations, @Nullable String name) { + /** + * Get the one or multiple stops of the pass-through point, of which only one is required to be + * passed through. + */ + @Override + public List stopLocations() { + return stopLocations; + } + + /** + * Get an optional name of the pass-through point for debugging and logging. + */ + @Override + @Nullable + public String name() { + return name; + } +} diff --git a/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java b/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java index 357afbe9ff8..4dabf5c0d86 100644 --- a/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java +++ b/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java @@ -8,6 +8,7 @@ import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.function.Consumer; @@ -53,6 +54,8 @@ public class RouteRequest implements Cloneable, Serializable { private GenericLocation to; + private List passThroughPoints = Collections.emptyList(); + private Instant dateTime = Instant.now(); private Duration searchWindow; @@ -295,6 +298,14 @@ public void setTo(GenericLocation to) { this.to = to; } + public List getPassThroughPoints() { + return passThroughPoints; + } + + public void setPassThroughPoints(final List passThroughPoints) { + this.passThroughPoints = passThroughPoints; + } + /** * This is the time/duration in seconds from the earliest-departure-time(EDT) to * latest-departure-time(LDT). In case of a reverse search it will be the time from earliest to diff --git a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java index 31c453056af..41e28e616d8 100644 --- a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java +++ b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java @@ -231,6 +231,11 @@ public StopLocation getStopLocation(FeedScopedId id) { return transitModel.getStopModel().getStopLocation(id); } + @Override + public Collection getStopOrChildStops(FeedScopedId id) { + return transitModel.getStopModel().findStopOrChildStops(id); + } + @Override public Collection listStopLocationGroups() { OTPRequestTimeoutException.checkForTimeout(); diff --git a/src/main/java/org/opentripplanner/transit/service/TransitService.java b/src/main/java/org/opentripplanner/transit/service/TransitService.java index 78c867ac83e..d0664aa292d 100644 --- a/src/main/java/org/opentripplanner/transit/service/TransitService.java +++ b/src/main/java/org/opentripplanner/transit/service/TransitService.java @@ -10,7 +10,6 @@ import java.util.List; import java.util.Optional; import java.util.Set; -import java.util.stream.Stream; import org.locationtech.jts.geom.Envelope; import org.opentripplanner.ext.flex.FlexIndex; import org.opentripplanner.model.FeedInfo; @@ -98,6 +97,8 @@ public interface TransitService { StopLocation getStopLocation(FeedScopedId parseId); + Collection getStopOrChildStops(FeedScopedId id); + Collection listStopLocationGroups(); StopLocationsGroup getStopLocationsGroup(FeedScopedId id); diff --git a/src/test/java/org/opentripplanner/raptor/api/request/PassThroughPointTest.java b/src/test/java/org/opentripplanner/raptor/api/request/PassThroughPointTest.java index 08a38279a4e..0938b58240d 100644 --- a/src/test/java/org/opentripplanner/raptor/api/request/PassThroughPointTest.java +++ b/src/test/java/org/opentripplanner/raptor/api/request/PassThroughPointTest.java @@ -11,7 +11,7 @@ class PassThroughPointTest { private static final int[] STOPS = { 2, 7, 13 }; - private final PassThroughPoint subject = new PassThroughPoint(STOPS); + private final PassThroughPoint subject = new PassThroughPoint(STOPS, "PT1"); @Test void asBitSet() { @@ -29,8 +29,8 @@ void asBitSet() { @Test void testEqualsAndHashCode() { - var same = new PassThroughPoint(STOPS); - var other = new PassThroughPoint(new int[] { 2, 7 }); + var same = new PassThroughPoint(STOPS, "PT1"); + var other = new PassThroughPoint(new int[] { 2, 7 }, "PT2"); assertEquals(subject, subject); assertEquals(same, subject); @@ -42,6 +42,7 @@ void testEqualsAndHashCode() { @Test void testToString() { - assertEquals("(stops: [2, 7, 13])", subject.toString()); + assertEquals("(name: 'PT1', stops: [2, 7, 13])", subject.toString()); + assertEquals("(stops: [2, 7, 13])", new PassThroughPoint(STOPS, null).toString()); } } diff --git a/src/test/java/org/opentripplanner/raptor/api/request/PassThroughPointsTest.java b/src/test/java/org/opentripplanner/raptor/api/request/PassThroughPointsTest.java index 7968017923b..83f4dda4879 100644 --- a/src/test/java/org/opentripplanner/raptor/api/request/PassThroughPointsTest.java +++ b/src/test/java/org/opentripplanner/raptor/api/request/PassThroughPointsTest.java @@ -12,17 +12,17 @@ class PassThroughPointsTest { - private static final int[] STOPS_POINT_1 = { 2, 7, 13 }; - private static final int[] STOPS_POINT_2 = { 12 }; + private static final int[] STOPS_1 = { 2, 7, 13 }; + private static final int[] STOPS_2 = { 12 }; private final PassThroughPoints subject = new PassThroughPoints( - List.of(new PassThroughPoint(STOPS_POINT_1), new PassThroughPoint(STOPS_POINT_2)) + List.of(new PassThroughPoint(STOPS_1, "PT1"), new PassThroughPoint(STOPS_2, "PT2")) ); @Test void stream() { assertEquals( - "(stops: [2, 7, 13]), (stops: [12])", + "(name: 'PT1', stops: [2, 7, 13]), (name: 'PT2', stops: [12])", subject.stream().map(Objects::toString).collect(Collectors.joining(", ")) ); } @@ -36,10 +36,10 @@ void isEmpty() { @Test void testEqualsAndHashCode() { var same = new PassThroughPoints( - List.of(new PassThroughPoint(STOPS_POINT_1), new PassThroughPoint(STOPS_POINT_2)) + List.of(new PassThroughPoint(STOPS_1, "PT1"), new PassThroughPoint(STOPS_2, "PT2")) ); var other = new PassThroughPoints( - List.of(new PassThroughPoint(STOPS_POINT_1), new PassThroughPoint(STOPS_POINT_1)) + List.of(new PassThroughPoint(STOPS_1, "PT1"), new PassThroughPoint(STOPS_1, "PT2")) ); assertEquals(same, subject); assertNotEquals(other, subject); @@ -51,7 +51,7 @@ void testEqualsAndHashCode() { @Test void testToString() { assertEquals( - "PassThroughPoints{points: [(stops: [2, 7, 13]), (stops: [12])]}", + "PassThroughPoints{points: [(name: 'PT1', stops: [2, 7, 13]), (name: 'PT2', stops: [12])]}", subject.toString() ); } diff --git a/src/test/java/org/opentripplanner/raptor/moduletests/J01_PassThroughTest.java b/src/test/java/org/opentripplanner/raptor/moduletests/J01_PassThroughTest.java index 5d9cc03ced4..350fcc72cbd 100644 --- a/src/test/java/org/opentripplanner/raptor/moduletests/J01_PassThroughTest.java +++ b/src/test/java/org/opentripplanner/raptor/moduletests/J01_PassThroughTest.java @@ -91,7 +91,7 @@ public void passThroughPointOnEgress() { .withMultiCriteria(mc -> mc.withPassThroughPoints( // Include desired pass-through point in the request - new PassThroughPoints(List.of(new PassThroughPoint(new int[] { STOP_D }))) + new PassThroughPoints(List.of(new PassThroughPoint(new int[] { STOP_D }, "PT1"))) ) ) .searchParams() @@ -127,7 +127,7 @@ public void passThroughPointOnAccess() { .withMultiCriteria(mc -> // Include desired pass-through point in the request mc.withPassThroughPoints( - new PassThroughPoints(List.of(new PassThroughPoint(new int[] { STOP_A }))) + new PassThroughPoints(List.of(new PassThroughPoint(new int[] { STOP_A }, "PT1"))) ) ) .searchParams() @@ -164,7 +164,7 @@ public void passThroughPointInTheMiddle() { .withMultiCriteria(mc -> // Include desired pass-through point in the request mc.withPassThroughPoints( - new PassThroughPoints(List.of(new PassThroughPoint(new int[] { STOP_C }))) + new PassThroughPoints(List.of(new PassThroughPoint(new int[] { STOP_C }, "PT1"))) ) ) .searchParams() @@ -203,8 +203,8 @@ public void multiplePassThroughPoints() { // Include desired pass-through point in the request new PassThroughPoints( List.of( - new PassThroughPoint(new int[] { STOP_B }), - new PassThroughPoint(new int[] { STOP_D }) + new PassThroughPoint(new int[] { STOP_B }, "PT1"), + new PassThroughPoint(new int[] { STOP_D }, "PT2") ) ) ) @@ -243,8 +243,8 @@ public void passThroughOrder() { // Include desired pass-through point in the request new PassThroughPoints( List.of( - new PassThroughPoint(new int[] { STOP_B }), - new PassThroughPoint(new int[] { STOP_C }) + new PassThroughPoint(new int[] { STOP_B }, "PT1"), + new PassThroughPoint(new int[] { STOP_C }, "PT2") ) ) ) @@ -282,7 +282,7 @@ public void passThroughGroup() { // Include desired pass-through point in the request new PassThroughPoints( // Both STOP_B and STOP_C is a valid pass-through point - List.of(new PassThroughPoint(new int[] { STOP_B, STOP_C })) + List.of(new PassThroughPoint(new int[] { STOP_B, STOP_C }, "PT1")) ) ) ) diff --git a/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/passthrough/BitSetPassThroughPointsServiceTest.java b/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/passthrough/BitSetPassThroughPointsServiceTest.java index 195bb39b453..bfe4853a3d8 100644 --- a/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/passthrough/BitSetPassThroughPointsServiceTest.java +++ b/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/passthrough/BitSetPassThroughPointsServiceTest.java @@ -32,7 +32,9 @@ class BitSetPassThroughPointsServiceTest { private static final int STOP_31 = 6; private static final PassThroughPointsService SUBJECT = BitSetPassThroughPointsService.of( - new PassThroughPoints(List.of(new PassThroughPoint(STOPS_1), new PassThroughPoint(STOPS_2))) + new PassThroughPoints( + List.of(new PassThroughPoint(STOPS_1, "PT1"), new PassThroughPoint(STOPS_2, "PT2")) + ) ); /** * We expect the c2 value at the destination to be the same as the number of pass-through From 82fa760bab29b0007c613f2a2c12a0e7baf0f7aa Mon Sep 17 00:00:00 2001 From: Johan Torin Date: Mon, 21 Aug 2023 16:51:33 +0200 Subject: [PATCH 82/95] Map from values in the RouteRequest to a Raptor PassThroughPoints instance. --- .../transit/mappers/RaptorRequestMapper.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java index 729cf839a27..e938471d808 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java @@ -1,5 +1,8 @@ package org.opentripplanner.routing.algorithm.raptoradapter.transit.mappers; +import static java.util.function.Predicate.not; +import static java.util.stream.Collectors.collectingAndThen; +import static java.util.stream.Collectors.toList; import static org.opentripplanner.raptor.api.request.Optimization.PARALLEL; import io.micrometer.core.instrument.MeterRegistry; @@ -7,16 +10,21 @@ import java.time.Instant; import java.time.ZonedDateTime; import java.util.Collection; +import java.util.List; +import java.util.Optional; import org.opentripplanner.framework.application.OTPFeature; import org.opentripplanner.raptor.api.model.RaptorAccessEgress; import org.opentripplanner.raptor.api.model.RaptorConstants; import org.opentripplanner.raptor.api.request.Optimization; +import org.opentripplanner.raptor.api.request.PassThroughPoint; +import org.opentripplanner.raptor.api.request.PassThroughPoints; import org.opentripplanner.raptor.api.request.RaptorRequest; import org.opentripplanner.raptor.api.request.RaptorRequestBuilder; import org.opentripplanner.raptor.rangeraptor.SystemErrDebugLogger; import org.opentripplanner.routing.algorithm.raptoradapter.router.performance.PerformanceTimersForRaptor; import org.opentripplanner.routing.algorithm.raptoradapter.transit.TripSchedule; import org.opentripplanner.routing.api.request.RouteRequest; +import org.opentripplanner.transit.model.site.StopLocation; public class RaptorRequestMapper { @@ -105,12 +113,25 @@ private RaptorRequest doMap() { if (preferences.transfer().maxAdditionalTransfers() != null) { searchParams.numberOfAdditionalTransfers(preferences.transfer().maxAdditionalTransfers()); } + + final Optional passThroughPoints = request + .getPassThroughPoints() + .stream() + .map(p -> { + final int[] stops = p.stopLocations().stream().mapToInt(StopLocation::getIndex).toArray(); + return new PassThroughPoint(stops, p.name()); + }) + .collect(collectingAndThen(toList(), Optional::ofNullable)) + .filter(not(List::isEmpty)) + .map(PassThroughPoints::new); + builder.withMultiCriteria(mcBuilder -> { preferences .transit() .raptor() .relaxGeneralizedCostAtDestination() .ifPresent(mcBuilder::withRelaxCostAtDestination); + passThroughPoints.ifPresent(pt -> mcBuilder.withPassThroughPoints(pt)); }); for (Optimization optimization : preferences.transit().raptor().optimizations()) { From 8d5490eb4c38ae7624f3e22df3790f159e250dc8 Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Tue, 3 Oct 2023 08:58:25 +0000 Subject: [PATCH 83/95] Add changelog entry for #5320 [ci skip] --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Changelog.md b/docs/Changelog.md index 55e24f2dd68..56d47aae729 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -13,6 +13,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Fix value mapping for bikesAllowed in GTFS GraphQL API [#5368](https://github.com/opentripplanner/OpenTripPlanner/pull/5368) - Apply correct traversal permissions to barrier vertex [#5369](https://github.com/opentripplanner/OpenTripPlanner/pull/5369) - Move GTFS GraphQL API out of the sandbox [#5339](https://github.com/opentripplanner/OpenTripPlanner/pull/5339) +- Transmodel GraphQL API for pass-through searches [#5320](https://github.com/opentripplanner/OpenTripPlanner/pull/5320) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.4.0 (2023-09-13) From dc731924454c7bf0259abdd150134677b5612565 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 4 Oct 2023 00:14:35 +0000 Subject: [PATCH 84/95] Update google.dagger.version to v2.48.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fadcf31e056..28b02afecfd 100644 --- a/pom.xml +++ b/pom.xml @@ -59,7 +59,7 @@ 120 29.2 - 2.48 + 2.48.1 2.15.2 3.1.3 5.10.0 From 36d638646b48675c00f5993ad8abfb04cda7528a Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 4 Oct 2023 22:23:51 +0200 Subject: [PATCH 85/95] Fix IBI fare calculators --- .../ext/fares/impl/AtlantaFareService.java | 6 +++ .../ext/fares/impl/DefaultFareService.java | 44 ++++++++++++------- .../ext/fares/impl/OrcaFareService.java | 7 +++ 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/fares/impl/AtlantaFareService.java b/src/ext/java/org/opentripplanner/ext/fares/impl/AtlantaFareService.java index 5235be01596..dc44a0ca292 100644 --- a/src/ext/java/org/opentripplanner/ext/fares/impl/AtlantaFareService.java +++ b/src/ext/java/org/opentripplanner/ext/fares/impl/AtlantaFareService.java @@ -374,6 +374,12 @@ public AtlantaFareService(Collection regularFareRules) { addFareRules(FareType.electronicSenior, regularFareRules); } + @Nullable + @Override + protected Collection fareRulesForFeed(FareType fareType, String feedId) { + return fareRulesPerType.get(fareType); + } + @Override public boolean populateFare( ItineraryFares fare, diff --git a/src/ext/java/org/opentripplanner/ext/fares/impl/DefaultFareService.java b/src/ext/java/org/opentripplanner/ext/fares/impl/DefaultFareService.java index 744782f7b56..5b947512840 100644 --- a/src/ext/java/org/opentripplanner/ext/fares/impl/DefaultFareService.java +++ b/src/ext/java/org/opentripplanner/ext/fares/impl/DefaultFareService.java @@ -107,34 +107,20 @@ public ItineraryFares calculateFares(Itinerary itinerary) { var fareLegsByFeed = fareLegs .stream() .collect(Collectors.groupingBy(leg -> leg.getAgency().getId().getFeedId())); - var fareRulesByTypeAndFeed = fareRulesPerType - .entrySet() - .stream() - .collect( - Collectors.toMap( - Map.Entry::getKey, - rules -> - rules - .getValue() - .stream() - .collect(Collectors.groupingBy(rule -> rule.getFareAttribute().getId().getFeedId())) - ) - ); ItineraryFares fare = ItineraryFares.empty(); boolean hasFare = false; for (FareType fareType : fareRulesPerType.keySet()) { List components = new ArrayList<>(); List fares = new ArrayList<>(); - ItineraryFares currentFare = ItineraryFares.empty(); boolean legWithoutRulesFound = false; boolean legsWithoutMatchingRulesFound = false; - boolean fareTypeHasFare = false; for (String feedId : fareLegsByFeed.keySet()) { - var fareRules = fareRulesByTypeAndFeed.get(fareType).get(feedId); + ItineraryFares currentFare = ItineraryFares.empty(); + var fareRules = fareRulesForFeed(fareType, feedId); // Get the currency from the first fareAttribute, assuming that all tickets use the same currency. - if (fareRules != null && fareRules.size() > 0) { + if (fareRules != null && !fareRules.isEmpty()) { Currency currency = Currency.getInstance( fareRules.iterator().next().getFareAttribute().getCurrencyType() ); @@ -153,8 +139,14 @@ public ItineraryFares calculateFares(Itinerary itinerary) { components.addAll(currentFare.getComponents(fareType)); fare.addFare(fareType, currentFare.getFare(fareType)); + + currentFare.getLegProducts().entries().forEach(entry ->{ + fare.addFareProduct(entry.getKey(), entry.getValue().product()); + }); + fares.add(currentFare.getFare(fareType)); + // If all the legs are from one feed, consider itinerary products if (fareLegs.equals(fareLegsByFeed.get(feedId))) { fare.addItineraryProducts(currentFare.getItineraryProducts()); @@ -192,6 +184,24 @@ public ItineraryFares calculateFares(Itinerary itinerary) { return hasFare ? fare : null; } + @Nullable + protected Collection fareRulesForFeed(FareType fareType, String feedId) { + var fareRulesByTypeAndFeed = fareRulesPerType + .entrySet() + .stream() + .collect( + Collectors.toMap( + Map.Entry::getKey, + rules -> + rules + .getValue() + .stream() + .collect(Collectors.groupingBy(rule -> rule.getFareAttribute().getId().getFeedId())) + ) + ); + return fareRulesByTypeAndFeed.get(fareType).get(feedId); + } + /** * Builds the Fare object for the given currency, fareType and fareRules. *

diff --git a/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareService.java b/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareService.java index fbad6e290bc..6482424e4a7 100644 --- a/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareService.java +++ b/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareService.java @@ -10,6 +10,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import javax.annotation.Nullable; import org.opentripplanner.ext.fares.model.FareRuleSet; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.model.fare.FareMedium; @@ -547,6 +548,12 @@ private static void addLegFareProduct( } } + @Nullable + @Override + protected Collection fareRulesForFeed(FareType fareType, String feedId) { + return fareRulesPerType.get(fareType); + } + /** * Check if trip falls within the transfer time window. * @param freeTransferStartTime From 32675a0f1585fc6a08dcf2680eeff5390ce90639 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Thu, 5 Oct 2023 10:42:24 +0200 Subject: [PATCH 86/95] Log Java version info at startup --- .../java/org/opentripplanner/standalone/OtpStartupInfo.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/standalone/OtpStartupInfo.java b/src/main/java/org/opentripplanner/standalone/OtpStartupInfo.java index 5722c9cc3a6..0b84c361fb4 100644 --- a/src/main/java/org/opentripplanner/standalone/OtpStartupInfo.java +++ b/src/main/java/org/opentripplanner/standalone/OtpStartupInfo.java @@ -36,7 +36,7 @@ private static String info() { public static void logInfo() { // 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 ({})", projectInfo().getVersionString()); + LOG.info("OTP STARTING UP ({}) using Java {}", projectInfo().getVersionString(), javaVersion()); Runtime .getRuntime() .addShutdownHook( @@ -56,4 +56,8 @@ public static void main(String[] args) { private static String line(String text) { return text + NEW_LINE; } + + private static String javaVersion() { + return System.getProperty("java.version"); + } } From 7373e95c272931c67548be777aeb1ba0ddb6fd50 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 5 Oct 2023 11:04:50 +0200 Subject: [PATCH 87/95] Add more tests --- .../fares/impl/DefaultFareServiceTest.java | 39 +- .../ext/fares/FaresToItineraryMapper.java | 8 +- .../ext/fares/impl/DefaultFareService.java | 19 +- .../model/fare/ItineraryFares.java | 43 +- .../__snapshots__/BikeRentalSnapshotTest.snap | 468 +++++++----------- .../__snapshots__/ElevationSnapshotTest.snap | 468 +++++++----------- .../__snapshots__/TransitSnapshotTest.snap | 450 ++++++----------- .../apis/gtfs/expectations/plan-fares.json | 34 +- 8 files changed, 641 insertions(+), 888 deletions(-) diff --git a/src/ext-test/java/org/opentripplanner/ext/fares/impl/DefaultFareServiceTest.java b/src/ext-test/java/org/opentripplanner/ext/fares/impl/DefaultFareServiceTest.java index 779ea7bd4b7..15721c97f23 100644 --- a/src/ext-test/java/org/opentripplanner/ext/fares/impl/DefaultFareServiceTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/fares/impl/DefaultFareServiceTest.java @@ -1,8 +1,10 @@ package org.opentripplanner.ext.fares.impl; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.opentripplanner.ext.fares.impl.FareModelForTest.AIRPORT_STOP; import static org.opentripplanner.ext.fares.impl.FareModelForTest.AIRPORT_TO_CITY_CENTER_SET; import static org.opentripplanner.ext.fares.impl.FareModelForTest.CITY_CENTER_A_STOP; @@ -25,6 +27,9 @@ class DefaultFareServiceTest implements PlanTestConstants { + private static final Money TEN_DOLLARS = Money.usDollars(10); + private static final Money TWENTY_DOLLARS = Money.usDollars(20); + @Test void noRules() { var service = new DefaultFareService(); @@ -46,7 +51,11 @@ void simpleZoneBasedFare() { var price = fare.getFare(FareType.regular); - assertEquals(Money.usDollars(10), price); + assertEquals(TEN_DOLLARS, price); + + var fp = fare.getItineraryProducts().get(0); + assertEquals(TEN_DOLLARS, fp.price()); + assertEquals("F:regular", fp.id().toString()); } @Test @@ -73,7 +82,24 @@ void shouldNotCombineInterlinedLegs() { var price = fare.getFare(FareType.regular); - assertEquals(Money.usDollars(20), price); + assertEquals(TWENTY_DOLLARS, price); + + assertTrue(fare.getLegProducts().isEmpty()); + + var legProductsFromComponents = fare.legProductsFromComponents(); + + var firstLeg = itin.getLegs().get(0); + var products = List.copyOf(legProductsFromComponents.get(firstLeg)); + + assertEquals(TEN_DOLLARS, products.get(0).product().price()); + + var secondLeg = itin.getLegs().get(1); + products = List.copyOf(legProductsFromComponents.get(secondLeg)); + assertEquals(TEN_DOLLARS, products.get(0).product().price()); + + assertFalse(fare.getItineraryProducts().isEmpty()); + assertEquals(1, fare.getItineraryProducts().size()); + assertEquals(TWENTY_DOLLARS, fare.getItineraryProducts().get(0).price()); } @Test @@ -97,10 +123,13 @@ void unknownLeg() { var component = components.get(0); assertEquals(AIRPORT_TO_CITY_CENTER_SET.getFareAttribute().getId(), component.fareId()); - assertEquals(Money.usDollars(10), component.price()); + assertEquals(TEN_DOLLARS, component.price()); var firstBusLeg = itin.firstTransitLeg().get(); assertEquals(List.of(firstBusLeg), component.legs()); + + var legProductsFromComponent = fare.legProductsFromComponents(); + assertEquals(1, legProductsFromComponent.size()); } @Test @@ -127,7 +156,7 @@ void multipleFeeds() { resultComponents ); - assertEquals(Money.usDollars(20), resultPrice); + assertEquals(TWENTY_DOLLARS, resultPrice); } @Test @@ -155,7 +184,7 @@ void multipleFeedsWithTransfersWithinFeed() { resultComponents ); - assertEquals(Money.usDollars(20), resultPrice); + assertEquals(TWENTY_DOLLARS, resultPrice); } @Test diff --git a/src/ext/java/org/opentripplanner/ext/fares/FaresToItineraryMapper.java b/src/ext/java/org/opentripplanner/ext/fares/FaresToItineraryMapper.java index 3dc64cb1587..75db3acf3a0 100644 --- a/src/ext/java/org/opentripplanner/ext/fares/FaresToItineraryMapper.java +++ b/src/ext/java/org/opentripplanner/ext/fares/FaresToItineraryMapper.java @@ -1,9 +1,11 @@ package org.opentripplanner.ext.fares; +import com.google.common.collect.Multimap; import org.opentripplanner.framework.collection.ListUtils; import org.opentripplanner.model.fare.FareProductUse; import org.opentripplanner.model.fare.ItineraryFares; import org.opentripplanner.model.plan.Itinerary; +import org.opentripplanner.model.plan.Leg; import org.opentripplanner.model.plan.ScheduledTransitLeg; /** @@ -21,13 +23,17 @@ public static void addFaresToLegs(ItineraryFares fares, Itinerary i) { }) .toList(); + final Multimap legProductsFromComponents = fares.legProductsFromComponents(); + i .getLegs() .stream() .filter(ScheduledTransitLeg.class::isInstance) .forEach(l -> { var legInstances = fares.getLegProducts().get(l); - l.setFareProducts(ListUtils.combine(itineraryInstances, legInstances)); + l.setFareProducts( + ListUtils.combine(itineraryInstances, legInstances, legProductsFromComponents.get(l)) + ); }); } } diff --git a/src/ext/java/org/opentripplanner/ext/fares/impl/DefaultFareService.java b/src/ext/java/org/opentripplanner/ext/fares/impl/DefaultFareService.java index 5b947512840..c3fd58af371 100644 --- a/src/ext/java/org/opentripplanner/ext/fares/impl/DefaultFareService.java +++ b/src/ext/java/org/opentripplanner/ext/fares/impl/DefaultFareService.java @@ -17,6 +17,7 @@ import org.opentripplanner.ext.fares.model.FareAttribute; import org.opentripplanner.ext.fares.model.FareRuleSet; import org.opentripplanner.ext.flex.FlexibleTransitLeg; +import org.opentripplanner.model.fare.FareProduct; import org.opentripplanner.model.fare.ItineraryFares; import org.opentripplanner.model.plan.Itinerary; import org.opentripplanner.model.plan.Leg; @@ -140,16 +141,24 @@ public ItineraryFares calculateFares(Itinerary itinerary) { components.addAll(currentFare.getComponents(fareType)); fare.addFare(fareType, currentFare.getFare(fareType)); - currentFare.getLegProducts().entries().forEach(entry ->{ - fare.addFareProduct(entry.getKey(), entry.getValue().product()); - }); + currentFare + .getLegProducts() + .entries() + .forEach(entry -> fare.addFareProduct(entry.getKey(), entry.getValue().product())); fares.add(currentFare.getFare(fareType)); - // If all the legs are from one feed, consider itinerary products if (fareLegs.equals(fareLegsByFeed.get(feedId))) { - fare.addItineraryProducts(currentFare.getItineraryProducts()); + currentFare + .getFareTypes() + .forEach(type -> { + var money = currentFare.getFare(type); + var fareProduct = FareProduct + .of(new FeedScopedId(feedId, type.name()), type.name(), money) + .build(); + fare.addItineraryProducts(List.of(fareProduct)); + }); } } else { legWithoutRulesFound = true; diff --git a/src/main/java/org/opentripplanner/model/fare/ItineraryFares.java b/src/main/java/org/opentripplanner/model/fare/ItineraryFares.java index bb71db1ecae..b3e3488b565 100644 --- a/src/main/java/org/opentripplanner/model/fare/ItineraryFares.java +++ b/src/main/java/org/opentripplanner/model/fare/ItineraryFares.java @@ -1,5 +1,6 @@ package org.opentripplanner.model.fare; +import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Multimap; @@ -10,6 +11,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.framework.lang.Sandbox; import org.opentripplanner.framework.tostring.ToStringBuilder; @@ -102,24 +104,6 @@ public void addFare(FareType fareType, Money money) { @Deprecated public void addFareComponent(FareType fareType, List components) { this.components.replaceValues(fareType, components); - - for (var c : components) { - var firstLegStartTime = c.legs().get(0).getStartTime(); - for (var leg : c.legs()) { - final FareProduct fareProduct = new FareProduct( - c.fareId(), - fareType.name(), - c.price(), - null, - null, - null - ); - legProducts.put( - leg, - new FareProductUse(fareProduct.uniqueInstanceId(firstLegStartTime), fareProduct) - ); - } - } } /** @@ -203,4 +187,27 @@ public void addFareProduct(Leg leg, FareProduct fareProduct) { public void addFareProduct(Leg leg, Collection fareProduct) { fareProduct.forEach(fp -> addFareProduct(leg, fp)); } + + @Nonnull + public Multimap legProductsFromComponents() { + Multimap legProductsFromComponents = HashMultimap.create(); + for (var type : getFareTypes()) { + var components = getComponents(type); + + for (var c : components) { + var firstLegStartTime = c.legs().get(0).getStartTime(); + for (var leg : c.legs()) { + final FareProduct fareProduct = FareProduct + .of(c.fareId(), type.name(), c.price()) + .build(); + + legProductsFromComponents.put( + leg, + new FareProductUse(fareProduct.uniqueInstanceId(firstLegStartTime), fareProduct) + ); + } + } + } + return legProductsFromComponents; + } } diff --git a/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/BikeRentalSnapshotTest.snap b/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/BikeRentalSnapshotTest.snap index 8720de2a5a1..d9695705c71 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/BikeRentalSnapshotTest.snap +++ b/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/BikeRentalSnapshotTest.snap @@ -7,6 +7,21 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.accessBikeR "elevationLost": 0.0, "endTime": "2009-10-21T23:32:24.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -42,29 +57,7 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.accessBikeR "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 3 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 2209, "legs": [ @@ -502,6 +495,21 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.accessBikeR "elevationLost": 0.0, "endTime": "2009-10-21T23:38:09.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -537,29 +545,7 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.accessBikeR "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 1 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 2539, "legs": [ @@ -955,6 +941,21 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.accessBikeR "elevationLost": 0.0, "endTime": "2009-10-21T23:40:10.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -990,29 +991,7 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.accessBikeR "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 1 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 1805, "legs": [ @@ -1304,6 +1283,21 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.accessBikeR "elevationLost": 0.0, "endTime": "2009-10-21T23:45:24.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -1339,29 +1333,7 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.accessBikeR "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 3 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 2209, "legs": [ @@ -1799,6 +1771,21 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.accessBikeR "elevationLost": 0.0, "endTime": "2009-10-21T23:54:24.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -1834,29 +1821,7 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.accessBikeR "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 3 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 2209, "legs": [ @@ -2294,6 +2259,21 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.accessBikeR "elevationLost": 0.0, "endTime": "2009-10-21T23:56:10.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -2329,29 +2309,7 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.accessBikeR "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 1 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 1805, "legs": [ @@ -3318,6 +3276,21 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.egressBikeR "elevationLost": 0.0, "endTime": "2009-10-21T23:28:51.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -3353,29 +3326,7 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.egressBikeR "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 1 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 2119, "legs": [ @@ -3865,6 +3816,21 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.egressBikeR "elevationLost": 0.0, "endTime": "2009-10-21T23:35:24.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -3900,29 +3866,7 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.egressBikeR "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 1 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 2503, "legs": [ @@ -4292,6 +4236,21 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.egressBikeR "elevationLost": 0.0, "endTime": "2009-10-21T23:37:21.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -4327,29 +4286,7 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.egressBikeR "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 1 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 2351, "legs": [ @@ -4693,6 +4630,21 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.egressBikeR "elevationLost": 0.0, "endTime": "2009-10-21T23:43:51.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -4728,29 +4680,7 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.egressBikeR "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 1 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 2119, "legs": [ @@ -5240,6 +5170,21 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.egressBikeR "elevationLost": 0.0, "endTime": "2009-10-21T23:51:24.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -5275,29 +5220,7 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.egressBikeR "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 1 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 2563, "legs": [ @@ -5667,6 +5590,21 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.egressBikeR "elevationLost": 0.0, "endTime": "2009-10-21T23:56:21.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -5702,29 +5640,7 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.egressBikeR "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 1 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 2351, "legs": [ diff --git a/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/ElevationSnapshotTest.snap b/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/ElevationSnapshotTest.snap index 0b4228c8829..9713429109d 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/ElevationSnapshotTest.snap +++ b/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/ElevationSnapshotTest.snap @@ -7,6 +7,21 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.accessBikeRe "elevationLost": 16.15, "endTime": "2009-10-21T23:32:25.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -42,29 +57,7 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.accessBikeRe "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 3 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 2282, "legs": [ @@ -506,6 +499,21 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.accessBikeRe "elevationLost": 3.93, "endTime": "2009-10-21T23:40:10.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -541,29 +549,7 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.accessBikeRe "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 1 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 1803, "legs": [ @@ -857,6 +843,21 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.accessBikeRe "elevationLost": 13.43, "endTime": "2009-10-21T23:45:25.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -892,29 +893,7 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.accessBikeRe "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 1 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 2235, "legs": [ @@ -1208,6 +1187,21 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.accessBikeRe "elevationLost": 16.15, "endTime": "2009-10-21T23:45:25.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -1243,29 +1237,7 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.accessBikeRe "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 3 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 2282, "legs": [ @@ -1707,6 +1679,21 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.accessBikeRe "elevationLost": 13.43, "endTime": "2009-10-21T23:54:25.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -1742,29 +1729,7 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.accessBikeRe "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 1 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 2235, "legs": [ @@ -2058,6 +2023,21 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.accessBikeRe "elevationLost": 16.15, "endTime": "2009-10-21T23:54:25.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -2093,29 +2073,7 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.accessBikeRe "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 3 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 2282, "legs": [ @@ -3091,6 +3049,21 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.transit=[ "elevationLost": 4.23, "endTime": "2009-10-21T23:31:21.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -3126,29 +3099,7 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.transit=[ "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 1 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 2305, "legs": [ @@ -3494,6 +3445,21 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.transit=[ "elevationLost": 23.58, "endTime": "2009-10-21T23:35:04.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -3529,29 +3495,7 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.transit=[ "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 1 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 2465, "legs": [ @@ -3923,6 +3867,21 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.transit=[ "elevationLost": 1.09, "endTime": "2009-10-21T23:37:44.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -3958,29 +3917,7 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.transit=[ "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 1 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 2396, "legs": [ @@ -4326,6 +4263,21 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.transit=[ "elevationLost": 4.76, "endTime": "2009-10-21T23:46:18.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -4361,29 +4313,7 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.transit=[ "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 1 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 2787, "legs": [ @@ -4807,6 +4737,21 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.transit=[ "elevationLost": 4.23, "endTime": "2009-10-21T23:46:21.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -4842,29 +4787,7 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.transit=[ "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 1 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 2305, "legs": [ @@ -5210,6 +5133,21 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.transit=[ "elevationLost": 23.58, "endTime": "2009-10-21T23:51:04.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -5245,29 +5183,7 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.transit=[ "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 1 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 2525, "legs": [ diff --git a/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/TransitSnapshotTest.snap b/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/TransitSnapshotTest.snap index 50650a89c16..4d991c93163 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/TransitSnapshotTest.snap +++ b/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/TransitSnapshotTest.snap @@ -235,6 +235,21 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "elevationLost": 0.0, "endTime": "2009-11-17T18:38:41.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -270,29 +285,7 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 1 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 4281, "legs": [ @@ -727,6 +720,21 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "elevationLost": 0.0, "endTime": "2009-11-17T18:39:22.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -762,29 +770,7 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 1 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 2315, "legs": [ @@ -1284,6 +1270,21 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "elevationLost": 0.0, "endTime": "2009-11-17T18:53:55.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -1319,29 +1320,7 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 1 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 4295, "legs": [ @@ -1776,6 +1755,21 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "elevationLost": 0.0, "endTime": "2009-11-17T18:55:32.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -1811,29 +1805,7 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 1 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 2385, "legs": [ @@ -2333,6 +2305,21 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "elevationLost": 0.0, "endTime": "2009-11-17T19:08:19.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -2372,49 +2359,7 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 1 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - }, - { - "legIndices": [ - 2 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 3375, "legs": [ @@ -3093,6 +3038,21 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "elevationLost": 0.0, "endTime": "2009-11-17T18:38:40.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -3128,29 +3088,7 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 1 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 2895, "legs": [ @@ -3588,6 +3526,21 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "elevationLost": 0.0, "endTime": "2009-11-17T18:54:10.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -3623,29 +3576,7 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 1 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 2925, "legs": [ @@ -4083,6 +4014,21 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "elevationLost": 0.0, "endTime": "2009-11-17T19:09:40.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -4118,29 +4064,7 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 1 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 2895, "legs": [ @@ -4578,6 +4502,21 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "elevationLost": 0.0, "endTime": "2009-11-17T19:11:51.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -4617,49 +4556,7 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 1 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - }, - { - "legIndices": [ - 2 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 2957, "legs": [ @@ -5117,6 +5014,21 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "elevationLost": 0.0, "endTime": "2009-11-17T19:25:05.000+00:00", "fare": { + "coveringItinerary": [ + { + "amount": { + "cents": 200, + "currency": { + "currency": "USD", + "currencyCode": "USD", + "defaultFractionDigits": 2, + "symbol": "$" + } + }, + "id": "prt:regular", + "name": "regular" + } + ], "details": { "regular": [ { @@ -5156,49 +5068,7 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "symbol": "$" } } - }, - "legProducts": [ - { - "legIndices": [ - 1 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - }, - { - "legIndices": [ - 2 - ], - "products": [ - { - "amount": { - "cents": 200, - "currency": { - "currency": "USD", - "currencyCode": "USD", - "defaultFractionDigits": 2, - "symbol": "$" - } - }, - "id": "prt:8", - "name": "regular" - } - ] - } - ] + } }, "generalizedCost": 3015, "legs": [ diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan-fares.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan-fares.json index cd6ed7970a5..896fb532e92 100644 --- a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan-fares.json +++ b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan-fares.json @@ -68,23 +68,6 @@ } } }, - { - "id" : "6bd27c98-c0ee-3f62-b606-bbacf2e1f41b", - "product" : { - "id" : "F:AB", - "name" : "regular", - "__typename" : "DefaultFareProduct", - "price" : { - "currency" : { - "digits" : 2, - "code" : "EUR" - }, - "amount" : 3.1 - }, - "riderCategory" : null, - "medium" : null - } - }, { "id" : "09bb5f2b-6af9-3355-8b5d-5e93a27ce280", "product" : { @@ -107,6 +90,23 @@ "name" : "TfL Oyster Card" } } + }, + { + "id" : "6bd27c98-c0ee-3f62-b606-bbacf2e1f41b", + "product" : { + "id" : "F:AB", + "name" : "regular", + "__typename" : "DefaultFareProduct", + "price" : { + "currency" : { + "digits" : 2, + "code" : "EUR" + }, + "amount" : 3.1 + }, + "riderCategory" : null, + "medium" : null + } } ] }, From 1b7880b2f0be646668f238e9daec8f3d6a598ac0 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 5 Oct 2023 11:30:17 +0200 Subject: [PATCH 88/95] Add full test of OrcaFareCalculator --- .../ext/fares/impl/OrcaFareServiceTest.java | 105 ++++++++++++++---- 1 file changed, 83 insertions(+), 22 deletions(-) diff --git a/src/ext-test/java/org/opentripplanner/ext/fares/impl/OrcaFareServiceTest.java b/src/ext-test/java/org/opentripplanner/ext/fares/impl/OrcaFareServiceTest.java index 9e8376ec635..385b4e96e2c 100644 --- a/src/ext-test/java/org/opentripplanner/ext/fares/impl/OrcaFareServiceTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/fares/impl/OrcaFareServiceTest.java @@ -1,6 +1,7 @@ package org.opentripplanner.ext.fares.impl; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.opentripplanner.ext.fares.impl.OrcaFareService.COMM_TRANS_AGENCY_ID; import static org.opentripplanner.ext.fares.impl.OrcaFareService.KC_METRO_AGENCY_ID; @@ -19,7 +20,6 @@ import java.util.Arrays; import java.util.Collection; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -37,6 +37,7 @@ import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.model.fare.FareProductUse; import org.opentripplanner.model.fare.ItineraryFares; +import org.opentripplanner.model.plan.Itinerary; import org.opentripplanner.model.plan.Leg; import org.opentripplanner.model.plan.Place; import org.opentripplanner.routing.core.FareType; @@ -55,12 +56,16 @@ public class OrcaFareServiceTest { private static final Money HALF_FERRY_FARE = usDollars(3.05f); private static final Money ORCA_REGULAR_FARE = usDollars(2.50f); private static final Money ORCA_SPECIAL_FARE = usDollars(1.50f); + private static final String FEED_ID = "A"; private static TestOrcaFareService orcaFareService; public static final Money DEFAULT_TEST_RIDE_PRICE = usDollars(3.49f); @BeforeAll public static void setUpClass() { - Map regularFareRules = new HashMap<>(); + Map regularFareRules = Map.of( + new FeedScopedId(FEED_ID, "regular"), + FareModelForTest.INSIDE_CITY_CENTER_SET + ); orcaFareService = new TestOrcaFareService(regularFareRules.values()); } @@ -114,7 +119,7 @@ private static void assertLegFareEquals( * Test to confirm the correct transfer cost per fare type within a single agency. */ @Test - public void calculateFareForSingleAgency() { + void calculateFareForSingleAgency() { List rides = List.of(getLeg(COMM_TRANS_AGENCY_ID, "400", 0)); calculateFare(rides, regular, DEFAULT_TEST_RIDE_PRICE); calculateFare(rides, FareType.senior, TWO_DOLLARS); @@ -130,7 +135,7 @@ public void calculateFareForSingleAgency() { * as the highest fare where Orca can be used. */ @Test - public void calculateFareWithNoFreeTransfer() { + void calculateFareWithNoFreeTransfer() { List rides = List.of( getLeg(KITSAP_TRANSIT_AGENCY_ID, 0), getLeg(WASHINGTON_STATE_FERRIES_AGENCY_ID, 1), @@ -161,7 +166,7 @@ public void calculateFareWithNoFreeTransfer() { * Check to make sure the fare by leg is calculated properly for a trip with two rides. */ @Test - public void calculateFareByLeg() { + void calculateFareByLeg() { List rides = List.of(getLeg(KITSAP_TRANSIT_AGENCY_ID, 0), getLeg(COMM_TRANS_AGENCY_ID, 2)); ItineraryFares fares = new ItineraryFares(); orcaFareService.populateFare(fares, USD, FareType.electronicRegular, rides, null); @@ -177,7 +182,7 @@ public void calculateFareByLeg() { * the new two hour window and will be free. */ @Test - public void calculateFareThatExceedsTwoHourFreeTransferWindow() { + void calculateFareThatExceedsTwoHourFreeTransferWindow() { List rides = List.of( getLeg(KITSAP_TRANSIT_AGENCY_ID, 0), getLeg(KITSAP_TRANSIT_AGENCY_ID, 30), @@ -207,7 +212,7 @@ public void calculateFareThatExceedsTwoHourFreeTransferWindow() { * trip! */ @Test - public void calculateFareThatIncludesNoFreeTransfers() { + void calculateFareThatIncludesNoFreeTransfers() { List rides = List.of( getLeg(KITSAP_TRANSIT_AGENCY_ID, 0), getLeg(WASHINGTON_STATE_FERRIES_AGENCY_ID, 30, "VashonIsland-Fauntelroy"), @@ -245,7 +250,7 @@ public void calculateFareThatIncludesNoFreeTransfers() { * Total trip time is 4h 30m. This is equivalent to three transfer windows and therefore three Orca fare charges. */ @Test - public void calculateFareThatExceedsTwoHourFreeTransferWindowTwice() { + void calculateFareThatExceedsTwoHourFreeTransferWindowTwice() { List rides = List.of( getLeg(KITSAP_TRANSIT_AGENCY_ID, 0), getLeg(KITSAP_TRANSIT_AGENCY_ID, 30), @@ -272,7 +277,7 @@ public void calculateFareThatExceedsTwoHourFreeTransferWindowTwice() { * all subsequent transfers will come under one transfer window and only one Orca discount charge will apply. */ @Test - public void calculateFareThatStartsWithACashFare() { + void calculateFareThatStartsWithACashFare() { List rides = List.of( getLeg(WASHINGTON_STATE_FERRIES_AGENCY_ID, 0), getLeg(KITSAP_TRANSIT_AGENCY_ID, 30), @@ -298,7 +303,7 @@ public void calculateFareThatStartsWithACashFare() { * Single trip with Kitsap transit fast ferry east to confirm correct non Orca fares are applied. */ @Test - public void calculateFareForKitsapFastFerryEastAgency() { + void calculateFareForKitsapFastFerryEastAgency() { List rides = List.of(getLeg(KITSAP_TRANSIT_AGENCY_ID, 0, 4, "Kitsap Fast Ferry", "east")); calculateFare(rides, regular, TWO_DOLLARS); calculateFare(rides, FareType.senior, TWO_DOLLARS); @@ -313,7 +318,7 @@ public void calculateFareForKitsapFastFerryEastAgency() { * Single trip (Point Defiance - Tahlequah) with WSF transit to confirm correct non Orca fares are applied. */ @Test - public void calculateFareForWSFPtToTahlequah() { + void calculateFareForWSFPtToTahlequah() { List rides = List.of( getLeg(WASHINGTON_STATE_FERRIES_AGENCY_ID, 0, "Point Defiance - Tahlequah") ); @@ -330,7 +335,7 @@ public void calculateFareForWSFPtToTahlequah() { * Single trip with Link Light Rail to ensure distance fare is calculated correctly. */ @Test - public void calculateFareForLightRailLeg() { + void calculateFareForLightRailLeg() { List rides = List.of( getLeg(SOUND_TRANSIT_AGENCY_ID, "1-Line", 0, "Roosevelt Station", "Int'l Dist/Chinatown") ); @@ -357,7 +362,7 @@ public void calculateFareForLightRailLeg() { } @Test - public void calculateFareForSounderLeg() { + void calculateFareForSounderLeg() { List rides = List.of( getLeg(SOUND_TRANSIT_AGENCY_ID, "S Line", 0, "King Street Station", "Auburn Station") ); @@ -388,7 +393,7 @@ public void calculateFareForSounderLeg() { * Make sure that we get ST's bus fare and not the contracted agency's fare. */ @Test - public void calculateSoundTransitBusFares() { + void calculateSoundTransitBusFares() { List rides = List.of( getLeg(COMM_TRANS_AGENCY_ID, "512", 0), getLeg(PIERCE_COUNTY_TRANSIT_AGENCY_ID, "594", 120), @@ -418,7 +423,7 @@ public void calculateSoundTransitBusFares() { } @Test - public void calculateCashFreeTransferKCMetro() { + void calculateCashFreeTransferKCMetro() { List rides = List.of( getLeg(KC_METRO_AGENCY_ID, 0), getLeg(KC_METRO_AGENCY_ID, 20), @@ -436,7 +441,7 @@ public void calculateCashFreeTransferKCMetro() { } @Test - public void calculateTransferExtension() { + void calculateTransferExtension() { List rides = List.of( getLeg(SOUND_TRANSIT_AGENCY_ID, "1-Line", 0, "Int'l Dist/Chinatown", "Roosevelt Station"), // 2.50 getLeg(SOUND_TRANSIT_AGENCY_ID, "1-Line", 60, "Roosevelt Station", "Angle Lake Station"), // 3.25, should extend transfer @@ -499,6 +504,37 @@ void nullShortName(FareType type) { assertNotNull(fare.getFare(type)); } + @Test + void fullItinerary() { + var itinerary = createItinerary( + WASHINGTON_STATE_FERRIES_AGENCY_ID, + "1-Line", + 0, + 1, + "route1", + "trip1", + null, + "first stop", + "last stop" + ); + var fares = orcaFareService.calculateFares(itinerary); + assertNotNull(fares); + + assertFalse(fares.getItineraryProducts().isEmpty()); + assertFalse(fares.getLegProducts().isEmpty()); + + var firstLeg = itinerary.getLegs().get(0); + var uses = fares.getLegProducts().get(firstLeg); + assertEquals(7, uses.size()); + + var regular = uses + .stream() + .filter(u -> u.product().category().name().equals("regular")) + .toList() + .get(0); + assertEquals(Money.usDollars(3.49f), regular.product().price()); + } + private static Leg getLeg(String agencyId, long startTimeMins) { return createLeg(agencyId, "-1", 3, startTimeMins, "test", "test", ""); } @@ -581,26 +617,52 @@ private static Leg createLeg( @Nullable String routeLongName, String firstStopName, String lastStopName + ) { + final var itin = createItinerary( + agencyId, + shortName, + transitMode, + startTimeMins, + routeId, + tripId, + routeLongName, + firstStopName, + lastStopName + ); + + return itin.getLegs().get(0); + } + + private static Itinerary createItinerary( + String agencyId, + String shortName, + int transitMode, + long startTimeMins, + String routeId, + String tripId, + @Nullable String routeLongName, + String firstStopName, + String lastStopName ) { Agency agency = Agency - .of(new FeedScopedId("A", agencyId)) + .of(new FeedScopedId(FEED_ID, agencyId)) .withName(agencyId) .withTimezone(ZoneIds.NEW_YORK.getId()) .build(); // Set up stops RegularStop firstStop = RegularStop - .of(new FeedScopedId("A", "1")) + .of(new FeedScopedId(FEED_ID, "1")) .withCoordinate(new WgsCoordinate(1, 1)) .withName(new NonLocalizedString(firstStopName)) .build(); RegularStop lastStop = RegularStop - .of(new FeedScopedId("A", "2")) + .of(new FeedScopedId(FEED_ID, "2")) .withCoordinate(new WgsCoordinate(1, 2)) .withName(new NonLocalizedString(lastStopName)) .build(); - FeedScopedId routeFeedScopeId = new FeedScopedId("A", routeId); + FeedScopedId routeFeedScopeId = new FeedScopedId(FEED_ID, routeId); NonLocalizedString longName = null; if (routeLongName != null) { longName = new NonLocalizedString(routeLongName); @@ -619,8 +681,7 @@ private static Leg createLeg( var itin = newItinerary(Place.forStop(firstStop), start) .transit(route, tripId, start, T11_12, 5, 7, Place.forStop(lastStop), null, null, null) .build(); - - return itin.getLegs().get(0); + return itin; } private static class TestOrcaFareService extends OrcaFareService { From 24ec65c368826b78f8d1f1109431bce88add0657 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 5 Oct 2023 11:46:46 +0200 Subject: [PATCH 89/95] Remove Impl from HslFareService --- .../ext/fares/impl/HSLFareServiceTest.java | 4 ++-- .../{HSLFareServiceImpl.java => HSLFareService.java} | 9 ++------- .../ext/fares/impl/HSLFareServiceFactory.java | 4 ++-- 3 files changed, 6 insertions(+), 11 deletions(-) rename src/ext/java/org/opentripplanner/ext/fares/impl/{HSLFareServiceImpl.java => HSLFareService.java} (96%) diff --git a/src/ext-test/java/org/opentripplanner/ext/fares/impl/HSLFareServiceTest.java b/src/ext-test/java/org/opentripplanner/ext/fares/impl/HSLFareServiceTest.java index d590f43ebe6..0a7ec4754e6 100644 --- a/src/ext-test/java/org/opentripplanner/ext/fares/impl/HSLFareServiceTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/fares/impl/HSLFareServiceTest.java @@ -92,7 +92,7 @@ private static List createTestCases() { float ABCD_PRICE = 5.70f; float D_PRICE = 2.80f; - HSLFareServiceImpl hslFareService = new HSLFareServiceImpl(); + HSLFareService hslFareService = new HSLFareService(); int fiveMinutes = 60 * 5; // Fare attributes @@ -446,7 +446,7 @@ void unknownFare() { FareRuleSet ruleSetAB = new FareRuleSet(fareAttributeAB); - var service = new HSLFareServiceImpl(); + var service = new HSLFareService(); service.addFareRules(FareType.regular, List.of(ruleSetAB)); // outside HSL's fare zones, should return null diff --git a/src/ext/java/org/opentripplanner/ext/fares/impl/HSLFareServiceImpl.java b/src/ext/java/org/opentripplanner/ext/fares/impl/HSLFareService.java similarity index 96% rename from src/ext/java/org/opentripplanner/ext/fares/impl/HSLFareServiceImpl.java rename to src/ext/java/org/opentripplanner/ext/fares/impl/HSLFareService.java index ba62e899f70..159d3384dc4 100644 --- a/src/ext/java/org/opentripplanner/ext/fares/impl/HSLFareServiceImpl.java +++ b/src/ext/java/org/opentripplanner/ext/fares/impl/HSLFareService.java @@ -3,9 +3,7 @@ import com.google.common.collect.Sets; import java.time.Duration; import java.time.ZonedDateTime; -import java.util.ArrayList; import java.util.Collection; -import java.util.Currency; import java.util.HashSet; import java.util.List; import java.util.Optional; @@ -14,11 +12,8 @@ import org.opentripplanner.ext.fares.model.FareAttribute; import org.opentripplanner.ext.fares.model.FareRuleSet; import org.opentripplanner.ext.fares.model.RouteOriginDestination; -import org.opentripplanner.model.fare.ItineraryFares; -import org.opentripplanner.model.plan.Itinerary; import org.opentripplanner.model.plan.Leg; import org.opentripplanner.model.plan.ScheduledTransitLeg; -import org.opentripplanner.routing.core.FareComponent; import org.opentripplanner.routing.core.FareType; import org.opentripplanner.transit.model.basic.Money; import org.slf4j.Logger; @@ -28,9 +23,9 @@ * This fare service module handles single feed HSL ticket pricing logic. */ -public class HSLFareServiceImpl extends DefaultFareService { +public class HSLFareService extends DefaultFareService { - private static final Logger LOG = LoggerFactory.getLogger(HSLFareServiceImpl.class); + private static final Logger LOG = LoggerFactory.getLogger(HSLFareService.class); // this is not Float.MAX_VALUE to avoid overflow which would then make debugging harder public static final Money MAX_PRICE = Money.euros(999999f); diff --git a/src/ext/java/org/opentripplanner/ext/fares/impl/HSLFareServiceFactory.java b/src/ext/java/org/opentripplanner/ext/fares/impl/HSLFareServiceFactory.java index 81d53291b1e..6a87f36f386 100644 --- a/src/ext/java/org/opentripplanner/ext/fares/impl/HSLFareServiceFactory.java +++ b/src/ext/java/org/opentripplanner/ext/fares/impl/HSLFareServiceFactory.java @@ -14,7 +14,7 @@ public class HSLFareServiceFactory extends DefaultFareServiceFactory { - private static final Logger LOG = LoggerFactory.getLogger(HSLFareServiceImpl.class); + private static final Logger LOG = LoggerFactory.getLogger(HSLFareService.class); @Override protected void fillFareRules( @@ -76,7 +76,7 @@ protected void fillFareRules( } public FareService makeFareService() { - HSLFareServiceImpl fareService = new HSLFareServiceImpl(); + HSLFareService fareService = new HSLFareService(); fareService.addFareRules(FareType.regular, regularFareRules.values()); if (LOG.isDebugEnabled()) { for (FareRuleSet ruleSet : regularFareRules.values()) { From 0df8eefdf640e55e1268ba06d66928088b915bcf Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 5 Oct 2023 12:18:16 +0200 Subject: [PATCH 90/95] Add full tests for AtlantaFare calculator --- .../ext/fares/FaresFilterTest.java | 2 +- .../fares/impl/AtlantaFareServiceTest.java | 51 ++++++++++++++----- .../ext/fares/FaresToItineraryMapper.java | 2 +- .../apis/gtfs/expectations/plan-fares.json | 34 ++++++------- 4 files changed, 56 insertions(+), 33 deletions(-) diff --git a/src/ext-test/java/org/opentripplanner/ext/fares/FaresFilterTest.java b/src/ext-test/java/org/opentripplanner/ext/fares/FaresFilterTest.java index ea0b0020e5c..2e47c486925 100644 --- a/src/ext-test/java/org/opentripplanner/ext/fares/FaresFilterTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/fares/FaresFilterTest.java @@ -20,7 +20,7 @@ public class FaresFilterTest implements PlanTestConstants { @Test - public void shouldAddFare() { + void shouldAddFare() { final int ID = 1; Itinerary i1 = newItinerary(A, 0) diff --git a/src/ext-test/java/org/opentripplanner/ext/fares/impl/AtlantaFareServiceTest.java b/src/ext-test/java/org/opentripplanner/ext/fares/impl/AtlantaFareServiceTest.java index 0938d0572ef..9b02e9d0dd7 100644 --- a/src/ext-test/java/org/opentripplanner/ext/fares/impl/AtlantaFareServiceTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/fares/impl/AtlantaFareServiceTest.java @@ -1,6 +1,9 @@ package org.opentripplanner.ext.fares.impl; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.opentripplanner.ext.fares.impl.AtlantaFareService.COBB_AGENCY_ID; import static org.opentripplanner.ext.fares.impl.AtlantaFareService.GCT_AGENCY_ID; import static org.opentripplanner.ext.fares.impl.AtlantaFareService.MARTA_AGENCY_ID; @@ -10,7 +13,6 @@ import static org.opentripplanner.transit.model.basic.Money.usDollars; import java.util.Collection; -import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.BeforeAll; @@ -20,6 +22,7 @@ import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.model.fare.ItineraryFares; +import org.opentripplanner.model.plan.Itinerary; import org.opentripplanner.model.plan.Leg; import org.opentripplanner.model.plan.Place; import org.opentripplanner.model.plan.PlanTestConstants; @@ -34,16 +37,20 @@ public class AtlantaFareServiceTest implements PlanTestConstants { public static final Money DEFAULT_TEST_RIDE_PRICE = usDollars(3.49f); + private static final String FEED_ID = "A"; private static AtlantaFareService atlFareService; @BeforeAll public static void setUpClass() { - Map regularFareRules = new HashMap<>(); + Map regularFareRules = Map.of( + new FeedScopedId(FEED_ID, "regular"), + FareModelForTest.INSIDE_CITY_CENTER_SET + ); atlFareService = new TestAtlantaFareService(regularFareRules.values()); } @Test - public void fromMartaTransfers() { + void fromMartaTransfers() { List rides = List.of(getLeg(MARTA_AGENCY_ID, 0), getLeg(XPRESS_AGENCY_ID, 1)); calculateFare(rides, DEFAULT_TEST_RIDE_PRICE); @@ -69,7 +76,7 @@ void nullShortName() { } @Test - public void fromCobbTransfers() { + void fromCobbTransfers() { List rides = List.of(getLeg(COBB_AGENCY_ID, 0), getLeg(MARTA_AGENCY_ID, 1)); calculateFare(rides, DEFAULT_TEST_RIDE_PRICE); @@ -98,13 +105,13 @@ public void fromCobbTransfers() { } @Test - public void fromGctTransfers() { + void fromGctTransfers() { List rides = List.of(getLeg(GCT_AGENCY_ID, 0), getLeg(MARTA_AGENCY_ID, 1)); calculateFare(rides, DEFAULT_TEST_RIDE_PRICE); } @Test - public void tooManyLegs() { + void tooManyLegs() { List rides = List.of( getLeg(MARTA_AGENCY_ID, 0), getLeg(MARTA_AGENCY_ID, 1), @@ -158,7 +165,7 @@ public void tooManyLegs() { } @Test - public void expiredTransfer() { + void expiredTransfer() { List rides = List.of( getLeg(MARTA_AGENCY_ID, 0), getLeg(MARTA_AGENCY_ID, 1), @@ -179,7 +186,7 @@ public void expiredTransfer() { } @Test - public void useStreetcar() { + void useStreetcar() { var STREETCAR_PRICE = DEFAULT_TEST_RIDE_PRICE.minus(usDollars(1)); List rides = List.of( getLeg(MARTA_AGENCY_ID, 0), @@ -199,6 +206,18 @@ public void useStreetcar() { calculateFare(rides, DEFAULT_TEST_RIDE_PRICE.plus(usDollars(1)).plus(STREETCAR_PRICE)); } + @Test + void fullItinerary() { + var itin = createItinerary(MARTA_AGENCY_ID, "1", 0); + var fares = atlFareService.calculateFares(itin); + assertNotNull(fares); + assertTrue(fares.getLegProducts().isEmpty()); + var itineraryProducts = fares.getItineraryProducts(); + assertFalse(itineraryProducts.isEmpty()); + var fp = itineraryProducts.stream().filter(p -> p.name().equals("regular")).findAny().get(); + assertEquals(Money.usDollars(3.49f), fp.price()); + } + /** * These tests are designed to specifically validate ATL fares. Since these fares are hard-coded, * it is acceptable to make direct calls to the ATL fare service with predefined routes. Where the @@ -230,25 +249,30 @@ private static Leg getLeg(String agencyId, String shortName, long startTimeMins) } private static Leg createLeg(String agencyId, String shortName, long startTimeMins) { + final var itin = createItinerary(agencyId, shortName, startTimeMins); + return itin.getLegs().get(0); + } + + private static Itinerary createItinerary(String agencyId, String shortName, long startTimeMins) { Agency agency = Agency - .of(new FeedScopedId("A", agencyId)) + .of(new FeedScopedId(FEED_ID, agencyId)) .withName(agencyId) .withTimezone(ZoneIds.NEW_YORK.getId()) .build(); // Set up stops RegularStop firstStop = RegularStop - .of(new FeedScopedId("A", "1")) + .of(new FeedScopedId(FEED_ID, "1")) .withCoordinate(new WgsCoordinate(1, 1)) .withName(new NonLocalizedString("first stop")) .build(); RegularStop lastStop = RegularStop - .of(new FeedScopedId("A", "2")) + .of(new FeedScopedId(FEED_ID, "2")) .withCoordinate(new WgsCoordinate(1, 2)) .withName(new NonLocalizedString("last stop")) .build(); - FeedScopedId routeFeedScopeId = new FeedScopedId("A", "123"); + FeedScopedId routeFeedScopeId = new FeedScopedId(FEED_ID, "123"); Route route = Route .of(routeFeedScopeId) .withAgency(agency) @@ -261,8 +285,7 @@ private static Leg createLeg(String agencyId, String shortName, long startTimeMi var itin = newItinerary(Place.forStop(firstStop), start) .bus(route, 1, start, T11_12, Place.forStop(lastStop)) .build(); - - return itin.getLegs().get(0); + return itin; } private static class TestAtlantaFareService extends AtlantaFareService { diff --git a/src/ext/java/org/opentripplanner/ext/fares/FaresToItineraryMapper.java b/src/ext/java/org/opentripplanner/ext/fares/FaresToItineraryMapper.java index 75db3acf3a0..c253b730611 100644 --- a/src/ext/java/org/opentripplanner/ext/fares/FaresToItineraryMapper.java +++ b/src/ext/java/org/opentripplanner/ext/fares/FaresToItineraryMapper.java @@ -32,7 +32,7 @@ public static void addFaresToLegs(ItineraryFares fares, Itinerary i) { .forEach(l -> { var legInstances = fares.getLegProducts().get(l); l.setFareProducts( - ListUtils.combine(itineraryInstances, legInstances, legProductsFromComponents.get(l)) + ListUtils.combine(itineraryInstances, legProductsFromComponents.get(l), legInstances) ); }); } diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan-fares.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan-fares.json index 896fb532e92..cd6ed7970a5 100644 --- a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan-fares.json +++ b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan-fares.json @@ -68,6 +68,23 @@ } } }, + { + "id" : "6bd27c98-c0ee-3f62-b606-bbacf2e1f41b", + "product" : { + "id" : "F:AB", + "name" : "regular", + "__typename" : "DefaultFareProduct", + "price" : { + "currency" : { + "digits" : 2, + "code" : "EUR" + }, + "amount" : 3.1 + }, + "riderCategory" : null, + "medium" : null + } + }, { "id" : "09bb5f2b-6af9-3355-8b5d-5e93a27ce280", "product" : { @@ -90,23 +107,6 @@ "name" : "TfL Oyster Card" } } - }, - { - "id" : "6bd27c98-c0ee-3f62-b606-bbacf2e1f41b", - "product" : { - "id" : "F:AB", - "name" : "regular", - "__typename" : "DefaultFareProduct", - "price" : { - "currency" : { - "digits" : 2, - "code" : "EUR" - }, - "amount" : 3.1 - }, - "riderCategory" : null, - "medium" : null - } } ] }, From 5a2f213af6afb69ac562484ed37e3bf25aded0c1 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 5 Oct 2023 13:15:47 +0200 Subject: [PATCH 91/95] Add Javadoc --- .../opentripplanner/ext/fares/impl/DefaultFareService.java | 3 +++ .../java/org/opentripplanner/model/fare/ItineraryFares.java | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/src/ext/java/org/opentripplanner/ext/fares/impl/DefaultFareService.java b/src/ext/java/org/opentripplanner/ext/fares/impl/DefaultFareService.java index c3fd58af371..583eca3f4e8 100644 --- a/src/ext/java/org/opentripplanner/ext/fares/impl/DefaultFareService.java +++ b/src/ext/java/org/opentripplanner/ext/fares/impl/DefaultFareService.java @@ -193,6 +193,9 @@ public ItineraryFares calculateFares(Itinerary itinerary) { return hasFare ? fare : null; } + /** + * For a given fareType and feedId return the applicable fare rule sets. + */ @Nullable protected Collection fareRulesForFeed(FareType fareType, String feedId) { var fareRulesByTypeAndFeed = fareRulesPerType diff --git a/src/main/java/org/opentripplanner/model/fare/ItineraryFares.java b/src/main/java/org/opentripplanner/model/fare/ItineraryFares.java index b3e3488b565..0f9f815efe6 100644 --- a/src/main/java/org/opentripplanner/model/fare/ItineraryFares.java +++ b/src/main/java/org/opentripplanner/model/fare/ItineraryFares.java @@ -188,6 +188,12 @@ public void addFareProduct(Leg leg, Collection fareProduct) { fareProduct.forEach(fp -> addFareProduct(leg, fp)); } + /** + * Convert the fare received via the deprecated {@link FareComponent} to leg products. This + * inverts the relationship: + * - fare component has several legs + * - leg product is a mapping from leg to a list of fare products + */ @Nonnull public Multimap legProductsFromComponents() { Multimap legProductsFromComponents = HashMultimap.create(); From e56a1f8c554b0d5243cec3c11d4a95250051e897 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 5 Oct 2023 14:57:48 +0200 Subject: [PATCH 92/95] Add more test assertions --- .../ext/fares/impl/DefaultFareServiceTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/ext-test/java/org/opentripplanner/ext/fares/impl/DefaultFareServiceTest.java b/src/ext-test/java/org/opentripplanner/ext/fares/impl/DefaultFareServiceTest.java index 15721c97f23..357f427d983 100644 --- a/src/ext-test/java/org/opentripplanner/ext/fares/impl/DefaultFareServiceTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/fares/impl/DefaultFareServiceTest.java @@ -56,6 +56,14 @@ void simpleZoneBasedFare() { var fp = fare.getItineraryProducts().get(0); assertEquals(TEN_DOLLARS, fp.price()); assertEquals("F:regular", fp.id().toString()); + + var lp = fare.legProductsFromComponents(); + assertEquals(1, lp.size()); + var product = lp.values().iterator().next().product(); + assertEquals(TEN_DOLLARS, product.price()); + + // the leg products from the components and the "true" leg products are different collections + assertTrue(fare.getLegProducts().isEmpty()); } @Test From f9ec3030c67df808a2123eeea4b39ee1fff6ba3a Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 5 Oct 2023 18:08:11 +0200 Subject: [PATCH 93/95] Update src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java --- .../algorithm/mapping/RaptorPathToItineraryMapperTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java b/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java index 6fac1fe49dd..2815f129fde 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java @@ -107,8 +107,8 @@ public void createItineraryTestZeroDurationEgress(int LAST_LEG_COST) { } /** - * Create a minimalist path FlexAccess-->Transfer-->Egress (without transit) and check that the 3 legs are properly mapped in the itinerary. - * + * Create a minimalist path FlexAccess-->Transfer-->Egress (without transit) and check that the 3 legs + * are properly mapped in the itinerary. */ @Test public void createItineraryWithOnBoardFlexAccess() { From 3350634ba93dbf4e3c2de92107727094f544e70e Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 6 Oct 2023 11:08:23 +0200 Subject: [PATCH 94/95] Apply review feedback --- .../ext/fares/impl/DefaultFareServiceTest.java | 2 -- .../opentripplanner/ext/fares/impl/AtlantaFareService.java | 4 ++++ .../org/opentripplanner/ext/fares/impl/OrcaFareService.java | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ext-test/java/org/opentripplanner/ext/fares/impl/DefaultFareServiceTest.java b/src/ext-test/java/org/opentripplanner/ext/fares/impl/DefaultFareServiceTest.java index 357f427d983..768e586da31 100644 --- a/src/ext-test/java/org/opentripplanner/ext/fares/impl/DefaultFareServiceTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/fares/impl/DefaultFareServiceTest.java @@ -1,7 +1,6 @@ package org.opentripplanner.ext.fares.impl; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -105,7 +104,6 @@ void shouldNotCombineInterlinedLegs() { products = List.copyOf(legProductsFromComponents.get(secondLeg)); assertEquals(TEN_DOLLARS, products.get(0).product().price()); - assertFalse(fare.getItineraryProducts().isEmpty()); assertEquals(1, fare.getItineraryProducts().size()); assertEquals(TWENTY_DOLLARS, fare.getItineraryProducts().get(0).price()); } diff --git a/src/ext/java/org/opentripplanner/ext/fares/impl/AtlantaFareService.java b/src/ext/java/org/opentripplanner/ext/fares/impl/AtlantaFareService.java index dc44a0ca292..d2d9d3e2a1b 100644 --- a/src/ext/java/org/opentripplanner/ext/fares/impl/AtlantaFareService.java +++ b/src/ext/java/org/opentripplanner/ext/fares/impl/AtlantaFareService.java @@ -374,6 +374,10 @@ public AtlantaFareService(Collection regularFareRules) { addFareRules(FareType.electronicSenior, regularFareRules); } + /** + * In the base class only the rules for a specific feed are selected and then passed to the + * fare engine, however here we want to explicitly compute fares across feed boundaries. + */ @Nullable @Override protected Collection fareRulesForFeed(FareType fareType, String feedId) { diff --git a/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareService.java b/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareService.java index 6482424e4a7..6bc0ad668cf 100644 --- a/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareService.java +++ b/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareService.java @@ -548,6 +548,10 @@ private static void addLegFareProduct( } } + /** + * In the base class only the rules for a specific feed are selected and then passed to the + * fare engine, however here we want to explicitly compute fares across feed boundaries. + */ @Nullable @Override protected Collection fareRulesForFeed(FareType fareType, String feedId) { From b4e4ea68a1ba25f43d20e173dd3eb6164abc3cf8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 8 Oct 2023 23:01:36 +0000 Subject: [PATCH 95/95] Update dependency org.mockito:mockito-core to v5.6.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 628dd75e37c..e06fc8e8b29 100644 --- a/pom.xml +++ b/pom.xml @@ -739,7 +739,7 @@ org.mockito mockito-core - 5.5.0 + 5.6.0 test