paint = new HashMap<>();
+
+ public static LayerStyleBuilder ofId(String id) {
+ return new LayerStyleBuilder(id);
+ }
+
+ public LayerStyleBuilder vectorSourceLayer(VectorSourceLayer source) {
+ source(source.vectorSource());
+ return sourceLayer(source.vectorLayer());
+ }
+
+ public enum LayerType {
+ Circle,
+ Raster,
+ }
+
+ private LayerStyleBuilder(String id) {
+ props.put("id", id);
+ }
+
+ public LayerStyleBuilder minZoom(int i) {
+ props.put("minzoom", i);
+ return this;
+ }
+
+ public LayerStyleBuilder maxZoom(int i) {
+ props.put("maxzoom", i);
+ return this;
+ }
+
+ /**
+ * Which vector tile source this should apply to.
+ */
+ public LayerStyleBuilder source(TileSource source) {
+ props.put("source", source.id());
+ return this;
+ }
+
+ /**
+ * For vector tile sources, specify which source layer in the tile the styles should apply to.
+ * There is an unfortunate collision in the name "layer" as it can both refer to a styling layer
+ * and the layer inside the vector tile.
+ */
+ public LayerStyleBuilder sourceLayer(String source) {
+ props.put(SOURCE_LAYER, source);
+ return this;
+ }
+
+ public LayerStyleBuilder typeRaster() {
+ return type(LayerType.Raster);
+ }
+
+ public LayerStyleBuilder typeCircle() {
+ return type(LayerType.Circle);
+ }
+
+ private LayerStyleBuilder type(LayerType type) {
+ props.put(TYPE, type.name().toLowerCase());
+ return this;
+ }
+
+ public LayerStyleBuilder circleColor(String color) {
+ paint.put("circle-color", validateColor(color));
+ return this;
+ }
+
+ public LayerStyleBuilder circleStroke(String color, int width) {
+ paint.put("circle-stroke-color", validateColor(color));
+ paint.put("circle-stroke-width", width);
+ return this;
+ }
+
+ public JsonNode toJson() {
+ validate();
+
+ var copy = new HashMap<>(props);
+ if (!paint.isEmpty()) {
+ copy.put("paint", paint);
+ }
+ return OBJECT_MAPPER.valueToTree(copy);
+ }
+
+ private String validateColor(String color) {
+ if (!color.startsWith("#")) {
+ throw new IllegalArgumentException("Colors must start with '#'");
+ }
+ return color;
+ }
+
+ private void validate() {
+ Stream
+ .of(TYPE)
+ .forEach(p -> Objects.requireNonNull(props.get(p), "%s must be set".formatted(p)));
+ }
+}
diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerType.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerType.java
new file mode 100644
index 00000000000..f4cb7a636fa
--- /dev/null
+++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerType.java
@@ -0,0 +1,7 @@
+package org.opentripplanner.apis.vectortiles.model;
+
+public enum LayerType {
+ RegularStop,
+ AreaStop,
+ GeofencingZones,
+}
diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleSpec.java
new file mode 100644
index 00000000000..84e19f25364
--- /dev/null
+++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleSpec.java
@@ -0,0 +1,48 @@
+package org.opentripplanner.apis.vectortiles.model;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Represents a style specification for Maplibre/Mapbox vector tile layers.
+ * https://maplibre.org/maplibre-style-spec/root/
+ *
+ * Maplibre uses these to render vector maps in the browser.
+ */
+public final class StyleSpec {
+
+ private final String name;
+ private final List sources;
+ private final List layers;
+
+ public StyleSpec(String name, List sources, List layers) {
+ this.name = name;
+ this.sources = sources;
+ this.layers = layers.stream().map(LayerStyleBuilder::toJson).toList();
+ }
+
+ @JsonSerialize
+ public int version() {
+ return 8;
+ }
+
+ @JsonSerialize
+ public String name() {
+ return name;
+ }
+
+ @JsonSerialize
+ public Map sources() {
+ var output = new HashMap();
+ sources.forEach(s -> output.put(s.id(), s));
+ return output;
+ }
+
+ @JsonSerialize
+ public List layers() {
+ return layers;
+ }
+}
diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java
new file mode 100644
index 00000000000..06af294a4f0
--- /dev/null
+++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java
@@ -0,0 +1,36 @@
+package org.opentripplanner.apis.vectortiles.model;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import java.util.List;
+
+/**
+ * Represent a data source where Maplibre can fetch data for rendering directly in the browser.
+ */
+public sealed interface TileSource {
+ @JsonSerialize
+ String type();
+
+ String id();
+
+ /**
+ * Represents a vector tile source which is rendered into a map in the browser.
+ */
+ record VectorSource(String id, String url) implements TileSource {
+ @Override
+ public String type() {
+ return "vector";
+ }
+ }
+
+ /**
+ * Represents a raster-based source for map tiles. These are used mainly for background
+ * map layers with vector data being rendered on top of it.
+ */
+ record RasterSource(String id, List tiles, int tileSize, String attribution)
+ implements TileSource {
+ @Override
+ public String type() {
+ return "raster";
+ }
+ }
+}
diff --git a/src/main/java/org/opentripplanner/framework/application/OTPFeature.java b/src/main/java/org/opentripplanner/framework/application/OTPFeature.java
index 816279d5a95..4847b204077 100644
--- a/src/main/java/org/opentripplanner/framework/application/OTPFeature.java
+++ b/src/main/java/org/opentripplanner/framework/application/OTPFeature.java
@@ -16,20 +16,23 @@
public enum OTPFeature {
APIBikeRental(true, false, "Enable the bike rental endpoint."),
APIServerInfo(true, false, "Enable the server info endpoint."),
- APIGraphInspectorTile(
- true,
- false,
- "Enable the inspector endpoint for graph information for inspection/debugging purpose."
- ),
APIUpdaterStatus(true, false, "Enable endpoint for graph updaters status."),
ConsiderPatternsForDirectTransfers(
true,
false,
"Enable limiting transfers so that there is only a single transfer to each pattern."
),
- DebugClient(true, false, "Enable the debug web client located at the root of the web server."),
+ DebugUi(
+ true,
+ false,
+ """
+ Enable the debug GraphQL client and web UI and located at the root of the web server as well as the debug map tiles it uses.
+ Be aware that the map tiles are not a stable API and can change without notice.
+ Use the [vector tiles feature if](sandbox/MapboxVectorTilesApi.md) you want a stable map tiles API.
+ """
+ ),
FloatingBike(true, false, "Enable floating bike routing."),
- GtfsGraphQlApi(true, false, "Enable GTFS GraphQL API."),
+ GtfsGraphQlApi(true, false, "Enable the [GTFS GraphQL API](apis/GTFS-GraphQL-API.md)."),
GtfsGraphQlApiRentalStationFuzzyMatching(
false,
false,
@@ -63,7 +66,11 @@ public enum OTPFeature {
false,
"Enforce transfers to happen according to the _transfers.txt_ (GTFS) and Interchanges (NeTEx). Turning this _off_ will increase the routing performance a little."
),
- TransmodelGraphQlApi(true, true, "Enable Transmodel (NeTEx) GraphQL API."),
+ TransmodelGraphQlApi(
+ true,
+ true,
+ "Enable the [Transmodel (NeTEx) GraphQL API](apis/TransmodelApi.md)."
+ ),
/* Sandbox extension features - Must be turned OFF by default */
diff --git a/src/main/java/org/opentripplanner/framework/io/HttpUtils.java b/src/main/java/org/opentripplanner/framework/io/HttpUtils.java
index 3450cf0786c..4981a8ab91b 100644
--- a/src/main/java/org/opentripplanner/framework/io/HttpUtils.java
+++ b/src/main/java/org/opentripplanner/framework/io/HttpUtils.java
@@ -24,16 +24,16 @@ private HttpUtils() {}
public static String getBaseAddress(UriInfo uri, HttpHeaders headers) {
String protocol;
if (headers.getRequestHeader(HEADER_X_FORWARDED_PROTO) != null) {
- protocol = headers.getRequestHeader(HEADER_X_FORWARDED_PROTO).get(0);
+ protocol = headers.getRequestHeader(HEADER_X_FORWARDED_PROTO).getFirst();
} else {
protocol = uri.getRequestUri().getScheme();
}
String host;
if (headers.getRequestHeader(HEADER_X_FORWARDED_HOST) != null) {
- host = headers.getRequestHeader(HEADER_X_FORWARDED_HOST).get(0);
+ host = headers.getRequestHeader(HEADER_X_FORWARDED_HOST).getFirst();
} else if (headers.getRequestHeader(HEADER_HOST) != null) {
- host = headers.getRequestHeader(HEADER_HOST).get(0);
+ host = headers.getRequestHeader(HEADER_HOST).getFirst();
} else {
host = uri.getBaseUri().getHost() + ":" + uri.getBaseUri().getPort();
}
diff --git a/src/main/java/org/opentripplanner/inspector/vector/AreaStopsLayerBuilder.java b/src/main/java/org/opentripplanner/inspector/vector/AreaStopsLayerBuilder.java
deleted file mode 100644
index 5e4539e1f5c..00000000000
--- a/src/main/java/org/opentripplanner/inspector/vector/AreaStopsLayerBuilder.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package org.opentripplanner.inspector.vector;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.function.Function;
-import org.locationtech.jts.geom.Envelope;
-import org.locationtech.jts.geom.Geometry;
-import org.opentripplanner.apis.support.mapping.PropertyMapper;
-import org.opentripplanner.transit.model.site.AreaStop;
-import org.opentripplanner.transit.service.TransitService;
-
-/**
- * A vector tile layer containing all {@link AreaStop}s inside the vector tile bounds.
- */
-public class AreaStopsLayerBuilder extends LayerBuilder {
-
- private static final Map mappers = Map.of(
- MapperType.DebugClient,
- DebugClientAreaStopPropertyMapper::create
- );
- private final Function> findAreaStops;
-
- public AreaStopsLayerBuilder(
- TransitService transitService,
- LayerParameters layerParameters,
- Locale locale
- ) {
- super(
- mappers.get(MapperType.valueOf(layerParameters.mapper())).build(transitService, locale),
- layerParameters.name(),
- layerParameters.expansionFactor()
- );
- this.findAreaStops = transitService::findAreaStops;
- }
-
- @Override
- protected List getGeometries(Envelope query) {
- return findAreaStops
- .apply(query)
- .stream()
- .map(areaStop -> {
- Geometry geometry = areaStop.getGeometry().copy();
-
- geometry.setUserData(areaStop);
-
- return geometry;
- })
- .toList();
- }
-
- enum MapperType {
- DebugClient,
- }
-
- @FunctionalInterface
- private interface MapperFactory {
- PropertyMapper build(TransitService transitService, Locale locale);
- }
-}
diff --git a/src/main/java/org/opentripplanner/inspector/vector/DebugClientAreaStopPropertyMapper.java b/src/main/java/org/opentripplanner/inspector/vector/DebugClientAreaStopPropertyMapper.java
deleted file mode 100644
index 63c58dd9b05..00000000000
--- a/src/main/java/org/opentripplanner/inspector/vector/DebugClientAreaStopPropertyMapper.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package org.opentripplanner.inspector.vector;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Locale;
-import org.opentripplanner.apis.support.mapping.PropertyMapper;
-import org.opentripplanner.framework.i18n.I18NStringMapper;
-import org.opentripplanner.transit.model.site.AreaStop;
-import org.opentripplanner.transit.service.TransitService;
-
-/**
- * A {@link PropertyMapper} for the {@link AreaStopsLayerBuilder} for the OTP debug client.
- */
-public class DebugClientAreaStopPropertyMapper extends PropertyMapper {
-
- private final I18NStringMapper i18NStringMapper;
-
- public DebugClientAreaStopPropertyMapper(TransitService transitService, Locale locale) {
- this.i18NStringMapper = new I18NStringMapper(locale);
- }
-
- public static PropertyMapper create(TransitService transitService, Locale locale) {
- return new DebugClientAreaStopPropertyMapper(transitService, locale);
- }
-
- @Override
- protected Collection map(AreaStop input) {
- return List.of(
- new KeyValue("id", input.getId().toString()),
- new KeyValue("name", i18NStringMapper.mapNonnullToApi(input.getName()))
- );
- }
-}
diff --git a/src/main/java/org/opentripplanner/inspector/vector/KeyValue.java b/src/main/java/org/opentripplanner/inspector/vector/KeyValue.java
index d57afd3429e..6c8b0f3aa4e 100644
--- a/src/main/java/org/opentripplanner/inspector/vector/KeyValue.java
+++ b/src/main/java/org/opentripplanner/inspector/vector/KeyValue.java
@@ -1,3 +1,7 @@
package org.opentripplanner.inspector.vector;
-public record KeyValue(String key, Object value) {}
+public record KeyValue(String key, Object value) {
+ public static KeyValue kv(String key, Object value) {
+ return new KeyValue(key, value);
+ }
+}
diff --git a/src/main/java/org/opentripplanner/inspector/vector/geofencing/GeofencingZonesLayerBuilder.java b/src/main/java/org/opentripplanner/inspector/vector/geofencing/GeofencingZonesLayerBuilder.java
index 1764451cc89..24be8d202a8 100644
--- a/src/main/java/org/opentripplanner/inspector/vector/geofencing/GeofencingZonesLayerBuilder.java
+++ b/src/main/java/org/opentripplanner/inspector/vector/geofencing/GeofencingZonesLayerBuilder.java
@@ -1,10 +1,8 @@
package org.opentripplanner.inspector.vector.geofencing;
import java.util.List;
-import java.util.Map;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Geometry;
-import org.opentripplanner.apis.support.mapping.PropertyMapper;
import org.opentripplanner.framework.geometry.GeometryUtils;
import org.opentripplanner.inspector.vector.LayerBuilder;
import org.opentripplanner.inspector.vector.LayerParameters;
@@ -19,15 +17,11 @@
*/
public class GeofencingZonesLayerBuilder extends LayerBuilder {
- private static final Map mappers = Map.of(
- MapperType.DebugClient,
- transitService -> new GeofencingZonesPropertyMapper()
- );
private final StreetIndex streetIndex;
public GeofencingZonesLayerBuilder(Graph graph, LayerParameters layerParameters) {
super(
- mappers.get(MapperType.valueOf(layerParameters.mapper())).build(graph),
+ new GeofencingZonesPropertyMapper(),
layerParameters.name(),
layerParameters.expansionFactor()
);
@@ -47,13 +41,4 @@ protected List getGeometries(Envelope query) {
})
.toList();
}
-
- enum MapperType {
- DebugClient,
- }
-
- @FunctionalInterface
- private interface MapperFactory {
- PropertyMapper build(Graph transitService);
- }
}
diff --git a/src/main/java/org/opentripplanner/inspector/vector/stop/StopLayerBuilder.java b/src/main/java/org/opentripplanner/inspector/vector/stop/StopLayerBuilder.java
new file mode 100644
index 00000000000..70ce6a58735
--- /dev/null
+++ b/src/main/java/org/opentripplanner/inspector/vector/stop/StopLayerBuilder.java
@@ -0,0 +1,49 @@
+package org.opentripplanner.inspector.vector.stop;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+import java.util.function.Function;
+import org.locationtech.jts.geom.Envelope;
+import org.locationtech.jts.geom.Geometry;
+import org.opentripplanner.inspector.vector.LayerBuilder;
+import org.opentripplanner.inspector.vector.LayerParameters;
+import org.opentripplanner.transit.model.site.AreaStop;
+import org.opentripplanner.transit.model.site.RegularStop;
+import org.opentripplanner.transit.model.site.StopLocation;
+
+/**
+ * A vector tile layer for {@link StopLocation}s inside the vector tile bounds. These can be further
+ * filtered to get only a subset of stop implementations like {@link RegularStop}
+ * or {@link AreaStop}.
+ */
+public class StopLayerBuilder extends LayerBuilder {
+
+ private final Function> findStops;
+
+ public StopLayerBuilder(
+ LayerParameters layerParameters,
+ Locale locale,
+ Function> findStops
+ ) {
+ super(
+ new StopLocationPropertyMapper(locale),
+ layerParameters.name(),
+ layerParameters.expansionFactor()
+ );
+ this.findStops = findStops;
+ }
+
+ @Override
+ protected List getGeometries(Envelope query) {
+ return findStops
+ .apply(query)
+ .stream()
+ .map(stop -> {
+ Geometry geometry = stop.getGeometry().copy();
+ geometry.setUserData(stop);
+ return geometry;
+ })
+ .toList();
+ }
+}
diff --git a/src/main/java/org/opentripplanner/inspector/vector/stop/StopLocationPropertyMapper.java b/src/main/java/org/opentripplanner/inspector/vector/stop/StopLocationPropertyMapper.java
new file mode 100644
index 00000000000..ab9685dd0f6
--- /dev/null
+++ b/src/main/java/org/opentripplanner/inspector/vector/stop/StopLocationPropertyMapper.java
@@ -0,0 +1,32 @@
+package org.opentripplanner.inspector.vector.stop;
+
+import static org.opentripplanner.inspector.vector.KeyValue.kv;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+import org.opentripplanner.apis.support.mapping.PropertyMapper;
+import org.opentripplanner.framework.i18n.I18NStringMapper;
+import org.opentripplanner.inspector.vector.KeyValue;
+import org.opentripplanner.transit.model.site.StopLocation;
+
+/**
+ * A {@link PropertyMapper} for the {@link StopLocationPropertyMapper} for the OTP debug client.
+ */
+public class StopLocationPropertyMapper extends PropertyMapper {
+
+ private final I18NStringMapper i18NStringMapper;
+
+ public StopLocationPropertyMapper(Locale locale) {
+ this.i18NStringMapper = new I18NStringMapper(locale);
+ }
+
+ @Override
+ protected Collection map(StopLocation stop) {
+ return List.of(
+ kv("name", i18NStringMapper.mapToApi(stop.getName())),
+ kv("id", stop.getId().toString()),
+ kv("parentId", stop.isPartOfStation() ? stop.getParentStation().getId().toString() : null)
+ );
+ }
+}
diff --git a/src/main/java/org/opentripplanner/standalone/server/GrizzlyServer.java b/src/main/java/org/opentripplanner/standalone/server/GrizzlyServer.java
index c422e9c24f3..7dc7c87f735 100644
--- a/src/main/java/org/opentripplanner/standalone/server/GrizzlyServer.java
+++ b/src/main/java/org/opentripplanner/standalone/server/GrizzlyServer.java
@@ -102,7 +102,7 @@ public void run() {
httpServer.getServerConfiguration().addHttpHandler(dynamicHandler, "/otp/");
/* 2. A static content handler to serve the client JS apps etc. from the classpath. */
- if (OTPFeature.DebugClient.isOn()) {
+ if (OTPFeature.DebugUi.isOn()) {
CLStaticHttpHandler staticHandler = new CLStaticHttpHandler(
GrizzlyServer.class.getClassLoader(),
"/client/"
diff --git a/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java b/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java
new file mode 100644
index 00000000000..d685e07a2f2
--- /dev/null
+++ b/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java
@@ -0,0 +1,25 @@
+package org.opentripplanner.apis.vectortiles;
+
+import static org.opentripplanner.test.support.JsonAssertions.assertEqualJson;
+
+import org.junit.jupiter.api.Test;
+import org.opentripplanner.apis.vectortiles.DebugStyleSpec.VectorSourceLayer;
+import org.opentripplanner.apis.vectortiles.model.TileSource.VectorSource;
+import org.opentripplanner.framework.json.ObjectMappers;
+import org.opentripplanner.test.support.ResourceLoader;
+
+class DebugStyleSpecTest {
+
+ private final ResourceLoader RES = ResourceLoader.of(this);
+
+ @Test
+ void spec() {
+ var vectorSource = new VectorSource("vectorSource", "https://example.com");
+ var regularStops = new VectorSourceLayer(vectorSource, "regularStops");
+ var spec = DebugStyleSpec.build(vectorSource, regularStops);
+
+ var json = ObjectMappers.ignoringExtraFields().valueToTree(spec);
+ var expectation = RES.fileToString("style.json");
+ assertEqualJson(expectation, json);
+ }
+}
diff --git a/src/test/java/org/opentripplanner/inspector/vector/AreaStopLayerBuilderTest.java b/src/test/java/org/opentripplanner/inspector/vector/stop/AreaStopLayerBuilderTest.java
similarity index 53%
rename from src/test/java/org/opentripplanner/inspector/vector/AreaStopLayerBuilderTest.java
rename to src/test/java/org/opentripplanner/inspector/vector/stop/AreaStopLayerBuilderTest.java
index 09b64adcf75..231f3ddce59 100644
--- a/src/test/java/org/opentripplanner/inspector/vector/AreaStopLayerBuilderTest.java
+++ b/src/test/java/org/opentripplanner/inspector/vector/stop/AreaStopLayerBuilderTest.java
@@ -1,51 +1,37 @@
-package org.opentripplanner.inspector.vector;
+package org.opentripplanner.inspector.vector.stop;
-import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Locale;
import org.junit.jupiter.api.Test;
-import org.locationtech.jts.geom.Coordinate;
-import org.opentripplanner.framework.geometry.GeometryUtils;
+import org.opentripplanner._support.geometry.Polygons;
import org.opentripplanner.framework.i18n.I18NString;
import org.opentripplanner.framework.i18n.NonLocalizedString;
+import org.opentripplanner.inspector.vector.KeyValue;
import org.opentripplanner.transit.model.framework.FeedScopedId;
import org.opentripplanner.transit.model.site.AreaStop;
-import org.opentripplanner.transit.service.DefaultTransitService;
import org.opentripplanner.transit.service.StopModel;
import org.opentripplanner.transit.service.StopModelBuilder;
-import org.opentripplanner.transit.service.TransitModel;
class AreaStopLayerBuilderTest {
- private static final Coordinate[] COORDINATES = {
- new Coordinate(0, 0),
- new Coordinate(0, 1),
- new Coordinate(1, 1),
- new Coordinate(1, 0),
- new Coordinate(0, 0),
- };
private static final FeedScopedId ID = new FeedScopedId("FEED", "ID");
- private static final I18NString NAME = new NonLocalizedString("Test stop");
+ private static final I18NString NAME = I18NString.of("Test stop");
private final StopModelBuilder stopModelBuilder = StopModel.of();
private final AreaStop areaStop = stopModelBuilder
.areaStop(ID)
.withName(NAME)
- .withGeometry(GeometryUtils.getGeometryFactory().createPolygon(COORDINATES))
+ .withGeometry(Polygons.BERLIN)
.build();
@Test
void map() {
- var subject = new DebugClientAreaStopPropertyMapper(
- new DefaultTransitService(new TransitModel()),
- Locale.ENGLISH
- );
+ var subject = new StopLocationPropertyMapper(Locale.ENGLISH);
var properties = subject.map(areaStop);
- assertEquals(2, properties.size());
assertTrue(properties.contains(new KeyValue("id", ID.toString())));
assertTrue(properties.contains(new KeyValue("name", NAME.toString())));
}
diff --git a/src/test/java/org/opentripplanner/test/support/JsonAssertions.java b/src/test/java/org/opentripplanner/test/support/JsonAssertions.java
index f3942c16f3b..2dab1e96190 100644
--- a/src/test/java/org/opentripplanner/test/support/JsonAssertions.java
+++ b/src/test/java/org/opentripplanner/test/support/JsonAssertions.java
@@ -3,6 +3,7 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.opentripplanner.standalone.config.framework.json.JsonSupport;
@@ -15,9 +16,19 @@ public class JsonAssertions {
*/
public static void assertEqualJson(String expected, String actual) {
try {
- var act = MAPPER.readTree(actual);
+ assertEqualJson(expected, MAPPER.readTree(actual));
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * @see JsonAssertions#assertEqualJson(String, String)
+ */
+ public static void assertEqualJson(String expected, JsonNode actual) {
+ try {
var exp = MAPPER.readTree(expected);
- assertEquals(JsonSupport.prettyPrint(exp), JsonSupport.prettyPrint(act));
+ assertEquals(JsonSupport.prettyPrint(exp), JsonSupport.prettyPrint(actual));
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
diff --git a/src/test/java/org/opentripplanner/test/support/ResourceLoader.java b/src/test/java/org/opentripplanner/test/support/ResourceLoader.java
index 38fe02a8c74..5eb51cac55a 100644
--- a/src/test/java/org/opentripplanner/test/support/ResourceLoader.java
+++ b/src/test/java/org/opentripplanner/test/support/ResourceLoader.java
@@ -55,6 +55,17 @@ public File file(String path) {
return file;
}
+ /**
+ * Returns the string content of a file.
+ */
+ public String fileToString(String p) {
+ try {
+ return Files.readString(file(p).toPath());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
/**
* Return a URL for the given resource.
*/
diff --git a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json
new file mode 100644
index 00000000000..f5bb18f6f6a
--- /dev/null
+++ b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json
@@ -0,0 +1,42 @@
+{
+ "name": "OTP Debug Tiles",
+ "sources": {
+ "background": {
+ "id": "background",
+ "tiles": [
+ "https://a.tile.openstreetmap.org/{z}/{x}/{y}.png"
+ ],
+ "tileSize": 256,
+ "attribution" : "© OpenStreetMap Contributors",
+ "type": "raster"
+ },
+ "vectorSource": {
+ "id": "vectorSource",
+ "url": "https://example.com",
+ "type": "vector"
+ }
+ },
+ "layers": [
+ {
+ "id": "background",
+ "source": "background",
+ "type": "raster",
+ "maxzoom": 22,
+ "minzoom": 0
+ },
+ {
+ "maxzoom": 22,
+ "paint": {
+ "circle-stroke-width": 2,
+ "circle-color": "#fcf9fa",
+ "circle-stroke-color": "#140d0e"
+ },
+ "id": "regular-stop",
+ "source": "vectorSource",
+ "source-layer": "regularStops",
+ "type": "circle",
+ "minzoom": 13
+ }
+ ],
+ "version": 8
+}