Skip to content

Commit

Permalink
vector tiles: add no-thru traffic layers
Browse files Browse the repository at this point in the history
+ group no-thru traffic and permission layers by mode
  • Loading branch information
flaktack committed Oct 11, 2024
1 parent b28d4b7 commit 24c6206
Show file tree
Hide file tree
Showing 4 changed files with 569 additions and 251 deletions.
118 changes: 96 additions & 22 deletions src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.opentripplanner.apis.vectortiles;

import static org.opentripplanner.inspector.vector.edge.EdgePropertyMapper.streetPermissionAsString;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
Expand All @@ -13,6 +15,7 @@
import org.opentripplanner.apis.vectortiles.model.ZoomDependentNumber.ZoomStop;
import org.opentripplanner.framework.collection.ListUtils;
import org.opentripplanner.service.vehiclerental.street.StreetVehicleRentalLink;
import org.opentripplanner.service.vehiclerental.street.VehicleRentalPlaceVertex;
import org.opentripplanner.street.model.StreetTraversalPermission;
import org.opentripplanner.street.model.edge.AreaEdge;
import org.opentripplanner.street.model.edge.BoardingLocationToStopLink;
Expand All @@ -30,8 +33,8 @@
import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex;

/**
* A Mapbox/Mapblibre style specification for rendering debug information about transit and
* street data.
* A Mapbox/Mapblibre style specification for rendering debug information about transit and street
* data.
*/
public class DebugStyleSpec {

Expand All @@ -47,13 +50,18 @@ public class DebugStyleSpec {
private static final String DARK_GREEN = "#136b04";
private static final String PURPLE = "#BC55F2";
private static final String BLACK = "#140d0e";
private static final String GRAY = "#DDDDDD";

private static final int MAX_ZOOM = 23;
private static final int LINE_DETAIL_ZOOM = 13;
private static final ZoomDependentNumber LINE_OFFSET = new ZoomDependentNumber(
List.of(new ZoomStop(13, 0.3f), new ZoomStop(MAX_ZOOM, 6))
List.of(new ZoomStop(LINE_DETAIL_ZOOM, 0.4f), new ZoomStop(MAX_ZOOM, 7))
);
private static final ZoomDependentNumber LINE_WIDTH = new ZoomDependentNumber(
List.of(new ZoomStop(13, 0.2f), new ZoomStop(MAX_ZOOM, 8))
List.of(new ZoomStop(LINE_DETAIL_ZOOM, 0.2f), new ZoomStop(MAX_ZOOM, 8))
);
private static final ZoomDependentNumber LINE_HALF_WIDTH = new ZoomDependentNumber(
List.of(new ZoomStop(LINE_DETAIL_ZOOM, 0.1f), new ZoomStop(MAX_ZOOM, 6))
);
private static final ZoomDependentNumber CIRCLE_STROKE = new ZoomDependentNumber(
List.of(new ZoomStop(15, 0.2f), new ZoomStop(MAX_ZOOM, 3))
Expand All @@ -70,7 +78,14 @@ public class DebugStyleSpec {
private static final String EDGES_GROUP = "Edges";
private static final String STOPS_GROUP = "Stops";
private static final String VERTICES_GROUP = "Vertices";
private static final String TRAVERSAL_PERMISSIONS_GROUP = "Traversal permissions";
private static final String PERMISSIONS_GROUP = "Permissions";
private static final String NO_THRU_TRAFFIC_GROUP = "No-thru traffic";

private static final StreetTraversalPermission[] streetModes = new StreetTraversalPermission[] {
StreetTraversalPermission.PEDESTRIAN,
StreetTraversalPermission.BICYCLE,
StreetTraversalPermission.CAR,
};

static StyleSpec build(
VectorSourceLayer regularStops,
Expand All @@ -90,8 +105,9 @@ static StyleSpec build(
allSources,
ListUtils.combine(
List.of(StyleBuilder.ofId("background").typeRaster().source(BACKGROUND_SOURCE).minZoom(0)),
traversalPermissions(edges),
edges(edges),
traversalPermissions(edges),
noThruTraffic(edges),
vertices(vertices),
stops(regularStops, areaStops, groupStops)
)
Expand Down Expand Up @@ -157,6 +173,20 @@ private static List<StyleBuilder> vertices(VectorSourceLayer vertices) {
.minZoom(15)
.maxZoom(MAX_ZOOM)
.intiallyHidden(),
StyleBuilder
.ofId("rental-vertex")
.group(VERTICES_GROUP)
.typeCircle()
.vectorSourceLayer(vertices)
.vertexFilter(VehicleRentalPlaceVertex.class)
.circleStroke(BLACK, CIRCLE_STROKE)
.circleRadius(
new ZoomDependentNumber(List.of(new ZoomStop(13, 1.4f), new ZoomStop(MAX_ZOOM, 10)))
)
.circleColor(MAGENTA)
.minZoom(13)
.maxZoom(MAX_ZOOM)
.intiallyHidden(),
StyleBuilder
.ofId("parking-vertex")
.group(VERTICES_GROUP)
Expand All @@ -181,9 +211,8 @@ private static List<StyleBuilder> edges(VectorSourceLayer edges) {
.group(EDGES_GROUP)
.typeLine()
.vectorSourceLayer(edges)
.lineColor(MAGENTA)
.edgeFilter(EDGES_TO_DISPLAY)
.lineWidth(LINE_WIDTH)
.lineColor(GRAY)
.lineWidth(LINE_HALF_WIDTH)
.lineOffset(LINE_OFFSET)
.minZoom(6)
.maxZoom(MAX_ZOOM)
Expand All @@ -194,7 +223,6 @@ private static List<StyleBuilder> edges(VectorSourceLayer edges) {
.typeSymbol()
.lineText("name")
.vectorSourceLayer(edges)
.edgeFilter(EDGES_TO_DISPLAY)
.minZoom(17)
.maxZoom(MAX_ZOOM)
.intiallyHidden(),
Expand All @@ -203,7 +231,8 @@ private static List<StyleBuilder> edges(VectorSourceLayer edges) {
.group(EDGES_GROUP)
.typeLine()
.vectorSourceLayer(edges)
.lineColor(BRIGHT_GREEN)
.lineColor(GRAY)
.lineOpacity(0.2f)
.edgeFilter(
StreetTransitStopLink.class,
StreetTransitEntranceLink.class,
Expand All @@ -222,39 +251,84 @@ private static List<StyleBuilder> edges(VectorSourceLayer edges) {

private static List<StyleBuilder> traversalPermissions(VectorSourceLayer edges) {
var permissionStyles = Arrays
.stream(StreetTraversalPermission.values())
.map(p ->
.stream(streetModes)
.map(streetTraversalPermission ->
StyleBuilder
.ofId(p.name())
.ofId("permission " + streetTraversalPermission)
.vectorSourceLayer(edges)
.group(TRAVERSAL_PERMISSIONS_GROUP)
.group(PERMISSIONS_GROUP)
.typeLine()
.lineColor(permissionColor(p))
.permissionsFilter(p)
.filterValueInProperty(streetTraversalPermission.name(), "permission")
.lineCap("butt")
.lineColorMatch("permission", permissionColors(), BLACK)
.lineWidth(LINE_WIDTH)
.lineOffset(LINE_OFFSET)
.minZoom(6)
.minZoom(LINE_DETAIL_ZOOM)
.maxZoom(MAX_ZOOM)
.intiallyHidden()
)
.toList();

var textStyle = StyleBuilder
.ofId("permission-text")
.vectorSourceLayer(edges)
.group(TRAVERSAL_PERMISSIONS_GROUP)
.group(PERMISSIONS_GROUP)
.typeSymbol()
.lineText("permission")
.textOffset(1)
.edgeFilter(EDGES_TO_DISPLAY)
.minZoom(17)
.maxZoom(MAX_ZOOM)
.intiallyHidden();
return ListUtils.combine(permissionStyles, List.of(textStyle));

return ListUtils.combine(List.of(textStyle), permissionStyles);
}

private static List<StyleBuilder> noThruTraffic(VectorSourceLayer edges) {
var noThruTrafficStyles = Arrays
.stream(streetModes)
.map(streetTraversalPermission ->
StyleBuilder
.ofId("no-thru-traffic " + streetTraversalPermission)
.vectorSourceLayer(edges)
.group(NO_THRU_TRAFFIC_GROUP)
.typeLine()
.filterValueInProperty(streetTraversalPermission.name(), "noThruTraffic")
.lineCap("butt")
.lineColorMatch("noThruTraffic", permissionColors(), BLACK)
.lineWidth(LINE_WIDTH)
.lineOffset(LINE_OFFSET)
.minZoom(LINE_DETAIL_ZOOM)
.maxZoom(MAX_ZOOM)
.intiallyHidden()
)
.toList();

var textStyle = StyleBuilder
.ofId("no-thru-traffic-text")
.vectorSourceLayer(edges)
.group(NO_THRU_TRAFFIC_GROUP)
.typeSymbol()
.lineText("noThruTraffic")
.textOffset(1)
.edgeFilter(EDGES_TO_DISPLAY)
.minZoom(17)
.maxZoom(MAX_ZOOM)
.intiallyHidden();

return ListUtils.combine(List.of(textStyle), noThruTrafficStyles);
}

private static List<String> permissionColors() {
return Arrays
.stream(StreetTraversalPermission.values())
.flatMap(p -> Stream.of(streetPermissionAsString(p), permissionColors(p)))
.toList();
}

private static String permissionColor(StreetTraversalPermission p) {
private static String permissionColors(StreetTraversalPermission p) {
return switch (p) {
case NONE -> "#000";
case NONE -> BLACK;
case PEDESTRIAN -> "#2ba812";
case BICYCLE, PEDESTRIAN_AND_BICYCLE -> "#10d3b6";
case CAR -> "#f92e13";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -29,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<String> filter = List.of();
private List<? extends Object> filter = List.of();

public static StyleBuilder ofId(String id) {
return new StyleBuilder(id);
Expand Down Expand Up @@ -167,12 +168,42 @@ public StyleBuilder circleRadius(ZoomDependentNumber radius) {
}

// Line styling
public StyleBuilder lineCap(String lineCap) {
layout.put("line-cap", lineCap);
return this;
}

public StyleBuilder lineColor(String color) {
paint.put("line-color", validateColor(color));
return this;
}

public StyleBuilder lineColorMatch(
String propertyName,
Collection<String> values,
String defaultValue
) {
paint.put(
"line-color",
ListUtils.combine(
List.of("match", List.of("get", propertyName)),
(Collection) values,
List.of(defaultValue)
)
);
return this;
}

public StyleBuilder lineOpacity(float lineOpacity) {
paint.put("line-opacity", lineOpacity);
return this;
}

public StyleBuilder lineDasharray(float... dashArray) {
paint.put("line-dasharray", dashArray);
return this;
}

public StyleBuilder lineWidth(float width) {
paint.put("line-width", width);
return this;
Expand Down Expand Up @@ -235,6 +266,11 @@ public final StyleBuilder vertexFilter(Class<? extends Vertex>... classToFilter)
return filterClasses(classToFilter);
}

public StyleBuilder filterValueInProperty(String value, String propertyName) {
filter = List.of("in", value, List.of("string", List.of("get", propertyName)));
return this;
}

public JsonNode toJson() {
validate();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.opentripplanner.apis.support.mapping.PropertyMapper;
import org.opentripplanner.framework.collection.ListUtils;
import org.opentripplanner.inspector.vector.KeyValue;
import org.opentripplanner.street.model.StreetTraversalPermission;
import org.opentripplanner.street.model.edge.Edge;
import org.opentripplanner.street.model.edge.EscalatorEdge;
import org.opentripplanner.street.model.edge.StreetEdge;
Expand All @@ -29,8 +30,9 @@ protected Collection<KeyValue> map(Edge input) {

private static List<KeyValue> mapStreetEdge(StreetEdge se) {
var props = Lists.newArrayList(
kv("permission", se.getPermission().toString()),
kv("bicycleSafetyFactor", roundTo2Decimals(se.getBicycleSafetyFactor()))
kv("permission", streetPermissionAsString(se.getPermission())),
kv("bicycleSafetyFactor", roundTo2Decimals(se.getBicycleSafetyFactor())),
kv("noThruTraffic", noThruTrafficAsString(se))
);
if (se.hasBogusName()) {
props.addFirst(kv("name", "%s (generated)".formatted(se.getName().toString())));
Expand All @@ -39,4 +41,26 @@ private static List<KeyValue> mapStreetEdge(StreetEdge se) {
}
return props;
}

public static String streetPermissionAsString(StreetTraversalPermission permission) {
return (
permission == StreetTraversalPermission.ALL
? "PEDESTRIAN_AND_BICYCLE_AND_CAR"
: permission.toString()
).replace("_AND_", " ");
}

private static String noThruTrafficAsString(StreetEdge se) {
var noThruPermission = StreetTraversalPermission.NONE;
if (se.isWalkNoThruTraffic()) {
noThruPermission = noThruPermission.add(StreetTraversalPermission.PEDESTRIAN);
}
if (se.isBicycleNoThruTraffic()) {
noThruPermission = noThruPermission.add(StreetTraversalPermission.BICYCLE);
}
if (se.isMotorVehicleNoThruTraffic()) {
noThruPermission = noThruPermission.add(StreetTraversalPermission.CAR);
}
return streetPermissionAsString(noThruPermission);
}
}
Loading

0 comments on commit 24c6206

Please sign in to comment.