|
4 | 4 | import static org.opentripplanner.street.model.edge.LinkingDirection.BIDIRECTIONAL; |
5 | 5 | import static org.opentripplanner.transit.model._data.FeedScopedIdForTestFactory.id; |
6 | 6 |
|
| 7 | +import java.text.DecimalFormat; |
| 8 | +import java.text.DecimalFormatSymbols; |
7 | 9 | import java.util.List; |
8 | 10 | import java.util.Set; |
9 | 11 | import org.junit.jupiter.api.Test; |
10 | 12 | import org.opentripplanner.core.model.id.FeedScopedId; |
11 | 13 | import org.opentripplanner.framework.application.OTPFeature; |
12 | 14 | import org.opentripplanner.routing.graph.Graph; |
13 | 15 | import org.opentripplanner.street.model.StreetModelForTest; |
| 16 | +import org.opentripplanner.street.model.StreetTraversalPermission; |
14 | 17 | import org.opentripplanner.street.model.edge.StreetEdge; |
| 18 | +import org.opentripplanner.street.model.edge.TemporaryPartialStreetEdge; |
15 | 19 | import org.opentripplanner.street.model.vertex.IntersectionVertex; |
16 | 20 | import org.opentripplanner.street.model.vertex.SplitterVertex; |
17 | 21 | import org.opentripplanner.street.model.vertex.StreetVertex; |
| 22 | +import org.opentripplanner.street.model.vertex.Vertex; |
18 | 23 | import org.opentripplanner.street.search.TraverseModeSet; |
19 | 24 |
|
20 | 25 | class VertexLinkerTest { |
21 | 26 |
|
22 | 27 | public static final FeedScopedId AREA_STOP_1 = id("area-stop-1"); |
23 | 28 | public static final FeedScopedId AREA_STOP_2 = id("area-stop-2"); |
| 29 | + public static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.######"); |
| 30 | + public static final DecimalFormatSymbols SYMBOLS = DECIMAL_FORMAT.getDecimalFormatSymbols(); |
| 31 | + |
| 32 | + { |
| 33 | + SYMBOLS.setDecimalSeparator('.'); |
| 34 | + DECIMAL_FORMAT.setDecimalFormatSymbols(SYMBOLS); |
| 35 | + } |
24 | 36 |
|
25 | 37 | @Test |
26 | 38 | void flex() { |
@@ -108,6 +120,95 @@ void splitRealtime() { |
108 | 120 | assertThat(model.graph().getEdgesOfType(StreetEdge.class)).hasSize(1); |
109 | 121 | } |
110 | 122 |
|
| 123 | + @Test |
| 124 | + void multiModeLinking() { |
| 125 | + // test model has 3 parallel horizontal edges, of which uppermost allows car driving |
| 126 | + IntersectionVertex[] vertices = { |
| 127 | + StreetModelForTest.intersectionVertex(0.0, 0.0), |
| 128 | + StreetModelForTest.intersectionVertex(0.01, 0.0), |
| 129 | + StreetModelForTest.intersectionVertex(0.0, 0.0001), |
| 130 | + StreetModelForTest.intersectionVertex(0.01, 0.0001), |
| 131 | + StreetModelForTest.intersectionVertex(0.0, 0.0002), |
| 132 | + StreetModelForTest.intersectionVertex(0.01, 0.0002), |
| 133 | + }; |
| 134 | + |
| 135 | + var walkEdge1 = StreetModelForTest.streetEdge( |
| 136 | + vertices[0], |
| 137 | + vertices[1], |
| 138 | + 0.01, |
| 139 | + StreetTraversalPermission.PEDESTRIAN |
| 140 | + ); |
| 141 | + var walkEdge2 = StreetModelForTest.streetEdge( |
| 142 | + vertices[2], |
| 143 | + vertices[3], |
| 144 | + 0.01, |
| 145 | + StreetTraversalPermission.PEDESTRIAN |
| 146 | + ); |
| 147 | + var carEdge = StreetModelForTest.streetEdge( |
| 148 | + vertices[4], |
| 149 | + vertices[5], |
| 150 | + 0.01, |
| 151 | + StreetTraversalPermission.CAR |
| 152 | + ); |
| 153 | + |
| 154 | + // link point below all edges, in the middle |
| 155 | + var split = StreetModelForTest.intersectionVertex(0.005, -0.0001); |
| 156 | + |
| 157 | + var g = new Graph(); |
| 158 | + for (IntersectionVertex vertex : vertices) { |
| 159 | + g.addVertex(vertex); |
| 160 | + } |
| 161 | + g.index(); |
| 162 | + g.insert(walkEdge1, Scope.PERMANENT); |
| 163 | + g.insert(walkEdge2, Scope.PERMANENT); |
| 164 | + g.insert(carEdge, Scope.PERMANENT); |
| 165 | + assertThat(g.getEdgesOfType(StreetEdge.class)).hasSize(3); |
| 166 | + var linker = VertexLinkerTestFactory.of(g); |
| 167 | + var temp = linker.linkVertexForRequest( |
| 168 | + split, |
| 169 | + TraverseModeSet.allModes(), |
| 170 | + BIDIRECTIONAL, |
| 171 | + (v1, v2) -> List.of() |
| 172 | + ); |
| 173 | + // vertex is linked to closest walk edge and to the car edge, not to all 3 edges |
| 174 | + assertThat(summarizeLinks(g)).containsExactly( |
| 175 | + "(0,0) → (0.005,0) PEDESTRIAN ♿✅", |
| 176 | + "(0,0.0002) → (0.005,0.0002) CAR ♿✅" |
| 177 | + ); |
| 178 | + temp.disposeEdges(); |
| 179 | + assertThat(summarizeLinks(g)).isEmpty(); |
| 180 | + } |
| 181 | + |
| 182 | + private static List<String> summarizeLinks(Graph graph) { |
| 183 | + return graph |
| 184 | + .getEdgesOfType(TemporaryPartialStreetEdge.class) |
| 185 | + .stream() |
| 186 | + .map(e -> |
| 187 | + String.format( |
| 188 | + "%s → %s %s ♿%s", |
| 189 | + summarizeVertex(e.getFromVertex()), |
| 190 | + summarizeVertex(e.getToVertex()), |
| 191 | + e.getPermission(), |
| 192 | + summarizeBoolean(e.isWheelchairAccessible()) |
| 193 | + ) |
| 194 | + ) |
| 195 | + .toList(); |
| 196 | + } |
| 197 | + |
| 198 | + private static String summarizeBoolean(boolean b) { |
| 199 | + if (b) { |
| 200 | + return "✅"; |
| 201 | + } else { |
| 202 | + return "❌"; |
| 203 | + } |
| 204 | + } |
| 205 | + |
| 206 | + private static String summarizeVertex(Vertex e) { |
| 207 | + return String.format( |
| 208 | + "(%s,%s)".formatted(DECIMAL_FORMAT.format(e.getLat()), DECIMAL_FORMAT.format(e.getLon())) |
| 209 | + ); |
| 210 | + } |
| 211 | + |
111 | 212 | private static TestModel buildModel() { |
112 | 213 | var v1 = StreetModelForTest.intersectionVertex(0.0, 0.0); |
113 | 214 | var v2 = StreetModelForTest.intersectionVertex(0.1, 0.1); |
|
0 commit comments