From 6db97ca5d05d510c838f58c49eb6e8d64ca3b900 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 2 Oct 2024 15:24:40 +0200 Subject: [PATCH 01/18] Put debug layers into groups --- .../src/components/MapView/LayerControl.tsx | 38 +++++++++++++++---- .../apis/vectortiles/DebugStyleSpec.java | 4 ++ .../apis/vectortiles/model/StyleBuilder.java | 10 +++++ .../apis/vectortiles/style.json | 9 +++++ 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/client/src/components/MapView/LayerControl.tsx b/client/src/components/MapView/LayerControl.tsx index 1517a9ce7c8..6ba3add3cd9 100644 --- a/client/src/components/MapView/LayerControl.tsx +++ b/client/src/components/MapView/LayerControl.tsx @@ -1,6 +1,6 @@ import type { ControlPosition } from 'react-map-gl'; import { useControl } from 'react-map-gl'; -import { IControl, Map } from 'maplibre-gl'; +import { IControl, Map as WebMap } from 'maplibre-gl'; type LayerControlProps = { position: ControlPosition; @@ -15,7 +15,7 @@ type LayerControlProps = { class LayerControl implements IControl { private readonly container: HTMLDivElement = document.createElement('div'); - onAdd(map: Map) { + onAdd(map: WebMap) { this.container.className = 'maplibregl-ctrl maplibregl-ctrl-group layer-select'; map.on('load', () => { @@ -24,10 +24,11 @@ class LayerControl implements IControl { this.container.removeChild(this.container.firstChild); } - const title = document.createElement('h6'); + const title = document.createElement('h4'); title.textContent = 'Debug layers'; this.container.appendChild(title); + const groups: Map = new Map(); map .getLayersOrder() .map((l) => map.getLayer(l)) @@ -38,7 +39,16 @@ class LayerControl implements IControl { .reverse() .forEach((layer) => { if (layer) { - const div = document.createElement('div'); + const meta: { group: string | undefined } = layer.metadata as { group: string | undefined }; + console.log(meta); + + let groupName: string = 'Other'; + if (meta.group) { + groupName = meta.group; + } + console.log(groupName); + + const layerDiv = document.createElement('div'); const input = document.createElement('input'); input.type = 'checkbox'; input.value = layer.id; @@ -57,9 +67,21 @@ class LayerControl implements IControl { const label = document.createElement('label'); label.textContent = layer.id; label.htmlFor = layer.id; - div.appendChild(input); - div.appendChild(label); - this.container.appendChild(div); + layerDiv.appendChild(input); + layerDiv.appendChild(label); + + if (groups.has(groupName)) { + const g = groups.get(groupName); + g?.appendChild(layerDiv); + } else { + const h4 = document.createElement('h6'); + h4.textContent = groupName; + const groupDiv = document.createElement('div'); + groupDiv.appendChild(h4); + groupDiv.appendChild(layerDiv); + groups.set(groupName, groupDiv); + this.container.appendChild(groupDiv); + } } }); }); @@ -67,7 +89,7 @@ class LayerControl implements IControl { return this.container; } - private layerVisible(map: Map, layer: { id: string }) { + private layerVisible(map: WebMap, layer: { id: string }) { return map.getLayoutProperty(layer.id, 'visibility') !== 'none'; } diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index 21cdfee9ef7..d84e57dcc7d 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -61,6 +61,7 @@ public class DebugStyleSpec { TemporaryPartialStreetEdge.class, TemporaryFreeEdge.class, }; + private static final String EDGES_GROUP = "Edges"; static StyleSpec build( VectorSourceLayer regularStops, @@ -82,6 +83,7 @@ static StyleSpec build( StyleBuilder.ofId("background").typeRaster().source(BACKGROUND_SOURCE).minZoom(0), StyleBuilder .ofId("edge") + .group(EDGES_GROUP) .typeLine() .vectorSourceLayer(edges) .lineColor(MAGENTA) @@ -92,6 +94,7 @@ static StyleSpec build( .intiallyHidden(), StyleBuilder .ofId("edge-name") + .group(EDGES_GROUP) .typeSymbol() .lineText("name") .vectorSourceLayer(edges) @@ -101,6 +104,7 @@ static StyleSpec build( .intiallyHidden(), StyleBuilder .ofId("link") + .group(EDGES_GROUP) .typeLine() .vectorSourceLayer(edges) .lineColor(BRIGHT_GREEN) diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java index d842b5e6687..128971b0627 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java @@ -26,6 +26,7 @@ public class StyleBuilder { private final Map props = new LinkedHashMap<>(); private final Map paint = new LinkedHashMap<>(); private final Map layout = new LinkedHashMap<>(); + private final Map metadata = new LinkedHashMap<>(); private final Map line = new LinkedHashMap<>(); private List filter = List.of(); @@ -48,6 +49,7 @@ public enum LayerType { private StyleBuilder(String id) { props.put("id", id); + metadata.put("group", "Misc"); } public StyleBuilder minZoom(int i) { @@ -92,6 +94,11 @@ public StyleBuilder typeLine() { return this; } + public StyleBuilder group(String group) { + metadata.put("group", group); + return this; + } + public StyleBuilder typeFill() { type(LayerType.Fill); return this; @@ -220,6 +227,9 @@ public JsonNode toJson() { if (!line.isEmpty()) { copy.put("line", line); } + if (!metadata.isEmpty()) { + copy.put("metadata", metadata); + } return OBJECT_MAPPER.valueToTree(copy); } diff --git a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json index 5a2ed9572e2..8d00dc64418 100644 --- a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json +++ b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json @@ -61,6 +61,9 @@ "layout" : { "line-cap" : "round", "visibility" : "none" + }, + "metadata" : { + "group" : "Edges" } }, { @@ -111,6 +114,9 @@ "text-keep-upright" : true, "text-rotation-alignment" : "map", "visibility" : "none" + }, + "metadata" : { + "group" : "Edges" } }, { @@ -148,6 +154,9 @@ "layout" : { "line-cap" : "round", "visibility" : "none" + }, + "metadata" : { + "group" : "Edges" } }, { From 9cf69ca96d8401acd7c86383b686a01dcafc8a33 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 2 Oct 2024 17:30:49 +0200 Subject: [PATCH 02/18] Update layer groups --- .../src/components/MapView/LayerControl.tsx | 20 +- client/src/style.css | 16 + .../apis/vectortiles/DebugStyleSpec.java | 252 ++++++++------ .../apis/vectortiles/model/StyleBuilder.java | 17 +- .../vector/edge/EdgePropertyMapper.java | 4 +- .../apis/vectortiles/style.json | 308 +++++++++++++++++- 6 files changed, 512 insertions(+), 105 deletions(-) diff --git a/client/src/components/MapView/LayerControl.tsx b/client/src/components/MapView/LayerControl.tsx index 6ba3add3cd9..59b03fc1d8b 100644 --- a/client/src/components/MapView/LayerControl.tsx +++ b/client/src/components/MapView/LayerControl.tsx @@ -39,16 +39,16 @@ class LayerControl implements IControl { .reverse() .forEach((layer) => { if (layer) { - const meta: { group: string | undefined } = layer.metadata as { group: string | undefined }; - console.log(meta); + const meta: { group: string } = layer.metadata as { group: string }; - let groupName: string = 'Other'; + let groupName: string = 'Misc'; if (meta.group) { groupName = meta.group; } console.log(groupName); const layerDiv = document.createElement('div'); + layerDiv.className = 'layer'; const input = document.createElement('input'); input.type = 'checkbox'; input.value = layer.id; @@ -74,10 +74,18 @@ class LayerControl implements IControl { const g = groups.get(groupName); g?.appendChild(layerDiv); } else { - const h4 = document.createElement('h6'); - h4.textContent = groupName; + const input = document.createElement('input'); + input.type = 'checkbox'; + input.id = groupName; + + const label = document.createElement('label'); + label.textContent = groupName; + label.htmlFor = groupName; + const groupDiv = document.createElement('div'); - groupDiv.appendChild(h4); + groupDiv.className = 'group'; + groupDiv.appendChild(input); + groupDiv.appendChild(label); groupDiv.appendChild(layerDiv); groups.set(groupName, groupDiv); this.container.appendChild(groupDiv); diff --git a/client/src/style.css b/client/src/style.css index 86310fd857d..62403940eed 100644 --- a/client/src/style.css +++ b/client/src/style.css @@ -170,3 +170,19 @@ .maplibregl-ctrl-group.layer-select label { margin-left: 6px; } + +.maplibregl-ctrl-group.layer-select h4 { + font-size: 17px; +} + +.maplibregl-ctrl-group.layer-select h6 { + font-size: 15px; +} + +.maplibregl-ctrl-group.layer-select div.group { + margin-top: 10px; +} + +.maplibregl-ctrl-group.layer-select div.layer { + margin-left: 14px; +} \ No newline at end of file diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index d84e57dcc7d..db3871f69ad 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -1,5 +1,6 @@ package org.opentripplanner.apis.vectortiles; +import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -10,7 +11,9 @@ import org.opentripplanner.apis.vectortiles.model.VectorSourceLayer; import org.opentripplanner.apis.vectortiles.model.ZoomDependentNumber; import org.opentripplanner.apis.vectortiles.model.ZoomDependentNumber.ZoomStop; +import org.opentripplanner.framework.collection.ListUtils; import org.opentripplanner.service.vehiclerental.street.StreetVehicleRentalLink; +import org.opentripplanner.street.model.StreetTraversalPermission; import org.opentripplanner.street.model.edge.AreaEdge; import org.opentripplanner.street.model.edge.BoardingLocationToStopLink; import org.opentripplanner.street.model.edge.Edge; @@ -43,11 +46,16 @@ 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 int MAX_ZOOM = 23; private static final ZoomDependentNumber LINE_WIDTH = new ZoomDependentNumber( 1.3f, List.of(new ZoomStop(13, 0.5f), new ZoomStop(MAX_ZOOM, 10)) ); + private static final ZoomDependentNumber FAT_LINE_WIDTH = new ZoomDependentNumber( + 1.3f, + List.of(new ZoomStop(11, 1f), new ZoomStop(MAX_ZOOM, 12)) + ); private static final ZoomDependentNumber CIRCLE_STROKE = new ZoomDependentNumber( 1, List.of(new ZoomStop(15, 0.2f), new ZoomStop(MAX_ZOOM, 3)) @@ -62,6 +70,9 @@ public class DebugStyleSpec { TemporaryFreeEdge.class, }; private static final String EDGES_GROUP = "Edges"; + private static final String STOPS = "Stops"; + private static final String VERTICES = "Vertices"; + private static final String TRAVERSAL_PERMISSIONS_GROU = "Traversal permissions"; static StyleSpec build( VectorSourceLayer regularStops, @@ -79,104 +90,159 @@ static StyleSpec build( return new StyleSpec( "OTP Debug Tiles", allSources, - List.of( - StyleBuilder.ofId("background").typeRaster().source(BACKGROUND_SOURCE).minZoom(0), - StyleBuilder - .ofId("edge") - .group(EDGES_GROUP) - .typeLine() - .vectorSourceLayer(edges) - .lineColor(MAGENTA) - .edgeFilter(EDGES_TO_DISPLAY) - .lineWidth(LINE_WIDTH) - .minZoom(6) - .maxZoom(MAX_ZOOM) - .intiallyHidden(), - StyleBuilder - .ofId("edge-name") - .group(EDGES_GROUP) - .typeSymbol() - .lineText("name") - .vectorSourceLayer(edges) - .edgeFilter(EDGES_TO_DISPLAY) - .minZoom(17) - .maxZoom(MAX_ZOOM) - .intiallyHidden(), + ListUtils.combine( + List.of(StyleBuilder.ofId("background").typeRaster().source(BACKGROUND_SOURCE).minZoom(0)), + traversalPermissions(edges), + List.of( + StyleBuilder + .ofId("edge") + .group(EDGES_GROUP) + .typeLine() + .vectorSourceLayer(edges) + .lineColor(MAGENTA) + .edgeFilter(EDGES_TO_DISPLAY) + .lineWidth(LINE_WIDTH) + .minZoom(6) + .maxZoom(MAX_ZOOM) + .intiallyHidden(), + StyleBuilder + .ofId("edge-name") + .group(EDGES_GROUP) + .typeSymbol() + .lineText("name") + .vectorSourceLayer(edges) + .edgeFilter(EDGES_TO_DISPLAY) + .minZoom(17) + .maxZoom(MAX_ZOOM) + .intiallyHidden(), + StyleBuilder + .ofId("link") + .group(EDGES_GROUP) + .typeLine() + .vectorSourceLayer(edges) + .lineColor(BRIGHT_GREEN) + .edgeFilter( + StreetTransitStopLink.class, + StreetTransitEntranceLink.class, + BoardingLocationToStopLink.class, + StreetVehicleRentalLink.class, + StreetVehicleParkingLink.class + ) + .lineWidth(LINE_WIDTH) + .minZoom(13) + .maxZoom(MAX_ZOOM) + .intiallyHidden(), + StyleBuilder + .ofId("vertex") + .group(VERTICES) + .typeCircle() + .vectorSourceLayer(vertices) + .circleStroke(BLACK, CIRCLE_STROKE) + .circleRadius( + new ZoomDependentNumber(1, List.of(new ZoomStop(15, 1), new ZoomStop(MAX_ZOOM, 7))) + ) + .circleColor(PURPLE) + .minZoom(15) + .maxZoom(MAX_ZOOM) + .intiallyHidden(), + StyleBuilder + .ofId("parking-vertex") + .group(VERTICES) + .typeCircle() + .vectorSourceLayer(vertices) + .vertexFilter(VehicleParkingEntranceVertex.class) + .circleStroke(BLACK, CIRCLE_STROKE) + .circleRadius( + new ZoomDependentNumber( + 1, + List.of(new ZoomStop(13, 1.4f), new ZoomStop(MAX_ZOOM, 10)) + ) + ) + .circleColor(DARK_GREEN) + .minZoom(13) + .maxZoom(MAX_ZOOM) + .intiallyHidden(), + StyleBuilder + .ofId("area-stop") + .group(STOPS) + .typeFill() + .vectorSourceLayer(areaStops) + .fillColor(BRIGHT_GREEN) + .fillOpacity(0.5f) + .fillOutlineColor(BLACK) + .minZoom(6) + .maxZoom(MAX_ZOOM), + StyleBuilder + .ofId("group-stop") + .group(STOPS) + .typeFill() + .vectorSourceLayer(groupStops) + .fillColor(BRIGHT_GREEN) + .fillOpacity(0.5f) + .fillOutlineColor(BLACK) + .minZoom(6) + .maxZoom(MAX_ZOOM), + StyleBuilder + .ofId("regular-stop") + .group(STOPS) + .typeCircle() + .vectorSourceLayer(regularStops) + .circleStroke( + BLACK, + new ZoomDependentNumber(1, List.of(new ZoomStop(11, 0.5f), new ZoomStop(MAX_ZOOM, 5))) + ) + .circleRadius( + new ZoomDependentNumber( + 1, + List.of(new ZoomStop(11, 0.5f), new ZoomStop(MAX_ZOOM, 10)) + ) + ) + .circleColor("#fcf9fa") + .minZoom(10) + .maxZoom(MAX_ZOOM) + ) + ) + ); + } + + private static List traversalPermissions(VectorSourceLayer edges) { + var permissionStyles = Arrays + .stream(StreetTraversalPermission.values()) + .map(p -> StyleBuilder - .ofId("link") - .group(EDGES_GROUP) + .ofId(p.name()) + .group(TRAVERSAL_PERMISSIONS_GROU) .typeLine() .vectorSourceLayer(edges) - .lineColor(BRIGHT_GREEN) - .edgeFilter( - StreetTransitStopLink.class, - StreetTransitEntranceLink.class, - BoardingLocationToStopLink.class, - StreetVehicleRentalLink.class, - StreetVehicleParkingLink.class - ) - .lineWidth(LINE_WIDTH) - .minZoom(13) - .maxZoom(MAX_ZOOM) - .intiallyHidden(), - StyleBuilder - .ofId("vertex") - .typeCircle() - .vectorSourceLayer(vertices) - .circleStroke(BLACK, CIRCLE_STROKE) - .circleRadius( - new ZoomDependentNumber(1, List.of(new ZoomStop(15, 1), new ZoomStop(MAX_ZOOM, 7))) - ) - .circleColor(PURPLE) - .minZoom(15) - .maxZoom(MAX_ZOOM) - .intiallyHidden(), - StyleBuilder - .ofId("parking-vertex") - .typeCircle() - .vectorSourceLayer(vertices) - .vertexFilter(VehicleParkingEntranceVertex.class) - .circleStroke(BLACK, CIRCLE_STROKE) - .circleRadius( - new ZoomDependentNumber(1, List.of(new ZoomStop(13, 1.4f), new ZoomStop(MAX_ZOOM, 10))) - ) - .circleColor(DARK_GREEN) - .minZoom(13) - .maxZoom(MAX_ZOOM) - .intiallyHidden(), - StyleBuilder - .ofId("area-stop") - .typeFill() - .vectorSourceLayer(areaStops) - .fillColor(BRIGHT_GREEN) - .fillOpacity(0.5f) - .fillOutlineColor(BLACK) + .lineColor(permissionColor(p)) + .permissionsFilter(p) + .lineWidth(FAT_LINE_WIDTH) + .offsetFromProperty() .minZoom(6) - .maxZoom(MAX_ZOOM), - StyleBuilder - .ofId("group-stop") - .typeFill() - .vectorSourceLayer(groupStops) - .fillColor(BRIGHT_GREEN) - .fillOpacity(0.5f) - .fillOutlineColor(BLACK) - .minZoom(6) - .maxZoom(MAX_ZOOM), - StyleBuilder - .ofId("regular-stop") - .typeCircle() - .vectorSourceLayer(regularStops) - .circleStroke( - BLACK, - new ZoomDependentNumber(1, List.of(new ZoomStop(11, 0.5f), new ZoomStop(MAX_ZOOM, 5))) - ) - .circleRadius( - new ZoomDependentNumber(1, List.of(new ZoomStop(11, 0.5f), new ZoomStop(MAX_ZOOM, 10))) - ) - .circleColor("#fcf9fa") - .minZoom(10) .maxZoom(MAX_ZOOM) + .intiallyHidden() ) - ); + .toList(); + var textStyle = StyleBuilder + .ofId("permission-text") + .group(TRAVERSAL_PERMISSIONS_GROU) + .typeSymbol() + .lineText("permission") + .vectorSourceLayer(edges) + .edgeFilter(EDGES_TO_DISPLAY) + .minZoom(17) + .maxZoom(MAX_ZOOM); + return ListUtils.combine(permissionStyles, List.of(textStyle)); + } + + private static String permissionColor(StreetTraversalPermission p) { + return switch (p) { + case NONE -> "#000"; + case PEDESTRIAN -> "#2ba812"; + case BICYCLE, PEDESTRIAN_AND_BICYCLE -> "#10d3b6"; + case CAR -> "#f92e13"; + case BICYCLE_AND_CAR, PEDESTRIAN_AND_CAR -> "#e25f8f"; + case ALL -> "#adb2b0"; + }; } } diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java index 128971b0627..c4038592823 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java @@ -11,6 +11,7 @@ import org.opentripplanner.apis.vectortiles.model.ZoomDependentNumber.ZoomStop; import org.opentripplanner.framework.collection.ListUtils; import org.opentripplanner.framework.json.ObjectMappers; +import org.opentripplanner.street.model.StreetTraversalPermission; import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.model.vertex.Vertex; @@ -39,6 +40,11 @@ public StyleBuilder vectorSourceLayer(VectorSourceLayer source) { return sourceLayer(source.vectorLayer()); } + public StyleBuilder offsetFromProperty() { + line.put("line-offset", List.of("get", "offset")); + return this; + } + public enum LayerType { Circle, Line, @@ -49,7 +55,7 @@ public enum LayerType { private StyleBuilder(String id) { props.put("id", id); - metadata.put("group", "Misc"); + metadata.put("group", "Other"); } public StyleBuilder minZoom(int i) { @@ -203,6 +209,11 @@ public final StyleBuilder edgeFilter(Class... classToFilter) { return filterClasses(classToFilter); } + public final StyleBuilder permissionsFilter(StreetTraversalPermission p) { + filter = List.of("==", "permission", p.name()); + return this; + } + /** * Only apply the style to the given vertices. */ @@ -227,9 +238,7 @@ public JsonNode toJson() { if (!line.isEmpty()) { copy.put("line", line); } - if (!metadata.isEmpty()) { - copy.put("metadata", metadata); - } + copy.put("metadata", metadata); return OBJECT_MAPPER.valueToTree(copy); } diff --git a/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java b/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java index d43a91d384d..dcb9bbfcda2 100644 --- a/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java +++ b/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java @@ -30,7 +30,9 @@ protected Collection map(Edge input) { private static List mapStreetEdge(StreetEdge se) { var props = Lists.newArrayList( kv("permission", se.getPermission().toString()), - kv("bicycleSafetyFactor", roundTo2Decimals(se.getBicycleSafetyFactor())) + kv("bicycleSafetyFactor", roundTo2Decimals(se.getBicycleSafetyFactor())), + kv("back", se.isBack()), + kv("offset", se.isBack() ? -10 : 10) ); if (se.hasBogusName()) { props.addFirst(kv("name", "%s (generated)".formatted(se.getName().toString()))); diff --git a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json index 8d00dc64418..c456e5bd0e7 100644 --- a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json +++ b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json @@ -22,7 +22,298 @@ "id" : "background", "type" : "raster", "source" : "background", - "minzoom" : 0 + "minzoom" : 0, + "metadata" : { + "group" : "Other" + } + }, + { + "id" : "NONE", + "type" : "line", + "source" : "vectorSource", + "source-layer" : "edges", + "minzoom" : 6, + "maxzoom" : 23, + "paint" : { + "line-color" : "#000", + "line-width" : { + "base" : 2.3, + "stops" : [ + [ + 11, + 1.0 + ], + [ + 23, + 14.0 + ] + ] + } + }, + "filter" : [ + "==", + "permission", + "NONE" + ], + "layout" : { + "line-cap" : "round", + "visibility" : "none" + }, + "metadata" : { + "group" : "Traversal permissions" + } + }, + { + "id" : "PEDESTRIAN", + "type" : "line", + "source" : "vectorSource", + "source-layer" : "edges", + "minzoom" : 6, + "maxzoom" : 23, + "paint" : { + "line-color" : "#0EE38B", + "line-width" : { + "base" : 2.3, + "stops" : [ + [ + 11, + 1.0 + ], + [ + 23, + 14.0 + ] + ] + } + }, + "filter" : [ + "==", + "permission", + "PEDESTRIAN" + ], + "layout" : { + "line-cap" : "round", + "visibility" : "none" + }, + "metadata" : { + "group" : "Traversal permissions" + } + }, + { + "id" : "BICYCLE", + "type" : "line", + "source" : "vectorSource", + "source-layer" : "edges", + "minzoom" : 6, + "maxzoom" : 23, + "paint" : { + "line-color" : "#E3A10E", + "line-width" : { + "base" : 2.3, + "stops" : [ + [ + 11, + 1.0 + ], + [ + 23, + 14.0 + ] + ] + } + }, + "filter" : [ + "==", + "permission", + "BICYCLE" + ], + "layout" : { + "line-cap" : "round", + "visibility" : "none" + }, + "metadata" : { + "group" : "Traversal permissions" + } + }, + { + "id" : "PEDESTRIAN_AND_BICYCLE", + "type" : "line", + "source" : "vectorSource", + "source-layer" : "edges", + "minzoom" : 6, + "maxzoom" : 23, + "paint" : { + "line-color" : "#00EB68", + "line-width" : { + "base" : 2.3, + "stops" : [ + [ + 11, + 1.0 + ], + [ + 23, + 14.0 + ] + ] + } + }, + "filter" : [ + "==", + "permission", + "PEDESTRIAN_AND_BICYCLE" + ], + "layout" : { + "line-cap" : "round", + "visibility" : "none" + }, + "metadata" : { + "group" : "Traversal permissions" + } + }, + { + "id" : "CAR", + "type" : "line", + "source" : "vectorSource", + "source-layer" : "edges", + "minzoom" : 6, + "maxzoom" : 23, + "paint" : { + "line-color" : "#EB131F", + "line-width" : { + "base" : 2.3, + "stops" : [ + [ + 11, + 1.0 + ], + [ + 23, + 14.0 + ] + ] + } + }, + "filter" : [ + "==", + "permission", + "CAR" + ], + "layout" : { + "line-cap" : "round", + "visibility" : "none" + }, + "metadata" : { + "group" : "Traversal permissions" + } + }, + { + "id" : "PEDESTRIAN_AND_CAR", + "type" : "line", + "source" : "vectorSource", + "source-layer" : "edges", + "minzoom" : 6, + "maxzoom" : 23, + "paint" : { + "line-color" : "#963E4A", + "line-width" : { + "base" : 2.3, + "stops" : [ + [ + 11, + 1.0 + ], + [ + 23, + 14.0 + ] + ] + } + }, + "filter" : [ + "==", + "permission", + "PEDESTRIAN_AND_CAR" + ], + "layout" : { + "line-cap" : "round", + "visibility" : "none" + }, + "metadata" : { + "group" : "Traversal permissions" + } + }, + { + "id" : "BICYCLE_AND_CAR", + "type" : "line", + "source" : "vectorSource", + "source-layer" : "edges", + "minzoom" : 6, + "maxzoom" : 23, + "paint" : { + "line-color" : "#3E5D6B", + "line-width" : { + "base" : 2.3, + "stops" : [ + [ + 11, + 1.0 + ], + [ + 23, + 14.0 + ] + ] + } + }, + "filter" : [ + "==", + "permission", + "BICYCLE_AND_CAR" + ], + "layout" : { + "line-cap" : "round", + "visibility" : "none" + }, + "metadata" : { + "group" : "Traversal permissions" + } + }, + { + "id" : "ALL", + "type" : "line", + "source" : "vectorSource", + "source-layer" : "edges", + "minzoom" : 6, + "maxzoom" : 23, + "paint" : { + "line-color" : "#413031", + "line-width" : { + "base" : 2.3, + "stops" : [ + [ + 11, + 1.0 + ], + [ + 23, + 14.0 + ] + ] + } + }, + "filter" : [ + "==", + "permission", + "ALL" + ], + "layout" : { + "line-cap" : "round", + "visibility" : "none" + }, + "metadata" : { + "group" : "Traversal permissions" + } }, { "id" : "edge", @@ -198,6 +489,9 @@ }, "layout" : { "visibility" : "none" + }, + "metadata" : { + "group" : "Vertices" } }, { @@ -244,6 +538,9 @@ ], "layout" : { "visibility" : "none" + }, + "metadata" : { + "group" : "Vertices" } }, { @@ -257,6 +554,9 @@ "fill-color" : "#22DD9E", "fill-opacity" : 0.5, "fill-outline-color" : "#140d0e" + }, + "metadata" : { + "group" : "Stops" } }, { @@ -270,6 +570,9 @@ "fill-color" : "#22DD9E", "fill-opacity" : 0.5, "fill-outline-color" : "#140d0e" + }, + "metadata" : { + "group" : "Stops" } }, { @@ -308,6 +611,9 @@ ] }, "circle-color" : "#fcf9fa" + }, + "metadata" : { + "group" : "Stops" } } ], From 04a6d579548f417a381dc567b7a94b72423b8563 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 3 Oct 2024 08:24:57 +0200 Subject: [PATCH 03/18] Add traversal permission layers --- .../src/components/MapView/LayerControl.tsx | 29 ++++++++++++------- client/src/style.css | 2 +- .../apis/vectortiles/DebugStyleSpec.java | 9 +++--- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/client/src/components/MapView/LayerControl.tsx b/client/src/components/MapView/LayerControl.tsx index 59b03fc1d8b..ff8fa26c91f 100644 --- a/client/src/components/MapView/LayerControl.tsx +++ b/client/src/components/MapView/LayerControl.tsx @@ -56,14 +56,14 @@ class LayerControl implements IControl { input.onchange = (e) => { e.preventDefault(); e.stopPropagation(); - - if (this.layerVisible(map, layer)) { - map.setLayoutProperty(layer.id, 'visibility', 'none'); - } else { + if (input.checked) { map.setLayoutProperty(layer.id, 'visibility', 'visible'); + } else { + map.setLayoutProperty(layer.id, 'visibility', 'none'); } }; input.checked = this.layerVisible(map, layer); + input.className = 'layer'; const label = document.createElement('label'); label.textContent = layer.id; label.htmlFor = layer.id; @@ -74,17 +74,26 @@ class LayerControl implements IControl { const g = groups.get(groupName); g?.appendChild(layerDiv); } else { - const input = document.createElement('input'); - input.type = 'checkbox'; - input.id = groupName; + const groupDiv = document.createElement('div'); + groupDiv.className = 'group'; + + const groupInput = document.createElement('input'); + groupInput.onchange = () => { + groupDiv.querySelectorAll('input.layer').forEach((i) => { + const input = i as HTMLInputElement; + input.checked = groupInput.checked; + var event = new Event('change'); + i.dispatchEvent(event); + }); + }; + groupInput.type = 'checkbox'; + groupInput.id = groupName; const label = document.createElement('label'); label.textContent = groupName; label.htmlFor = groupName; - const groupDiv = document.createElement('div'); - groupDiv.className = 'group'; - groupDiv.appendChild(input); + groupDiv.appendChild(groupInput); groupDiv.appendChild(label); groupDiv.appendChild(layerDiv); groups.set(groupName, groupDiv); diff --git a/client/src/style.css b/client/src/style.css index 62403940eed..58f9e8dff2a 100644 --- a/client/src/style.css +++ b/client/src/style.css @@ -185,4 +185,4 @@ .maplibregl-ctrl-group.layer-select div.layer { margin-left: 14px; -} \ No newline at end of file +} diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index db3871f69ad..20c713d2825 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -72,7 +72,7 @@ public class DebugStyleSpec { private static final String EDGES_GROUP = "Edges"; private static final String STOPS = "Stops"; private static final String VERTICES = "Vertices"; - private static final String TRAVERSAL_PERMISSIONS_GROU = "Traversal permissions"; + private static final String TRAVERSAL_PERMISSIONS_GROUP = "Traversal permissions"; static StyleSpec build( VectorSourceLayer regularStops, @@ -211,7 +211,7 @@ private static List traversalPermissions(VectorSourceLayer edges) .map(p -> StyleBuilder .ofId(p.name()) - .group(TRAVERSAL_PERMISSIONS_GROU) + .group(TRAVERSAL_PERMISSIONS_GROUP) .typeLine() .vectorSourceLayer(edges) .lineColor(permissionColor(p)) @@ -225,13 +225,14 @@ private static List traversalPermissions(VectorSourceLayer edges) .toList(); var textStyle = StyleBuilder .ofId("permission-text") - .group(TRAVERSAL_PERMISSIONS_GROU) + .group(TRAVERSAL_PERMISSIONS_GROUP) .typeSymbol() .lineText("permission") .vectorSourceLayer(edges) .edgeFilter(EDGES_TO_DISPLAY) .minZoom(17) - .maxZoom(MAX_ZOOM); + .maxZoom(MAX_ZOOM) + .intiallyHidden(); return ListUtils.combine(permissionStyles, List.of(textStyle)); } From 1ac0029fba78c0cd92bd4e743b68f960d0cbc31d Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 3 Oct 2024 08:59:47 +0200 Subject: [PATCH 04/18] Remove offsets --- .../apis/vectortiles/DebugStyleSpec.java | 1 - .../apis/vectortiles/model/StyleBuilder.java | 5 - .../vector/edge/EdgePropertyMapper.java | 4 +- .../apis/vectortiles/style.json | 99 ++++++++++++++----- 4 files changed, 77 insertions(+), 32 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index 20c713d2825..bca22c14c26 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -217,7 +217,6 @@ private static List traversalPermissions(VectorSourceLayer edges) .lineColor(permissionColor(p)) .permissionsFilter(p) .lineWidth(FAT_LINE_WIDTH) - .offsetFromProperty() .minZoom(6) .maxZoom(MAX_ZOOM) .intiallyHidden() diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java index c4038592823..689a6a17f78 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java @@ -40,11 +40,6 @@ public StyleBuilder vectorSourceLayer(VectorSourceLayer source) { return sourceLayer(source.vectorLayer()); } - public StyleBuilder offsetFromProperty() { - line.put("line-offset", List.of("get", "offset")); - return this; - } - public enum LayerType { Circle, Line, diff --git a/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java b/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java index dcb9bbfcda2..d43a91d384d 100644 --- a/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java +++ b/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java @@ -30,9 +30,7 @@ protected Collection map(Edge input) { private static List mapStreetEdge(StreetEdge se) { var props = Lists.newArrayList( kv("permission", se.getPermission().toString()), - kv("bicycleSafetyFactor", roundTo2Decimals(se.getBicycleSafetyFactor())), - kv("back", se.isBack()), - kv("offset", se.isBack() ? -10 : 10) + kv("bicycleSafetyFactor", roundTo2Decimals(se.getBicycleSafetyFactor())) ); if (se.hasBogusName()) { props.addFirst(kv("name", "%s (generated)".formatted(se.getName().toString()))); diff --git a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json index c456e5bd0e7..acdc521d13f 100644 --- a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json +++ b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json @@ -37,7 +37,7 @@ "paint" : { "line-color" : "#000", "line-width" : { - "base" : 2.3, + "base" : 1.3, "stops" : [ [ 11, @@ -45,7 +45,7 @@ ], [ 23, - 14.0 + 12.0 ] ] } @@ -71,9 +71,9 @@ "minzoom" : 6, "maxzoom" : 23, "paint" : { - "line-color" : "#0EE38B", + "line-color" : "#2ba812", "line-width" : { - "base" : 2.3, + "base" : 1.3, "stops" : [ [ 11, @@ -81,7 +81,7 @@ ], [ 23, - 14.0 + 12.0 ] ] } @@ -107,9 +107,9 @@ "minzoom" : 6, "maxzoom" : 23, "paint" : { - "line-color" : "#E3A10E", + "line-color" : "#10d3b6", "line-width" : { - "base" : 2.3, + "base" : 1.3, "stops" : [ [ 11, @@ -117,7 +117,7 @@ ], [ 23, - 14.0 + 12.0 ] ] } @@ -143,9 +143,9 @@ "minzoom" : 6, "maxzoom" : 23, "paint" : { - "line-color" : "#00EB68", + "line-color" : "#10d3b6", "line-width" : { - "base" : 2.3, + "base" : 1.3, "stops" : [ [ 11, @@ -153,7 +153,7 @@ ], [ 23, - 14.0 + 12.0 ] ] } @@ -179,9 +179,9 @@ "minzoom" : 6, "maxzoom" : 23, "paint" : { - "line-color" : "#EB131F", + "line-color" : "#f92e13", "line-width" : { - "base" : 2.3, + "base" : 1.3, "stops" : [ [ 11, @@ -189,7 +189,7 @@ ], [ 23, - 14.0 + 12.0 ] ] } @@ -215,9 +215,9 @@ "minzoom" : 6, "maxzoom" : 23, "paint" : { - "line-color" : "#963E4A", + "line-color" : "#e25f8f", "line-width" : { - "base" : 2.3, + "base" : 1.3, "stops" : [ [ 11, @@ -225,7 +225,7 @@ ], [ 23, - 14.0 + 12.0 ] ] } @@ -251,9 +251,9 @@ "minzoom" : 6, "maxzoom" : 23, "paint" : { - "line-color" : "#3E5D6B", + "line-color" : "#e25f8f", "line-width" : { - "base" : 2.3, + "base" : 1.3, "stops" : [ [ 11, @@ -261,7 +261,7 @@ ], [ 23, - 14.0 + 12.0 ] ] } @@ -287,9 +287,9 @@ "minzoom" : 6, "maxzoom" : 23, "paint" : { - "line-color" : "#413031", + "line-color" : "#adb2b0", "line-width" : { - "base" : 2.3, + "base" : 1.3, "stops" : [ [ 11, @@ -297,7 +297,7 @@ ], [ 23, - 14.0 + 12.0 ] ] } @@ -315,6 +315,59 @@ "group" : "Traversal permissions" } }, + { + "id" : "permission-text", + "type" : "symbol", + "source" : "vectorSource", + "source-layer" : "edges", + "minzoom" : 17, + "maxzoom" : 23, + "paint" : { + "text-color" : "#000", + "text-halo-color" : "#fff", + "text-halo-blur" : 4, + "text-halo-width" : 3 + }, + "filter" : [ + "in", + "class", + "StreetEdge", + "AreaEdge", + "EscalatorEdge", + "PathwayEdge", + "ElevatorHopEdge", + "TemporaryPartialStreetEdge", + "TemporaryFreeEdge" + ], + "layout" : { + "symbol-placement" : "line", + "symbol-spacing" : 500, + "text-field" : "{permission}", + "text-font" : [ + "KlokanTech Noto Sans Regular" + ], + "text-size" : { + "base" : 14.0, + "stops" : [ + [ + 14, + 12.0 + ], + [ + 20, + 14.0 + ] + ] + }, + "text-max-width" : 5, + "text-keep-upright" : true, + "text-rotation-alignment" : "map", + "visibility" : "none" + }, + "metadata" : { + "group" : "Traversal permissions" + } + }, { "id" : "edge", "type" : "line", From 14721168e3ed399fae9526cb989607ead689b7cf Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 3 Oct 2024 11:50:09 +0200 Subject: [PATCH 05/18] Apply styles --- client/src/components/MapView/LayerControl.tsx | 9 +++++---- client/src/style.css | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/client/src/components/MapView/LayerControl.tsx b/client/src/components/MapView/LayerControl.tsx index ff8fa26c91f..65515e1b460 100644 --- a/client/src/components/MapView/LayerControl.tsx +++ b/client/src/components/MapView/LayerControl.tsx @@ -89,12 +89,13 @@ class LayerControl implements IControl { groupInput.type = 'checkbox'; groupInput.id = groupName; - const label = document.createElement('label'); - label.textContent = groupName; - label.htmlFor = groupName; + const groupLabel = document.createElement('label'); + groupLabel.textContent = groupName; + groupLabel.htmlFor = groupName; + groupLabel.className = 'group-label'; groupDiv.appendChild(groupInput); - groupDiv.appendChild(label); + groupDiv.appendChild(groupLabel); groupDiv.appendChild(layerDiv); groups.set(groupName, groupDiv); this.container.appendChild(groupDiv); diff --git a/client/src/style.css b/client/src/style.css index 58f9e8dff2a..ddc1d64ae3b 100644 --- a/client/src/style.css +++ b/client/src/style.css @@ -175,8 +175,9 @@ font-size: 17px; } -.maplibregl-ctrl-group.layer-select h6 { +.maplibregl-ctrl-group.layer-select .group-label { font-size: 15px; + margin-bottom: 5px; } .maplibregl-ctrl-group.layer-select div.group { From 01e6fe142394f718504c028d93c00decb8a4546f Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 4 Oct 2024 09:45:25 +0200 Subject: [PATCH 06/18] Split up style into several methods --- .../apis/vectortiles/DebugStyleSpec.java | 230 ++++++++++-------- 1 file changed, 122 insertions(+), 108 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index bca22c14c26..4031458182e 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -93,118 +93,132 @@ static StyleSpec build( ListUtils.combine( List.of(StyleBuilder.ofId("background").typeRaster().source(BACKGROUND_SOURCE).minZoom(0)), traversalPermissions(edges), - List.of( - StyleBuilder - .ofId("edge") - .group(EDGES_GROUP) - .typeLine() - .vectorSourceLayer(edges) - .lineColor(MAGENTA) - .edgeFilter(EDGES_TO_DISPLAY) - .lineWidth(LINE_WIDTH) - .minZoom(6) - .maxZoom(MAX_ZOOM) - .intiallyHidden(), - StyleBuilder - .ofId("edge-name") - .group(EDGES_GROUP) - .typeSymbol() - .lineText("name") - .vectorSourceLayer(edges) - .edgeFilter(EDGES_TO_DISPLAY) - .minZoom(17) - .maxZoom(MAX_ZOOM) - .intiallyHidden(), - StyleBuilder - .ofId("link") - .group(EDGES_GROUP) - .typeLine() - .vectorSourceLayer(edges) - .lineColor(BRIGHT_GREEN) - .edgeFilter( - StreetTransitStopLink.class, - StreetTransitEntranceLink.class, - BoardingLocationToStopLink.class, - StreetVehicleRentalLink.class, - StreetVehicleParkingLink.class - ) - .lineWidth(LINE_WIDTH) - .minZoom(13) - .maxZoom(MAX_ZOOM) - .intiallyHidden(), - StyleBuilder - .ofId("vertex") - .group(VERTICES) - .typeCircle() - .vectorSourceLayer(vertices) - .circleStroke(BLACK, CIRCLE_STROKE) - .circleRadius( - new ZoomDependentNumber(1, List.of(new ZoomStop(15, 1), new ZoomStop(MAX_ZOOM, 7))) - ) - .circleColor(PURPLE) - .minZoom(15) - .maxZoom(MAX_ZOOM) - .intiallyHidden(), - StyleBuilder - .ofId("parking-vertex") - .group(VERTICES) - .typeCircle() - .vectorSourceLayer(vertices) - .vertexFilter(VehicleParkingEntranceVertex.class) - .circleStroke(BLACK, CIRCLE_STROKE) - .circleRadius( - new ZoomDependentNumber( - 1, - List.of(new ZoomStop(13, 1.4f), new ZoomStop(MAX_ZOOM, 10)) - ) - ) - .circleColor(DARK_GREEN) - .minZoom(13) - .maxZoom(MAX_ZOOM) - .intiallyHidden(), - StyleBuilder - .ofId("area-stop") - .group(STOPS) - .typeFill() - .vectorSourceLayer(areaStops) - .fillColor(BRIGHT_GREEN) - .fillOpacity(0.5f) - .fillOutlineColor(BLACK) - .minZoom(6) - .maxZoom(MAX_ZOOM), - StyleBuilder - .ofId("group-stop") - .group(STOPS) - .typeFill() - .vectorSourceLayer(groupStops) - .fillColor(BRIGHT_GREEN) - .fillOpacity(0.5f) - .fillOutlineColor(BLACK) - .minZoom(6) - .maxZoom(MAX_ZOOM), - StyleBuilder - .ofId("regular-stop") - .group(STOPS) - .typeCircle() - .vectorSourceLayer(regularStops) - .circleStroke( - BLACK, - new ZoomDependentNumber(1, List.of(new ZoomStop(11, 0.5f), new ZoomStop(MAX_ZOOM, 5))) - ) - .circleRadius( - new ZoomDependentNumber( - 1, - List.of(new ZoomStop(11, 0.5f), new ZoomStop(MAX_ZOOM, 10)) - ) - ) - .circleColor("#fcf9fa") - .minZoom(10) - .maxZoom(MAX_ZOOM) - ) + edges(edges), + vertices(vertices), + stops(regularStops, areaStops, groupStops) ) ); } + private static List stops( + VectorSourceLayer regularStops, + VectorSourceLayer areaStops, + VectorSourceLayer groupStops + ) { + return List.of( + StyleBuilder + .ofId("area-stop") + .group(STOPS) + .typeFill() + .vectorSourceLayer(areaStops) + .fillColor(BRIGHT_GREEN) + .fillOpacity(0.5f) + .fillOutlineColor(BLACK) + .minZoom(6) + .maxZoom(MAX_ZOOM), + StyleBuilder + .ofId("group-stop") + .group(STOPS) + .typeFill() + .vectorSourceLayer(groupStops) + .fillColor(BRIGHT_GREEN) + .fillOpacity(0.5f) + .fillOutlineColor(BLACK) + .minZoom(6) + .maxZoom(MAX_ZOOM), + StyleBuilder + .ofId("regular-stop") + .group(STOPS) + .typeCircle() + .vectorSourceLayer(regularStops) + .circleStroke( + BLACK, + new ZoomDependentNumber(1, List.of(new ZoomStop(11, 0.5f), new ZoomStop(MAX_ZOOM, 5))) + ) + .circleRadius( + new ZoomDependentNumber(1, List.of(new ZoomStop(11, 0.5f), new ZoomStop(MAX_ZOOM, 10))) + ) + .circleColor("#fcf9fa") + .minZoom(10) + .maxZoom(MAX_ZOOM) + ); + } + + private static List vertices(VectorSourceLayer vertices) { + return List.of( + StyleBuilder + .ofId("vertex") + .group(VERTICES) + .typeCircle() + .vectorSourceLayer(vertices) + .circleStroke(BLACK, CIRCLE_STROKE) + .circleRadius( + new ZoomDependentNumber(1, List.of(new ZoomStop(15, 1), new ZoomStop(MAX_ZOOM, 7))) + ) + .circleColor(PURPLE) + .minZoom(15) + .maxZoom(MAX_ZOOM) + .intiallyHidden(), + StyleBuilder + .ofId("parking-vertex") + .group(VERTICES) + .typeCircle() + .vectorSourceLayer(vertices) + .vertexFilter(VehicleParkingEntranceVertex.class) + .circleStroke(BLACK, CIRCLE_STROKE) + .circleRadius( + new ZoomDependentNumber(1, List.of(new ZoomStop(13, 1.4f), new ZoomStop(MAX_ZOOM, 10))) + ) + .circleColor(DARK_GREEN) + .minZoom(13) + .maxZoom(MAX_ZOOM) + .intiallyHidden() + ); + } + + private static List edges(VectorSourceLayer edges) { + return List.of( + StyleBuilder + .ofId("edge") + .group(EDGES_GROUP) + .typeLine() + .vectorSourceLayer(edges) + .lineColor(MAGENTA) + .edgeFilter(EDGES_TO_DISPLAY) + .lineWidth(LINE_WIDTH) + .minZoom(6) + .maxZoom(MAX_ZOOM) + .intiallyHidden(), + StyleBuilder + .ofId("edge-name") + .group(EDGES_GROUP) + .typeSymbol() + .lineText("name") + .vectorSourceLayer(edges) + .edgeFilter(EDGES_TO_DISPLAY) + .minZoom(17) + .maxZoom(MAX_ZOOM) + .intiallyHidden(), + StyleBuilder + .ofId("link") + .group(EDGES_GROUP) + .typeLine() + .vectorSourceLayer(edges) + .lineColor(BRIGHT_GREEN) + .edgeFilter( + StreetTransitStopLink.class, + StreetTransitEntranceLink.class, + BoardingLocationToStopLink.class, + StreetVehicleRentalLink.class, + StreetVehicleParkingLink.class + ) + .lineWidth(LINE_WIDTH) + .minZoom(13) + .maxZoom(MAX_ZOOM) + .intiallyHidden() + ); + } + private static List traversalPermissions(VectorSourceLayer edges) { var permissionStyles = Arrays .stream(StreetTraversalPermission.values()) From beb76d5a66404d7ee741efc50d5e4c7cad1b400d Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 4 Oct 2024 09:47:52 +0200 Subject: [PATCH 07/18] Extract method --- .../src/components/MapView/LayerControl.tsx | 52 ++++++++++--------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/client/src/components/MapView/LayerControl.tsx b/client/src/components/MapView/LayerControl.tsx index 65515e1b460..e85ecf64135 100644 --- a/client/src/components/MapView/LayerControl.tsx +++ b/client/src/components/MapView/LayerControl.tsx @@ -45,7 +45,6 @@ class LayerControl implements IControl { if (meta.group) { groupName = meta.group; } - console.log(groupName); const layerDiv = document.createElement('div'); layerDiv.className = 'layer'; @@ -74,29 +73,7 @@ class LayerControl implements IControl { const g = groups.get(groupName); g?.appendChild(layerDiv); } else { - const groupDiv = document.createElement('div'); - groupDiv.className = 'group'; - - const groupInput = document.createElement('input'); - groupInput.onchange = () => { - groupDiv.querySelectorAll('input.layer').forEach((i) => { - const input = i as HTMLInputElement; - input.checked = groupInput.checked; - var event = new Event('change'); - i.dispatchEvent(event); - }); - }; - groupInput.type = 'checkbox'; - groupInput.id = groupName; - - const groupLabel = document.createElement('label'); - groupLabel.textContent = groupName; - groupLabel.htmlFor = groupName; - groupLabel.className = 'group-label'; - - groupDiv.appendChild(groupInput); - groupDiv.appendChild(groupLabel); - groupDiv.appendChild(layerDiv); + const groupDiv = this.buildgGroupDiv(groupName, layerDiv); groups.set(groupName, groupDiv); this.container.appendChild(groupDiv); } @@ -107,6 +84,33 @@ class LayerControl implements IControl { return this.container; } + private buildgGroupDiv(groupName: string, layerDiv: HTMLDivElement) { + const groupDiv = document.createElement('div'); + groupDiv.className = 'group'; + + const groupInput = document.createElement('input'); + groupInput.onchange = () => { + groupDiv.querySelectorAll('input.layer').forEach((i) => { + const input = i as HTMLInputElement; + input.checked = groupInput.checked; + const event = new Event('change'); + i.dispatchEvent(event); + }); + }; + groupInput.type = 'checkbox'; + groupInput.id = groupName; + + const groupLabel = document.createElement('label'); + groupLabel.textContent = groupName; + groupLabel.htmlFor = groupName; + groupLabel.className = 'group-label'; + + groupDiv.appendChild(groupInput); + groupDiv.appendChild(groupLabel); + groupDiv.appendChild(layerDiv); + return groupDiv; + } + private layerVisible(map: WebMap, layer: { id: string }) { return map.getLayoutProperty(layer.id, 'visibility') !== 'none'; } From 700d4efe01406ae4891c7817fe7e6f2deb054343 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 4 Oct 2024 11:43:16 +0200 Subject: [PATCH 08/18] Update docs --- .../apis/vectortiles/model/StyleBuilder.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java index 689a6a17f78..f01c953462b 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java @@ -95,11 +95,6 @@ public StyleBuilder typeLine() { return this; } - public StyleBuilder group(String group) { - metadata.put("group", group); - return this; - } - public StyleBuilder typeFill() { type(LayerType.Fill); return this; @@ -115,6 +110,15 @@ private StyleBuilder type(LayerType type) { return this; } + /** + * Puts the layer into an arbitrarily defined group in the layer selector. This allows you + * to switch the entire group on and off. + */ + public StyleBuilder group(String group) { + metadata.put("group", group); + return this; + } + public StyleBuilder lineText(String name) { layout.put("symbol-placement", "line"); layout.put("symbol-spacing", 500); @@ -204,6 +208,9 @@ public final StyleBuilder edgeFilter(Class... classToFilter) { return filterClasses(classToFilter); } + /** + * Filter the entities by their "permission" property. + */ public final StyleBuilder permissionsFilter(StreetTraversalPermission p) { filter = List.of("==", "permission", p.name()); return this; From eba10a6c89252f7382ee8a8968befd2b02743d0e Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sun, 6 Oct 2024 09:30:43 +0200 Subject: [PATCH 09/18] Finetune checkbox placement --- client/src/style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/style.css b/client/src/style.css index ddc1d64ae3b..69fb6917185 100644 --- a/client/src/style.css +++ b/client/src/style.css @@ -185,5 +185,5 @@ } .maplibregl-ctrl-group.layer-select div.layer { - margin-left: 14px; + margin-left: 17px; } From aa39b75dca54cd82489bca5a04b81036682e09b9 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 9 Oct 2024 16:54:36 +0200 Subject: [PATCH 10/18] Add line offset --- .../apis/vectortiles/DebugStyleSpec.java | 18 +-- .../apis/vectortiles/style.json | 119 +++++++++++++++++- 2 files changed, 128 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index 8643f0c088b..bbe3538857b 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -49,14 +49,14 @@ public class DebugStyleSpec { private static final String BLACK = "#140d0e"; private static final int MAX_ZOOM = 23; - private static final ZoomDependentNumber LINE_WIDTH = new ZoomDependentNumber( - 1.3f, - List.of(new ZoomStop(13, 0.5f), new ZoomStop(MAX_ZOOM, 10)) - ); private static final ZoomDependentNumber LINE_OFFSET = new ZoomDependentNumber( 1.3f, List.of(new ZoomStop(13, 0.3f), new ZoomStop(MAX_ZOOM, 6)) ); + private static final ZoomDependentNumber LINE_WIDTH = new ZoomDependentNumber( + 1.3f, + List.of(new ZoomStop(13, 0.5f), new ZoomStop(MAX_ZOOM, 10)) + ); private static final ZoomDependentNumber FAT_LINE_WIDTH = new ZoomDependentNumber( 1.3f, List.of(new ZoomStop(11, 1f), new ZoomStop(MAX_ZOOM, 12)) @@ -191,7 +191,7 @@ private static List edges(VectorSourceLayer edges) { .lineColor(MAGENTA) .edgeFilter(EDGES_TO_DISPLAY) .lineWidth(LINE_WIDTH) - .lineOffset(LINE_OFFSET) + .lineOffset(LINE_OFFSET) .minZoom(6) .maxZoom(MAX_ZOOM) .intiallyHidden(), @@ -216,11 +216,11 @@ private static List edges(VectorSourceLayer edges) { StreetTransitEntranceLink.class, BoardingLocationToStopLink.class, StreetVehicleRentalLink.class, - StreetVehicleParkingLink.class, - StreetStationCentroidLink.class + StreetVehicleParkingLink.class, + StreetStationCentroidLink.class ) .lineWidth(LINE_WIDTH) - .lineOffset(LINE_OFFSET) + .lineOffset(LINE_OFFSET) .minZoom(13) .maxZoom(MAX_ZOOM) .intiallyHidden() @@ -239,6 +239,7 @@ private static List traversalPermissions(VectorSourceLayer edges) .lineColor(permissionColor(p)) .permissionsFilter(p) .lineWidth(FAT_LINE_WIDTH) + .lineOffset(LINE_OFFSET) .minZoom(6) .maxZoom(MAX_ZOOM) .intiallyHidden() @@ -251,6 +252,7 @@ private static List traversalPermissions(VectorSourceLayer edges) .lineText("permission") .vectorSourceLayer(edges) .edgeFilter(EDGES_TO_DISPLAY) + .lineOffset(LINE_OFFSET) .minZoom(17) .maxZoom(MAX_ZOOM) .intiallyHidden(); diff --git a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json index 4c513ca31d4..bc4824b1ff1 100644 --- a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json +++ b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json @@ -48,6 +48,19 @@ 12.0 ] ] + }, + "line-offset" : { + "base" : 1.3, + "stops" : [ + [ + 13, + 0.3 + ], + [ + 23, + 6.0 + ] + ] } }, "filter" : [ @@ -84,6 +97,19 @@ 12.0 ] ] + }, + "line-offset" : { + "base" : 1.3, + "stops" : [ + [ + 13, + 0.3 + ], + [ + 23, + 6.0 + ] + ] } }, "filter" : [ @@ -120,6 +146,19 @@ 12.0 ] ] + }, + "line-offset" : { + "base" : 1.3, + "stops" : [ + [ + 13, + 0.3 + ], + [ + 23, + 6.0 + ] + ] } }, "filter" : [ @@ -156,6 +195,19 @@ 12.0 ] ] + }, + "line-offset" : { + "base" : 1.3, + "stops" : [ + [ + 13, + 0.3 + ], + [ + 23, + 6.0 + ] + ] } }, "filter" : [ @@ -192,6 +244,19 @@ 12.0 ] ] + }, + "line-offset" : { + "base" : 1.3, + "stops" : [ + [ + 13, + 0.3 + ], + [ + 23, + 6.0 + ] + ] } }, "filter" : [ @@ -228,6 +293,19 @@ 12.0 ] ] + }, + "line-offset" : { + "base" : 1.3, + "stops" : [ + [ + 13, + 0.3 + ], + [ + 23, + 6.0 + ] + ] } }, "filter" : [ @@ -264,6 +342,19 @@ 12.0 ] ] + }, + "line-offset" : { + "base" : 1.3, + "stops" : [ + [ + 13, + 0.3 + ], + [ + 23, + 6.0 + ] + ] } }, "filter" : [ @@ -300,6 +391,19 @@ 12.0 ] ] + }, + "line-offset" : { + "base" : 1.3, + "stops" : [ + [ + 13, + 0.3 + ], + [ + 23, + 6.0 + ] + ] } }, "filter" : [ @@ -326,7 +430,20 @@ "text-color" : "#000", "text-halo-color" : "#fff", "text-halo-blur" : 4, - "text-halo-width" : 3 + "text-halo-width" : 3, + "line-offset" : { + "base" : 1.3, + "stops" : [ + [ + 13, + 0.3 + ], + [ + 23, + 6.0 + ] + ] + } }, "filter" : [ "in", From 6fea235cd7332de907dd07f37e1e473cb6a2eb24 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 9 Oct 2024 22:01:35 +0200 Subject: [PATCH 11/18] Finetune vector tile styling --- .../apis/vectortiles/DebugStyleSpec.java | 39 +- .../apis/vectortiles/model/StyleBuilder.java | 14 +- .../model/ZoomDependentNumber.java | 17 +- .../apis/vectortiles/style.json | 779 +++++++++--------- 4 files changed, 423 insertions(+), 426 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index bbe3538857b..1fc87176af8 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -50,19 +50,12 @@ public class DebugStyleSpec { private static final int MAX_ZOOM = 23; private static final ZoomDependentNumber LINE_OFFSET = new ZoomDependentNumber( - 1.3f, List.of(new ZoomStop(13, 0.3f), new ZoomStop(MAX_ZOOM, 6)) ); private static final ZoomDependentNumber LINE_WIDTH = new ZoomDependentNumber( - 1.3f, - List.of(new ZoomStop(13, 0.5f), new ZoomStop(MAX_ZOOM, 10)) - ); - private static final ZoomDependentNumber FAT_LINE_WIDTH = new ZoomDependentNumber( - 1.3f, - List.of(new ZoomStop(11, 1f), new ZoomStop(MAX_ZOOM, 12)) + List.of(new ZoomStop(13, 0.2f), new ZoomStop(MAX_ZOOM, 8)) ); private static final ZoomDependentNumber CIRCLE_STROKE = new ZoomDependentNumber( - 1, List.of(new ZoomStop(15, 0.2f), new ZoomStop(MAX_ZOOM, 3)) ); private static final Class[] EDGES_TO_DISPLAY = new Class[] { @@ -75,8 +68,8 @@ public class DebugStyleSpec { TemporaryFreeEdge.class, }; private static final String EDGES_GROUP = "Edges"; - private static final String STOPS = "Stops"; - private static final String VERTICES = "Vertices"; + private static final String STOPS_GROUP = "Stops"; + private static final String VERTICES_GROUP = "Vertices"; private static final String TRAVERSAL_PERMISSIONS_GROUP = "Traversal permissions"; static StyleSpec build( @@ -113,7 +106,7 @@ private static List stops( return List.of( StyleBuilder .ofId("area-stop") - .group(STOPS) + .group(STOPS_GROUP) .typeFill() .vectorSourceLayer(areaStops) .fillColor(BRIGHT_GREEN) @@ -123,7 +116,7 @@ private static List stops( .maxZoom(MAX_ZOOM), StyleBuilder .ofId("group-stop") - .group(STOPS) + .group(STOPS_GROUP) .typeFill() .vectorSourceLayer(groupStops) .fillColor(BRIGHT_GREEN) @@ -133,15 +126,15 @@ private static List stops( .maxZoom(MAX_ZOOM), StyleBuilder .ofId("regular-stop") - .group(STOPS) + .group(STOPS_GROUP) .typeCircle() .vectorSourceLayer(regularStops) .circleStroke( BLACK, - new ZoomDependentNumber(1, List.of(new ZoomStop(11, 0.5f), new ZoomStop(MAX_ZOOM, 5))) + new ZoomDependentNumber(List.of(new ZoomStop(11, 0.5f), new ZoomStop(MAX_ZOOM, 5))) ) .circleRadius( - new ZoomDependentNumber(1, List.of(new ZoomStop(11, 0.5f), new ZoomStop(MAX_ZOOM, 10))) + new ZoomDependentNumber(List.of(new ZoomStop(11, 0.5f), new ZoomStop(MAX_ZOOM, 10))) ) .circleColor("#fcf9fa") .minZoom(10) @@ -153,12 +146,12 @@ private static List vertices(VectorSourceLayer vertices) { return List.of( StyleBuilder .ofId("vertex") - .group(VERTICES) + .group(VERTICES_GROUP) .typeCircle() .vectorSourceLayer(vertices) .circleStroke(BLACK, CIRCLE_STROKE) .circleRadius( - new ZoomDependentNumber(1, List.of(new ZoomStop(15, 1), new ZoomStop(MAX_ZOOM, 7))) + new ZoomDependentNumber(List.of(new ZoomStop(15, 1), new ZoomStop(MAX_ZOOM, 7))) ) .circleColor(PURPLE) .minZoom(15) @@ -166,13 +159,13 @@ private static List vertices(VectorSourceLayer vertices) { .intiallyHidden(), StyleBuilder .ofId("parking-vertex") - .group(VERTICES) + .group(VERTICES_GROUP) .typeCircle() .vectorSourceLayer(vertices) .vertexFilter(VehicleParkingEntranceVertex.class) .circleStroke(BLACK, CIRCLE_STROKE) .circleRadius( - new ZoomDependentNumber(1, List.of(new ZoomStop(13, 1.4f), new ZoomStop(MAX_ZOOM, 10))) + new ZoomDependentNumber(List.of(new ZoomStop(13, 1.4f), new ZoomStop(MAX_ZOOM, 10))) ) .circleColor(DARK_GREEN) .minZoom(13) @@ -233,12 +226,12 @@ private static List traversalPermissions(VectorSourceLayer edges) .map(p -> StyleBuilder .ofId(p.name()) + .vectorSourceLayer(edges) .group(TRAVERSAL_PERMISSIONS_GROUP) .typeLine() - .vectorSourceLayer(edges) .lineColor(permissionColor(p)) .permissionsFilter(p) - .lineWidth(FAT_LINE_WIDTH) + .lineWidth(LINE_WIDTH) .lineOffset(LINE_OFFSET) .minZoom(6) .maxZoom(MAX_ZOOM) @@ -247,12 +240,12 @@ private static List traversalPermissions(VectorSourceLayer edges) .toList(); var textStyle = StyleBuilder .ofId("permission-text") + .vectorSourceLayer(edges) .group(TRAVERSAL_PERMISSIONS_GROUP) .typeSymbol() .lineText("permission") - .vectorSourceLayer(edges) + .textOffset(1) .edgeFilter(EDGES_TO_DISPLAY) - .lineOffset(LINE_OFFSET) .minZoom(17) .maxZoom(MAX_ZOOM) .intiallyHidden(); diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java index 58e8b8e7499..93b7ea91e7c 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java @@ -120,17 +120,18 @@ public StyleBuilder group(String group) { } public StyleBuilder lineText(String name) { - layout.put("symbol-placement", "line"); - layout.put("symbol-spacing", 500); + layout.put("symbol-placement", "line-center"); + layout.put("symbol-spacing", 1000); layout.put("text-field", "{%s}".formatted(name)); layout.put("text-font", List.of("KlokanTech Noto Sans Regular")); layout.put( "text-size", - new ZoomDependentNumber(14, List.of(new ZoomStop(14, 12), new ZoomStop(20, 14))).toJson() + new ZoomDependentNumber(List.of(new ZoomStop(10, 6), new ZoomStop(24, 12))).toJson() ); - layout.put("text-max-width", 5); + layout.put("text-max-width", 100); layout.put("text-keep-upright", true); layout.put("text-rotation-alignment", "map"); + layout.put("text-overlap", "never"); paint.put("text-color", "#000"); paint.put("text-halo-color", "#fff"); paint.put("text-halo-blur", 4); @@ -138,6 +139,11 @@ public StyleBuilder lineText(String name) { return this; } + public StyleBuilder textOffset(float offset) { + layout.put("text-offset", List.of(0, offset)); + return this; + } + public StyleBuilder circleColor(String color) { paint.put("circle-color", validateColor(color)); return this; diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/ZoomDependentNumber.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/ZoomDependentNumber.java index d83c4b63495..4d1d83ee8a9 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/ZoomDependentNumber.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/ZoomDependentNumber.java @@ -2,21 +2,26 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; +import java.util.stream.Stream; +import org.opentripplanner.framework.collection.ListUtils; import org.opentripplanner.framework.json.ObjectMappers; /** * A style parameter that allows you to specify a number that changes dependent on the zoom level. */ -public record ZoomDependentNumber(float base, List stops) { +public record ZoomDependentNumber(List stops) { private static final ObjectMapper OBJECT_MAPPER = ObjectMappers.ignoringExtraFields(); public JsonNode toJson() { - var props = new LinkedHashMap<>(); - props.put("base", base); - var vals = stops.stream().map(ZoomStop::toList).toList(); - props.put("stops", vals); - return OBJECT_MAPPER.valueToTree(props); + var interpolation = new ArrayList<>(); + interpolation.add("interpolate"); + interpolation.add(List.of("linear")); + interpolation.add(List.of("zoom")); + stops.forEach(s -> interpolation.addAll(s.toList())); + + return OBJECT_MAPPER.valueToTree(interpolation); } /** diff --git a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json index bc4824b1ff1..8a0e457396e 100644 --- a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json +++ b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json @@ -29,39 +29,39 @@ }, { "id" : "NONE", - "type" : "line", "source" : "vectorSource", "source-layer" : "edges", + "type" : "line", "minzoom" : 6, "maxzoom" : 23, "paint" : { "line-color" : "#000", - "line-width" : { - "base" : 1.3, - "stops" : [ - [ - 11, - 1.0 - ], - [ - 23, - 12.0 - ] - ] - }, - "line-offset" : { - "base" : 1.3, - "stops" : [ - [ - 13, - 0.3 - ], - [ - 23, - 6.0 - ] - ] - } + "line-width" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.2, + 23, + 8.0 + ], + "line-offset" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.3, + 23, + 6.0 + ] }, "filter" : [ "==", @@ -78,39 +78,39 @@ }, { "id" : "PEDESTRIAN", - "type" : "line", "source" : "vectorSource", "source-layer" : "edges", + "type" : "line", "minzoom" : 6, "maxzoom" : 23, "paint" : { "line-color" : "#2ba812", - "line-width" : { - "base" : 1.3, - "stops" : [ - [ - 11, - 1.0 - ], - [ - 23, - 12.0 - ] - ] - }, - "line-offset" : { - "base" : 1.3, - "stops" : [ - [ - 13, - 0.3 - ], - [ - 23, - 6.0 - ] - ] - } + "line-width" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.2, + 23, + 8.0 + ], + "line-offset" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.3, + 23, + 6.0 + ] }, "filter" : [ "==", @@ -127,39 +127,39 @@ }, { "id" : "BICYCLE", - "type" : "line", "source" : "vectorSource", "source-layer" : "edges", + "type" : "line", "minzoom" : 6, "maxzoom" : 23, "paint" : { "line-color" : "#10d3b6", - "line-width" : { - "base" : 1.3, - "stops" : [ - [ - 11, - 1.0 - ], - [ - 23, - 12.0 - ] - ] - }, - "line-offset" : { - "base" : 1.3, - "stops" : [ - [ - 13, - 0.3 - ], - [ - 23, - 6.0 - ] - ] - } + "line-width" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.2, + 23, + 8.0 + ], + "line-offset" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.3, + 23, + 6.0 + ] }, "filter" : [ "==", @@ -176,39 +176,39 @@ }, { "id" : "PEDESTRIAN_AND_BICYCLE", - "type" : "line", "source" : "vectorSource", "source-layer" : "edges", + "type" : "line", "minzoom" : 6, "maxzoom" : 23, "paint" : { "line-color" : "#10d3b6", - "line-width" : { - "base" : 1.3, - "stops" : [ - [ - 11, - 1.0 - ], - [ - 23, - 12.0 - ] - ] - }, - "line-offset" : { - "base" : 1.3, - "stops" : [ - [ - 13, - 0.3 - ], - [ - 23, - 6.0 - ] - ] - } + "line-width" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.2, + 23, + 8.0 + ], + "line-offset" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.3, + 23, + 6.0 + ] }, "filter" : [ "==", @@ -225,39 +225,39 @@ }, { "id" : "CAR", - "type" : "line", "source" : "vectorSource", "source-layer" : "edges", + "type" : "line", "minzoom" : 6, "maxzoom" : 23, "paint" : { "line-color" : "#f92e13", - "line-width" : { - "base" : 1.3, - "stops" : [ - [ - 11, - 1.0 - ], - [ - 23, - 12.0 - ] - ] - }, - "line-offset" : { - "base" : 1.3, - "stops" : [ - [ - 13, - 0.3 - ], - [ - 23, - 6.0 - ] - ] - } + "line-width" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.2, + 23, + 8.0 + ], + "line-offset" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.3, + 23, + 6.0 + ] }, "filter" : [ "==", @@ -274,39 +274,39 @@ }, { "id" : "PEDESTRIAN_AND_CAR", - "type" : "line", "source" : "vectorSource", "source-layer" : "edges", + "type" : "line", "minzoom" : 6, "maxzoom" : 23, "paint" : { "line-color" : "#e25f8f", - "line-width" : { - "base" : 1.3, - "stops" : [ - [ - 11, - 1.0 - ], - [ - 23, - 12.0 - ] - ] - }, - "line-offset" : { - "base" : 1.3, - "stops" : [ - [ - 13, - 0.3 - ], - [ - 23, - 6.0 - ] - ] - } + "line-width" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.2, + 23, + 8.0 + ], + "line-offset" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.3, + 23, + 6.0 + ] }, "filter" : [ "==", @@ -323,39 +323,39 @@ }, { "id" : "BICYCLE_AND_CAR", - "type" : "line", "source" : "vectorSource", "source-layer" : "edges", + "type" : "line", "minzoom" : 6, "maxzoom" : 23, "paint" : { "line-color" : "#e25f8f", - "line-width" : { - "base" : 1.3, - "stops" : [ - [ - 11, - 1.0 - ], - [ - 23, - 12.0 - ] - ] - }, - "line-offset" : { - "base" : 1.3, - "stops" : [ - [ - 13, - 0.3 - ], - [ - 23, - 6.0 - ] - ] - } + "line-width" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.2, + 23, + 8.0 + ], + "line-offset" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.3, + 23, + 6.0 + ] }, "filter" : [ "==", @@ -372,39 +372,39 @@ }, { "id" : "ALL", - "type" : "line", "source" : "vectorSource", "source-layer" : "edges", + "type" : "line", "minzoom" : 6, "maxzoom" : 23, "paint" : { "line-color" : "#adb2b0", - "line-width" : { - "base" : 1.3, - "stops" : [ - [ - 11, - 1.0 - ], - [ - 23, - 12.0 - ] - ] - }, - "line-offset" : { - "base" : 1.3, - "stops" : [ - [ - 13, - 0.3 - ], - [ - 23, - 6.0 - ] - ] - } + "line-width" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.2, + 23, + 8.0 + ], + "line-offset" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.3, + 23, + 6.0 + ] }, "filter" : [ "==", @@ -421,29 +421,16 @@ }, { "id" : "permission-text", - "type" : "symbol", "source" : "vectorSource", "source-layer" : "edges", + "type" : "symbol", "minzoom" : 17, "maxzoom" : 23, "paint" : { "text-color" : "#000", "text-halo-color" : "#fff", "text-halo-blur" : 4, - "text-halo-width" : 3, - "line-offset" : { - "base" : 1.3, - "stops" : [ - [ - 13, - 0.3 - ], - [ - 23, - 6.0 - ] - ] - } + "text-halo-width" : 3 }, "filter" : [ "in", @@ -457,28 +444,33 @@ "TemporaryFreeEdge" ], "layout" : { - "symbol-placement" : "line", - "symbol-spacing" : 500, + "symbol-placement" : "line-center", + "symbol-spacing" : 1000, "text-field" : "{permission}", "text-font" : [ "KlokanTech Noto Sans Regular" ], - "text-size" : { - "base" : 14.0, - "stops" : [ - [ - 14, - 12.0 - ], - [ - 20, - 14.0 - ] - ] - }, - "text-max-width" : 5, + "text-size" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 10, + 6.0, + 24, + 12.0 + ], + "text-max-width" : 100, "text-keep-upright" : true, "text-rotation-alignment" : "map", + "text-overlap" : "never", + "text-offset" : [ + 0, + 1.0 + ], "visibility" : "none" }, "metadata" : { @@ -494,32 +486,32 @@ "maxzoom" : 23, "paint" : { "line-color" : "#f21d52", - "line-width" : { - "base" : 1.3, - "stops" : [ - [ - 13, - 0.5 - ], - [ - 23, - 10.0 - ] - ] - }, - "line-offset" : { - "base" : 1.3, - "stops" : [ - [ - 13, - 0.3 - ], - [ - 23, - 6.0 - ] - ] - } + "line-width" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.2, + 23, + 8.0 + ], + "line-offset" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.3, + 23, + 6.0 + ] }, "filter" : [ "in", @@ -565,28 +557,29 @@ "TemporaryFreeEdge" ], "layout" : { - "symbol-placement" : "line", - "symbol-spacing" : 500, + "symbol-placement" : "line-center", + "symbol-spacing" : 1000, "text-field" : "{name}", "text-font" : [ "KlokanTech Noto Sans Regular" ], - "text-size" : { - "base" : 14.0, - "stops" : [ - [ - 14, - 12.0 - ], - [ - 20, - 14.0 - ] - ] - }, - "text-max-width" : 5, + "text-size" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 10, + 6.0, + 24, + 12.0 + ], + "text-max-width" : 100, "text-keep-upright" : true, "text-rotation-alignment" : "map", + "text-overlap" : "never", "visibility" : "none" }, "metadata" : { @@ -602,32 +595,32 @@ "maxzoom" : 23, "paint" : { "line-color" : "#22DD9E", - "line-width" : { - "base" : 1.3, - "stops" : [ - [ - 13, - 0.5 - ], - [ - 23, - 10.0 - ] - ] - }, - "line-offset" : { - "base" : 1.3, - "stops" : [ - [ - 13, - 0.3 - ], - [ - 23, - 6.0 - ] - ] - } + "line-width" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.2, + 23, + 8.0 + ], + "line-offset" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.3, + 23, + 6.0 + ] }, "filter" : [ "in", @@ -656,32 +649,32 @@ "maxzoom" : 23, "paint" : { "circle-stroke-color" : "#140d0e", - "circle-stroke-width" : { - "base" : 1.0, - "stops" : [ - [ - 15, - 0.2 - ], - [ - 23, - 3.0 - ] - ] - }, - "circle-radius" : { - "base" : 1.0, - "stops" : [ - [ - 15, - 1.0 - ], - [ - 23, - 7.0 - ] - ] - }, + "circle-stroke-width" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 15, + 0.2, + 23, + 3.0 + ], + "circle-radius" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 15, + 1.0, + 23, + 7.0 + ], "circle-color" : "#BC55F2" }, "layout" : { @@ -700,32 +693,32 @@ "maxzoom" : 23, "paint" : { "circle-stroke-color" : "#140d0e", - "circle-stroke-width" : { - "base" : 1.0, - "stops" : [ - [ - 15, - 0.2 - ], - [ - 23, - 3.0 - ] - ] - }, - "circle-radius" : { - "base" : 1.0, - "stops" : [ - [ - 13, - 1.4 - ], - [ - 23, - 10.0 - ] - ] - }, + "circle-stroke-width" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 15, + 0.2, + 23, + 3.0 + ], + "circle-radius" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 1.4, + 23, + 10.0 + ], "circle-color" : "#136b04" }, "filter" : [ @@ -781,32 +774,32 @@ "maxzoom" : 23, "paint" : { "circle-stroke-color" : "#140d0e", - "circle-stroke-width" : { - "base" : 1.0, - "stops" : [ - [ - 11, - 0.5 - ], - [ - 23, - 5.0 - ] - ] - }, - "circle-radius" : { - "base" : 1.0, - "stops" : [ - [ - 11, - 0.5 - ], - [ - 23, - 10.0 - ] - ] - }, + "circle-stroke-width" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 11, + 0.5, + 23, + 5.0 + ], + "circle-radius" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 11, + 0.5, + 23, + 10.0 + ], "circle-color" : "#fcf9fa" }, "metadata" : { From c9ed8ee724beaf787bf36c519d27ebf730ea4d81 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sat, 12 Oct 2024 08:48:52 +0200 Subject: [PATCH 12/18] Fix spelling --- client/src/components/MapView/LayerControl.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/components/MapView/LayerControl.tsx b/client/src/components/MapView/LayerControl.tsx index e85ecf64135..26e81834cf7 100644 --- a/client/src/components/MapView/LayerControl.tsx +++ b/client/src/components/MapView/LayerControl.tsx @@ -73,7 +73,7 @@ class LayerControl implements IControl { const g = groups.get(groupName); g?.appendChild(layerDiv); } else { - const groupDiv = this.buildgGroupDiv(groupName, layerDiv); + const groupDiv = this.buildGroupDiv(groupName, layerDiv); groups.set(groupName, groupDiv); this.container.appendChild(groupDiv); } @@ -84,7 +84,7 @@ class LayerControl implements IControl { return this.container; } - private buildgGroupDiv(groupName: string, layerDiv: HTMLDivElement) { + private buildGroupDiv(groupName: string, layerDiv: HTMLDivElement) { const groupDiv = document.createElement('div'); groupDiv.className = 'group'; From a27bbfd1cbdd2312fde650cf15ad7d4ffd3ea058 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sat, 12 Oct 2024 08:54:14 +0200 Subject: [PATCH 13/18] Fix schema path --- client/codegen.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/codegen.ts b/client/codegen.ts index b5b68a0650a..60299a21a97 100644 --- a/client/codegen.ts +++ b/client/codegen.ts @@ -2,7 +2,7 @@ import type { CodegenConfig } from '@graphql-codegen/cli'; const config: CodegenConfig = { overwrite: true, - schema: '../src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql', + schema: '../application/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql', documents: 'src/**/*.{ts,tsx}', generates: { 'src/gql/': { From 522fdf8f5e05597d901aab5099effdc2b8c59fbe Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 14 Oct 2024 10:42:38 +0200 Subject: [PATCH 14/18] Apply review suggestions --- .../src/components/MapView/LayerControl.tsx | 54 ++++++++++--------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/client/src/components/MapView/LayerControl.tsx b/client/src/components/MapView/LayerControl.tsx index 26e81834cf7..536723fcd8f 100644 --- a/client/src/components/MapView/LayerControl.tsx +++ b/client/src/components/MapView/LayerControl.tsx @@ -1,6 +1,6 @@ import type { ControlPosition } from 'react-map-gl'; import { useControl } from 'react-map-gl'; -import { IControl, Map as WebMap } from 'maplibre-gl'; +import { IControl, Map as WebMap, TypedStyleLayer } from 'maplibre-gl'; type LayerControlProps = { position: ControlPosition; @@ -46,32 +46,10 @@ class LayerControl implements IControl { groupName = meta.group; } - const layerDiv = document.createElement('div'); - layerDiv.className = 'layer'; - const input = document.createElement('input'); - input.type = 'checkbox'; - input.value = layer.id; - input.id = layer.id; - input.onchange = (e) => { - e.preventDefault(); - e.stopPropagation(); - if (input.checked) { - map.setLayoutProperty(layer.id, 'visibility', 'visible'); - } else { - map.setLayoutProperty(layer.id, 'visibility', 'none'); - } - }; - input.checked = this.layerVisible(map, layer); - input.className = 'layer'; - const label = document.createElement('label'); - label.textContent = layer.id; - label.htmlFor = layer.id; - layerDiv.appendChild(input); - layerDiv.appendChild(label); + const layerDiv = this.buildLayerDiv(layer as TypedStyleLayer, map); if (groups.has(groupName)) { - const g = groups.get(groupName); - g?.appendChild(layerDiv); + groups.get(groupName)?.appendChild(layerDiv); } else { const groupDiv = this.buildGroupDiv(groupName, layerDiv); groups.set(groupName, groupDiv); @@ -84,6 +62,32 @@ class LayerControl implements IControl { return this.container; } + private buildLayerDiv(layer: TypedStyleLayer, map: WebMap) { + const layerDiv = document.createElement('div'); + layerDiv.className = 'layer'; + const input = document.createElement('input'); + input.type = 'checkbox'; + input.value = layer.id; + input.id = layer.id; + input.onchange = (e) => { + e.preventDefault(); + e.stopPropagation(); + if (input.checked) { + map.setLayoutProperty(layer.id, 'visibility', 'visible'); + } else { + map.setLayoutProperty(layer.id, 'visibility', 'none'); + } + }; + input.checked = this.layerVisible(map, layer); + input.className = 'layer'; + const label = document.createElement('label'); + label.textContent = layer.id; + label.htmlFor = layer.id; + layerDiv.appendChild(input); + layerDiv.appendChild(label); + return layerDiv; + } + private buildGroupDiv(groupName: string, layerDiv: HTMLDivElement) { const groupDiv = document.createElement('div'); groupDiv.className = 'group'; From 65272ba55b47cc26d807ae992176ff6cffc13736 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 14 Oct 2024 10:59:54 +0200 Subject: [PATCH 15/18] Convert Map to Record --- client/src/components/MapView/LayerControl.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/client/src/components/MapView/LayerControl.tsx b/client/src/components/MapView/LayerControl.tsx index 536723fcd8f..4c3f81dfe36 100644 --- a/client/src/components/MapView/LayerControl.tsx +++ b/client/src/components/MapView/LayerControl.tsx @@ -1,6 +1,6 @@ import type { ControlPosition } from 'react-map-gl'; import { useControl } from 'react-map-gl'; -import { IControl, Map as WebMap, TypedStyleLayer } from 'maplibre-gl'; +import { IControl, Map, TypedStyleLayer } from 'maplibre-gl'; type LayerControlProps = { position: ControlPosition; @@ -15,7 +15,7 @@ type LayerControlProps = { class LayerControl implements IControl { private readonly container: HTMLDivElement = document.createElement('div'); - onAdd(map: WebMap) { + onAdd(map: Map) { this.container.className = 'maplibregl-ctrl maplibregl-ctrl-group layer-select'; map.on('load', () => { @@ -28,7 +28,7 @@ class LayerControl implements IControl { title.textContent = 'Debug layers'; this.container.appendChild(title); - const groups: Map = new Map(); + const groups: Record = {}; map .getLayersOrder() .map((l) => map.getLayer(l)) @@ -48,11 +48,11 @@ class LayerControl implements IControl { const layerDiv = this.buildLayerDiv(layer as TypedStyleLayer, map); - if (groups.has(groupName)) { - groups.get(groupName)?.appendChild(layerDiv); + if (groups.hasOwnProperty(groupName)) { + groups[groupName]?.appendChild(layerDiv); } else { const groupDiv = this.buildGroupDiv(groupName, layerDiv); - groups.set(groupName, groupDiv); + groups[groupName] = groupDiv; this.container.appendChild(groupDiv); } } @@ -62,7 +62,7 @@ class LayerControl implements IControl { return this.container; } - private buildLayerDiv(layer: TypedStyleLayer, map: WebMap) { + private buildLayerDiv(layer: TypedStyleLayer, map: Map) { const layerDiv = document.createElement('div'); layerDiv.className = 'layer'; const input = document.createElement('input'); @@ -115,7 +115,7 @@ class LayerControl implements IControl { return groupDiv; } - private layerVisible(map: WebMap, layer: { id: string }) { + private layerVisible(map: Map, layer: { id: string }) { return map.getLayoutProperty(layer.id, 'visibility') !== 'none'; } From b05658db28be226bab99b17402733bc3a344961b Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 14 Oct 2024 11:03:48 +0200 Subject: [PATCH 16/18] Fix lint issues --- client/src/components/MapView/LayerControl.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/MapView/LayerControl.tsx b/client/src/components/MapView/LayerControl.tsx index 4c3f81dfe36..b5c53ddd6c5 100644 --- a/client/src/components/MapView/LayerControl.tsx +++ b/client/src/components/MapView/LayerControl.tsx @@ -48,7 +48,7 @@ class LayerControl implements IControl { const layerDiv = this.buildLayerDiv(layer as TypedStyleLayer, map); - if (groups.hasOwnProperty(groupName)) { + if (groups[groupName]) { groups[groupName]?.appendChild(layerDiv); } else { const groupDiv = this.buildGroupDiv(groupName, layerDiv); From 79bb50742370af8aedf2c1bdb3e66992f49f8030 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 14 Oct 2024 12:11:50 +0200 Subject: [PATCH 17/18] Call method on correct variable --- client/src/components/MapView/LayerControl.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/MapView/LayerControl.tsx b/client/src/components/MapView/LayerControl.tsx index b5c53ddd6c5..3b7c239747a 100644 --- a/client/src/components/MapView/LayerControl.tsx +++ b/client/src/components/MapView/LayerControl.tsx @@ -98,7 +98,7 @@ class LayerControl implements IControl { const input = i as HTMLInputElement; input.checked = groupInput.checked; const event = new Event('change'); - i.dispatchEvent(event); + input.dispatchEvent(event); }); }; groupInput.type = 'checkbox'; From ead941e8d1bbd8144c9fc37bb5c8e92bde20520d Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 14 Oct 2024 12:20:09 +0200 Subject: [PATCH 18/18] Properly apply type cast --- client/src/components/MapView/LayerControl.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/src/components/MapView/LayerControl.tsx b/client/src/components/MapView/LayerControl.tsx index 3b7c239747a..d6be2d641d7 100644 --- a/client/src/components/MapView/LayerControl.tsx +++ b/client/src/components/MapView/LayerControl.tsx @@ -94,8 +94,7 @@ class LayerControl implements IControl { const groupInput = document.createElement('input'); groupInput.onchange = () => { - groupDiv.querySelectorAll('input.layer').forEach((i) => { - const input = i as HTMLInputElement; + groupDiv.querySelectorAll('input.layer').forEach((input) => { input.checked = groupInput.checked; const event = new Event('change'); input.dispatchEvent(event);