Skip to content

Commit

Permalink
Merge pull request #6295 from ibi-group/background-tiles
Browse files Browse the repository at this point in the history
Make debug UI background layers configurable with new file `debug-ui-config.json`
  • Loading branch information
leonardehrenfried authored Dec 9, 2024
2 parents 83dc8da + 20696c1 commit 1438c98
Show file tree
Hide file tree
Showing 33 changed files with 598 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.opentripplanner.apis.vectortiles.model.StyleBuilder;
import org.opentripplanner.apis.vectortiles.model.StyleSpec;
Expand All @@ -14,6 +13,7 @@
import org.opentripplanner.apis.vectortiles.model.ZoomDependentNumber;
import org.opentripplanner.apis.vectortiles.model.ZoomDependentNumber.ZoomStop;
import org.opentripplanner.service.vehiclerental.street.StreetVehicleRentalLink;
import org.opentripplanner.standalone.config.debuguiconfig.BackgroundTileLayer;
import org.opentripplanner.street.model.StreetTraversalPermission;
import org.opentripplanner.street.model.edge.AreaEdge;
import org.opentripplanner.street.model.edge.BoardingLocationToStopLink;
Expand All @@ -37,13 +37,25 @@
*/
public class DebugStyleSpec {

private static final TileSource BACKGROUND_SOURCE = new RasterSource(
"background",
private static final TileSource OSM_BACKGROUND = new RasterSource(
"OSM Carto",
List.of("https://a.tile.openstreetmap.org/{z}/{x}/{y}.png"),
19,
256,
"© OpenStreetMap Contributors"
);
private static final TileSource POSITRON_BACKGROUND = new RasterSource(
"Positron",
List.of("https://cartodb-basemaps-a.global.ssl.fastly.net/light_all/{z}/{x}/{y}{ratio}.png"),
19,
256,
"© <a href=\"http://www.openstreetmap.org/copyright\">OpenStreetMap</a>, &copy; <a href=\"https://carto.com/attributions\">CARTO</a>"
);

private static final List<TileSource> BACKGROUND_LAYERS = List.of(
OSM_BACKGROUND,
POSITRON_BACKGROUND
);
private static final String MAGENTA = "#f21d52";
private static final String BRIGHT_GREEN = "#22DD9E";
private static final String DARK_GREEN = "#136b04";
Expand Down Expand Up @@ -92,19 +104,33 @@ static StyleSpec build(
VectorSourceLayer areaStops,
VectorSourceLayer groupStops,
VectorSourceLayer edges,
VectorSourceLayer vertices
VectorSourceLayer vertices,
List<BackgroundTileLayer> extraLayers
) {
var vectorSources = Stream
List<TileSource> vectorSources = Stream
.of(regularStops, edges, vertices)
.map(VectorSourceLayer::vectorSource);
var allSources = Stream
.concat(Stream.of(BACKGROUND_SOURCE), vectorSources)
.collect(Collectors.toSet());
.map(VectorSourceLayer::vectorSource)
.map(TileSource.class::cast)
.toList();

List<TileSource> extraRasterSources = extraLayers
.stream()
.map(l ->
(TileSource) new RasterSource(
l.name(),
List.of(l.templateUrl()),
19,
l.tileSize(),
l.attribution()
)
)
.toList();
var allSources = ListUtils.combine(BACKGROUND_LAYERS, extraRasterSources, vectorSources);
return new StyleSpec(
"OTP Debug Tiles",
allSources,
ListUtils.combine(
List.of(StyleBuilder.ofId("background").typeRaster().source(BACKGROUND_SOURCE).minZoom(0)),
backgroundLayers(extraRasterSources),
wheelchair(edges),
noThruTraffic(edges),
traversalPermissions(edges),
Expand All @@ -115,6 +141,25 @@ static StyleSpec build(
);
}

private static List<StyleBuilder> backgroundLayers(List<TileSource> extraLayers) {
return ListUtils
.combine(BACKGROUND_LAYERS, extraLayers)
.stream()
.map(layer -> {
var builder = StyleBuilder
.ofId(layer.id())
.displayName(layer.name())
.typeRaster()
.source(layer)
.minZoom(0);
if (!layer.equals(OSM_BACKGROUND)) {
builder.intiallyHidden();
}
return builder;
})
.toList();
}

private static List<StyleBuilder> stops(
VectorSourceLayer regularStops,
VectorSourceLayer areaStops,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ public StyleSpec getTileJson(@Context UriInfo uri, @Context HttpHeaders headers)
AREA_STOPS.toVectorSourceLayer(stopsSource),
GROUP_STOPS.toVectorSourceLayer(stopsSource),
EDGES.toVectorSourceLayer(streetSource),
VERTICES.toVectorSourceLayer(streetSource)
VERTICES.toVectorSourceLayer(streetSource),
serverContext.debugUiConfig().additionalBackgroundLayers()
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class StyleBuilder {
private final Map<String, Object> layout = new LinkedHashMap<>();
private final Map<String, Object> metadata = new LinkedHashMap<>();
private final Map<String, Object> line = new LinkedHashMap<>();
private List<? extends Object> filter = List.of();
private List<?> filter = List.of();

public static StyleBuilder ofId(String id) {
return new StyleBuilder(id);
Expand Down Expand Up @@ -120,6 +120,14 @@ public StyleBuilder group(String group) {
return this;
}

/**
* A nice human-readable name for the layer.
*/
public StyleBuilder displayName(String name) {
metadata.put("name", name);
return this;
}

public StyleBuilder lineText(String name) {
layout.put("symbol-placement", "line-center");
layout.put("symbol-spacing", 1000);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.util.List;
import org.opentripplanner.utils.lang.StringUtils;

/**
* Represent a data source where Maplibre can fetch data for rendering directly in the browser.
Expand All @@ -12,6 +13,8 @@ public sealed interface TileSource {

String id();

String name();

/**
* Represents a vector tile source which is rendered into a map in the browser.
*/
Expand All @@ -20,17 +23,32 @@ record VectorSource(String id, String url) implements TileSource {
public String type() {
return "vector";
}

@Override
public String name() {
return id;
}
}

/**
* 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<String> tiles, int maxzoom, int tileSize, String attribution)
record RasterSource(
String name,
List<String> tiles,
int maxzoom,
int tileSize,
String attribution
)
implements TileSource {
@Override
public String type() {
return "raster";
}

public String id() {
return StringUtils.slugify(name);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,18 @@ public class OtpFileNames {
public static final String OTP_CONFIG_FILENAME = "otp-config.json";
public static final String BUILD_CONFIG_FILENAME = "build-config.json";
public static final String ROUTER_CONFIG_FILENAME = "router-config.json";
public static final String DEBUG_UI_CONFIG_FILENAME = "debug-ui-config.json";

/**
* Check if a file is a config file using the configuration file name. This method returns {@code
* true} if the file match {@code (otp|build|router)-config.json}.
* true} if the file match {@code (otp|build|router|debug-ui)-config.json}.
*/
public static boolean isConfigFile(String filename) {
return (
OTP_CONFIG_FILENAME.equals(filename) ||
BUILD_CONFIG_FILENAME.equals(filename) ||
ROUTER_CONFIG_FILENAME.equals(filename)
ROUTER_CONFIG_FILENAME.equals(filename) ||
DEBUG_UI_CONFIG_FILENAME.equals(filename)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.opentripplanner.service.vehicleparking.VehicleParkingService;
import org.opentripplanner.service.vehiclerental.VehicleRentalService;
import org.opentripplanner.service.worldenvelope.WorldEnvelopeService;
import org.opentripplanner.standalone.config.DebugUiConfig;
import org.opentripplanner.standalone.config.routerconfig.VectorTileConfig;
import org.opentripplanner.street.model.edge.Edge;
import org.opentripplanner.street.search.state.State;
Expand Down Expand Up @@ -127,6 +128,8 @@ default GraphFinder graphFinder() {

VectorTileConfig vectorTileConfig();

DebugUiConfig debugUiConfig();

/* Sandbox modules */

@Nullable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,29 @@ public class ConfigModel {
*/
private RouterConfig routerConfig;

public ConfigModel(OtpConfig otpConfig, BuildConfig buildConfig, RouterConfig routerConfig) {
private final DebugUiConfig debugUiConfig;

public ConfigModel(
OtpConfig otpConfig,
BuildConfig buildConfig,
RouterConfig routerConfig,
DebugUiConfig debugUiConfig
) {
this.otpConfig = otpConfig;
this.buildConfig = buildConfig;
this.routerConfig = routerConfig;
this.debugUiConfig = debugUiConfig;

initializeOtpFeatures(otpConfig);
}

public ConfigModel(OtpConfigLoader loader) {
this(loader.loadOtpConfig(), loader.loadBuildConfig(), loader.loadRouterConfig());
this(
loader.loadOtpConfig(),
loader.loadBuildConfig(),
loader.loadRouterConfig(),
loader.loadDebugUiConfig()
);
}

public void updateConfigFromSerializedGraph(BuildConfig buildConfig, RouterConfig routerConfig) {
Expand Down Expand Up @@ -102,6 +115,10 @@ public RouterConfig routerConfig() {
return routerConfig;
}

public DebugUiConfig debugUiConfig() {
return debugUiConfig;
}

public static void initializeOtpFeatures(OtpConfig otpConfig) {
OTPFeature.enableFeatures(otpConfig.otpFeatures);
OTPFeature.logFeatureSetup();
Expand All @@ -117,7 +134,8 @@ public void abortOnUnknownParameters() {
(
otpConfig.hasUnknownParameters() ||
buildConfig.hasUnknownParameters() ||
routerConfig.hasUnknownParameters()
routerConfig.hasUnknownParameters() ||
debugUiConfig.hasUnknownParameters()
)
) {
throw new OtpAppException(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package org.opentripplanner.standalone.config;

import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_7;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.MissingNode;
import java.util.List;
import org.opentripplanner.standalone.config.debuguiconfig.BackgroundTileLayer;
import org.opentripplanner.standalone.config.framework.json.NodeAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* This class is an object representation of the 'debug-ui-config.json'.
*/
public class DebugUiConfig {

private static final Logger LOG = LoggerFactory.getLogger(DebugUiConfig.class);

public static final DebugUiConfig DEFAULT = new DebugUiConfig(
MissingNode.getInstance(),
"DEFAULT",
false
);

/**
* The node adaptor kept for reference and (de)serialization.
*/
private final NodeAdapter root;
private final List<BackgroundTileLayer> additionalBackgroundLayers;

public DebugUiConfig(JsonNode node, String source, boolean logUnusedParams) {
this(new NodeAdapter(node, source), logUnusedParams);
}

/** protected to give unit-test access */
DebugUiConfig(NodeAdapter root, boolean logUnusedParams) {
this.root = root;

this.additionalBackgroundLayers =
root
.of("additionalBackgroundLayers")
.since(V2_7)
.summary("Additional background raster map layers.")
.description(
"""
Add additional background layers that will appear in the Debug UI as one of the choices.
Currently only raster tile layers are supported.
"""
)
.asObjects(
List.of(),
node ->
new BackgroundTileLayer(
node
.of("name")
.since(V2_7)
.summary("Name to appear in the layer selector.")
.asString(),
node
.of("templateUrl")
.since(V2_7)
.summary(
"""
The [Maplibre-compatible template URL](https://maplibre.org/maplibre-native/ios/api/tile-url-templates.html)
for the raster layer, for example `https://examples.com/tiles/{z}/{x}/{y}.png`.
"""
)
.asString(),
node.of("tileSize").since(V2_7).summary("Size of the tile in pixels.").asInt(256),
node
.of("attribution")
.since(V2_7)
.summary("Attribution for the map data.")
.asString("© OpenTripPlanner")
)
);

if (logUnusedParams) {
root.logAllWarnings(LOG::warn);
}
}

public NodeAdapter asNodeAdapter() {
return root;
}

public List<BackgroundTileLayer> additionalBackgroundLayers() {
return additionalBackgroundLayers;
}

/**
* If {@code true} the config is loaded from file, in not the DEFAULT config is used.
*/
public boolean isDefault() {
return root.isEmpty();
}

/**
* Checks if any unknown or invalid parameters were encountered while loading the configuration.
*/
public boolean hasUnknownParameters() {
return root.hasUnknownParameters();
}
}
Loading

0 comments on commit 1438c98

Please sign in to comment.