Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.opentripplanner.street.model.edge.StreetVehicleParkingLink;
import org.opentripplanner.street.model.edge.TemporaryFreeEdge;
import org.opentripplanner.street.model.edge.TemporaryPartialStreetEdge;
import org.opentripplanner.street.model.vertex.ElevatorVertex;
import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex;
import org.opentripplanner.utils.collection.ListUtils;

Expand Down Expand Up @@ -118,6 +119,7 @@ public class DebugStyleSpec {
private static final String RENTAL_GROUP = "Rental";
private static final String PERMISSIONS_GROUP = "Permissions";
private static final String NO_THRU_TRAFFIC_GROUP = "No-thru traffic";
private static final String ELEVATORS_GROUP = "Elevators";

private static final StreetTraversalPermission[] streetModes = new StreetTraversalPermission[] {
StreetTraversalPermission.PEDESTRIAN,
Expand Down Expand Up @@ -173,6 +175,7 @@ static StyleSpec build(
traversalPermissions(edges),
edges(edges),
elevation(edges, vertices),
elevators(edges, vertices),
vertices(vertices),
stops(regularStops, areaStops, groupStops)
)
Expand Down Expand Up @@ -260,6 +263,35 @@ private static List<StyleBuilder> vertices(VectorSourceLayer vertices) {
);
}

private static List<StyleBuilder> elevators(VectorSourceLayer edges, VectorSourceLayer vertices) {
return List.of(
StyleBuilder.ofId("elevator-hop-edge")
.group(ELEVATORS_GROUP)
.typeLine()
.vectorSourceLayer(edges)
.edgeFilter(ElevatorHopEdge.class)
.lineColor(ORANGE)
.lineWidth(LINE_WIDTH)
.lineOffset(LINE_OFFSET)
.minZoom(6)
.maxZoom(MAX_ZOOM)
.intiallyHidden(),
StyleBuilder.ofId("elevator-vertex")
.group(ELEVATORS_GROUP)
.typeCircle()
.vectorSourceLayer(vertices)
.vertexFilter(ElevatorVertex.class)
.circleStroke(BLACK, CIRCLE_STROKE)
.circleRadius(
new ZoomDependentNumber(List.of(new ZoomStop(15, 1), new ZoomStop(MAX_ZOOM, 7)))
)
.circleColor(ORANGE)
.minZoom(15)
.maxZoom(MAX_ZOOM)
.intiallyHidden()
);
}

private static List<StyleBuilder> rental(
VectorSourceLayer rentalLayer,
VectorSourceLayer geofencingZones
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ protected List<Geometry> getGeometries(Envelope query) {
return graph
.findEdges(query)
.stream()
.filter(e -> e.getGeometry() != null)
.filter(e -> e.getGeometryForDebugUi() != null)
.map(edge -> {
Geometry geometry = edge.getGeometry();
Geometry geometry = edge.getGeometryForDebugUi();
geometry.setUserData(edge);
return geometry;
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
import static org.opentripplanner.utils.lang.DoubleUtils.roundTo2Decimals;

import com.google.common.collect.Lists;
import java.time.Duration;
import java.util.Collection;
import java.util.List;
import org.opentripplanner.apis.support.mapping.PropertyMapper;
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.ElevatorHopEdge;
import org.opentripplanner.street.model.edge.EscalatorEdge;
import org.opentripplanner.street.model.edge.StreetEdge;
import org.opentripplanner.utils.collection.ListUtils;
Expand All @@ -24,7 +26,13 @@ protected Collection<KeyValue> map(Edge input) {
case StreetEdge e -> mapStreetEdge(e);
case EscalatorEdge e -> List.of(
kv("distance", e.getDistanceMeters()),
kv("duration", e.getDuration().map(d -> d.toString()).orElse(null))
kv("duration", e.getDuration().map(Duration::toString).orElse(null))
);
case ElevatorHopEdge e -> List.of(
kv("permission", e.getPermission()),
kv("levels", e.getLevels()),
kv("wheelchairAccessible", e.isWheelchairAccessible()),
kv("travelTime", e.getTravelTime())
Copy link
Member

@optionsome optionsome Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default value for the travel time is 0 which means that it's not actually being used. Maybe it would be less confusing to not expose this field for the edge if it's 0 or expose it as null as otherwise it leads to confusion.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mapping zero to null can be a problem if the value from OSM is zero. Having a default value of -1 is also not good imo because it would represent traveling back in time. Maybe travelTime could be defined as a Nullable Duration instead of an int. We could also just leave it as it is and expect the user to be able to look up what the value represents. What does @leonardehrenfried think of this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method is only used in tests and now here, so we don't have to adjust any calling code. I would indeed return Optional<Duration> and an empty optional in the 0 case.

);
default -> List.of();
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.opentripplanner.service.vehiclerental.street.VehicleRentalPlaceVertex;
import org.opentripplanner.street.model.edge.StreetEdge;
import org.opentripplanner.street.model.vertex.BarrierVertex;
import org.opentripplanner.street.model.vertex.ElevatorVertex;
import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex;
import org.opentripplanner.street.model.vertex.Vertex;
import org.opentripplanner.street.search.TraverseMode;
Expand All @@ -39,6 +40,7 @@ protected Collection<KeyValue> map(Vertex input) {
kColl("spacesFor", spacesFor(v.getVehicleParking())),
kColl("traversalPermission", traversalPermissions(v.getParkingEntrance()))
);
case ElevatorVertex v -> List.of(kv("level", v.getLevel()));
default -> List.of();
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ public LineString getGeometry() {
return null;
}

public LineString getGeometryForDebugUi() {
return this.getGeometry();
}

public boolean hasGeometry() {
return getGeometry() != null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package org.opentripplanner.street.model.edge;

import java.util.Arrays;
import java.util.List;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.LineString;
import org.opentripplanner.framework.geometry.GeometryUtils;
import org.opentripplanner.framework.i18n.I18NString;
import org.opentripplanner.routing.api.request.preference.RoutingPreferences;
import org.opentripplanner.street.model.StreetTraversalPermission;
Expand Down Expand Up @@ -98,6 +103,25 @@ public StreetTraversalPermission getPermission() {
return permission;
}

@Override
public LineString getGeometryForDebugUi() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like us to think if we consider how the edge should look like in the debug UI to be part of its domain model. If it is, then this code can stay here. It it isn't we should create a new mapper inside the inspector package.

But I generally agree that there can be debug-specific geometries.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be easily moved into inspector.vector.edge.EdgeLayerBuilder where it is called from and have a mapper there. I'm not sure which is better.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think @t2gran suggested this current solution (although I'm not sure if he gave any name suggestions for the method name).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay then, lets call it getDebugGeometry and keep it here. Please add a bit of Javadoc about what it is.

// If coordinates are equal, move the other one slightly to make the edge visible
if (fromv.getCoordinate().equals(tov.getCoordinate())) {
Coordinate newTo = tov.getCoordinate().copy();
newTo.setX(tov.getX() + 0.000001);
return GeometryUtils.makeLineString(Arrays.asList(fromv.getCoordinate(), newTo));
}
List<Coordinate> segmentCoordinates = Arrays.asList(fromv.getCoordinate(), tov.getCoordinate());
return GeometryUtils.makeLineString(segmentCoordinates);
}

/**
* The number of levels that the elevator travels
*/
public double getLevels() {
return levels;
}

public int getTravelTime() {
return travelTime;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ public ElevatorVertex(Vertex sourceVertex, String label, @Nullable String level)
this.label = label;
}

/**
* OSM level name or "{elevatorWay osm id} / {node index}"
*/
public String getLevel() {
return level;
}

@Override
public VertexLabel getLabel() {
return VertexLabel.string(LABEL_TEMPLATE.formatted(label, level));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1586,6 +1586,104 @@
"group" : "Elevation"
}
},
{
"id" : "elevator-hop-edge",
"type" : "line",
"source" : "vectorSource",
"source-layer" : "edges",
"minzoom" : 6,
"maxzoom" : 23,
"paint" : {
"line-color" : "#ffa500",
"line-width" : [
"interpolate",
[
"linear"
],
[
"zoom"
],
13,
0.2,
23,
8.0
],
"line-offset" : [
"interpolate",
[
"linear"
],
[
"zoom"
],
13,
0.4,
23,
7.0
]
},
"filter" : [
"in",
"class",
"ElevatorHopEdge"
],
"layout" : {
"line-cap" : "round",
"visibility" : "none"
},
"metadata" : {
"group" : "Elevators"
}
},
{
"id" : "elevator-vertex",
"type" : "circle",
"source" : "vectorSource",
"source-layer" : "vertices",
"minzoom" : 15,
"maxzoom" : 23,
"paint" : {
"circle-stroke-color" : "#140d0e",
"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" : "#ffa500"
},
"filter" : [
"in",
"class",
"ElevatorVertex"
],
"layout" : {
"visibility" : "none"
},
"metadata" : {
"group" : "Elevators"
}
},
{
"id" : "vertex",
"type" : "circle",
Expand Down
Loading