OpenTripPlanner/application/src/test/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinderTest.java
2026-02-25 08:24:06 +01:00

201 lines
7 KiB
Java

package org.opentripplanner.graph_builder.module.nearbystops;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import java.time.Duration;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.opentripplanner.routing.algorithm.GraphRoutingTest;
import org.opentripplanner.routing.api.request.RouteRequest;
import org.opentripplanner.routing.graphfinder.NearbyStop;
import org.opentripplanner.street.geometry.WgsCoordinate;
import org.opentripplanner.street.model.StreetMode;
import org.opentripplanner.street.model.vertex.TransitStopVertex;
import org.opentripplanner.street.model.vertex.Vertex;
class StreetNearbyStopFinderTest extends GraphRoutingTest {
private static final WgsCoordinate ORIGIN = new WgsCoordinate(0.0, 0.0);
private TransitStopVertex isolatedStop;
private TransitStopVertex stopA;
private TransitStopVertex stopB;
private TransitStopVertex stopC;
private TransitStopVertex stopD;
private StopResolver stopResolver;
@BeforeEach
protected void setUp() throws Exception {
var model = modelOf(
new GraphRoutingTest.Builder() {
@Override
public void build() {
var isolated = intersection("isolated", ORIGIN.moveNorthMeters(1000));
var A = intersection("A", ORIGIN);
var B = intersection("B", ORIGIN.moveEastMeters(100));
var C = intersection("C", ORIGIN.moveEastMeters(200));
var D = intersection("D", ORIGIN.moveEastMeters(300));
biStreet(A, B, 100);
biStreet(B, C, 100);
biStreet(C, D, 100);
isolatedStop = stop("IsolatedStop", isolated.toWgsCoordinate());
stopA = stop("StopA", A.toWgsCoordinate());
stopB = stop("StopB", B.toWgsCoordinate());
stopC = stop("StopC", C.toWgsCoordinate());
stopD = stop("StopD", D.toWgsCoordinate());
biLink(A, stopA);
biLink(B, stopB);
biLink(C, stopC);
biLink(D, stopD);
}
}
);
this.stopResolver = new SiteRepositoryResolver(model.timetableRepository().getSiteRepository());
}
@Test
void testIsolatedStop() {
var durationLimit = Duration.ofMinutes(10);
var maxStopCount = 0;
var finder = StreetNearbyStopFinder.of(stopResolver, durationLimit, maxStopCount).build();
var nearbyStops = finder.findNearbyStops(
isolatedStop,
RouteRequest.defaultValue(),
StreetMode.WALK,
false
);
assertThat(nearbyStops).hasSize(1);
var nearbyStop = nearbyStops.stream().findFirst().get();
assertZeroDistanceStop(isolatedStop, nearbyStop);
}
@Test
void testMultipleStops() {
var durationLimit = Duration.ofMinutes(10);
var maxStopCount = 0;
var finder = StreetNearbyStopFinder.of(stopResolver, durationLimit, maxStopCount).build();
var sortedNearbyStops = sort(
finder.findNearbyStops(stopA, RouteRequest.defaultValue(), StreetMode.WALK, false)
);
assertThat(sortedNearbyStops).hasSize(4);
assertZeroDistanceStop(stopA, sortedNearbyStops.get(0));
assertStopAtDistance(stopB, 100, sortedNearbyStops.get(1));
assertStopAtDistance(stopC, 200, sortedNearbyStops.get(2));
assertStopAtDistance(stopD, 300, sortedNearbyStops.get(3));
}
@Test
void testMaxStopCount() {
var durationLimit = Duration.ofMinutes(10);
var maxStopCount = 2;
var finder = StreetNearbyStopFinder.of(stopResolver, durationLimit, maxStopCount).build();
var sortedNearbyStops = sort(
finder.findNearbyStops(stopA, RouteRequest.defaultValue(), StreetMode.WALK, false)
);
assertThat(sortedNearbyStops).hasSize(2);
assertZeroDistanceStop(stopA, sortedNearbyStops.get(0));
assertStopAtDistance(stopB, 100, sortedNearbyStops.get(1));
}
@Test
void testDurationLimit() {
// If we only allow walk for 101 seconds and speed is 1 m/s we should only be able to reach
// one extra stop.
var durationLimit = Duration.ofSeconds(101);
var maxStopCount = 0;
var routeRequest = RouteRequest.of()
.withPreferences(b -> b.withWalk(w -> w.withSpeed(1.0)))
.buildDefault();
var finder = StreetNearbyStopFinder.of(stopResolver, durationLimit, maxStopCount).build();
var sortedNearbyStops = sort(
finder.findNearbyStops(stopA, routeRequest, StreetMode.WALK, false)
);
assertThat(sortedNearbyStops).hasSize(2);
assertZeroDistanceStop(stopA, sortedNearbyStops.get(0));
assertStopAtDistance(stopB, 100, sortedNearbyStops.get(1));
}
@Test
void testIgnoreStops() {
var durationLimit = Duration.ofMinutes(10);
var maxStopCount = 0;
Set<Vertex> ignore = Set.of(stopA, stopB);
var finder = StreetNearbyStopFinder.of(stopResolver, durationLimit, maxStopCount)
.withIgnoreVertices(ignore)
.build();
var sortedNearbyStops = sort(
finder.findNearbyStops(Set.of(stopA), RouteRequest.defaultValue(), StreetMode.WALK, false)
);
assertThat(sortedNearbyStops).hasSize(2);
assertStopAtDistance(stopC, 200, sortedNearbyStops.get(0));
assertStopAtDistance(stopD, 300, sortedNearbyStops.get(1));
}
@Test
void testIgnoreStopsWithMaxStops() {
var durationLimit = Duration.ofMinutes(10);
var maxStopCount = 1;
Set<Vertex> ignore = Set.of(stopA, stopB);
var finder = StreetNearbyStopFinder.of(stopResolver, durationLimit, maxStopCount)
.withIgnoreVertices(ignore)
.build();
var sortedNearbyStops = sort(
finder.findNearbyStops(Set.of(stopA), RouteRequest.defaultValue(), StreetMode.WALK, false)
);
assertThat(sortedNearbyStops).hasSize(1);
assertStopAtDistance(stopC, 200, sortedNearbyStops.get(0));
}
static List<NearbyStop> sort(Collection<NearbyStop> stops) {
return stops.stream().sorted(Comparator.comparing(x -> x.distance)).toList();
}
/**
* Verify that the nearby stop is zero distance and corresponds to the expected vertex
*/
void assertZeroDistanceStop(TransitStopVertex expected, NearbyStop nearbyStop) {
assertEquals(stopResolver.getRegularStop(expected.getId()), nearbyStop.stop);
assertEquals(0, nearbyStop.distance);
assertEquals(0, nearbyStop.edges.size());
assertEquals(expected, nearbyStop.state.getVertex());
assertNull(nearbyStop.state.getBackState());
}
/**
* Verify that the nearby stop is at a specific distance and corresponds to the expected vertex
*/
void assertStopAtDistance(
TransitStopVertex expected,
double expectedDistance,
NearbyStop nearbyStop
) {
assertEquals(stopResolver.getRegularStop(expected.getId()), nearbyStop.stop);
assertEquals(expectedDistance, nearbyStop.distance);
assertEquals(expected, nearbyStop.state.getVertex());
assertFalse(nearbyStop.edges.isEmpty());
assertNotNull(nearbyStop.state.getBackState());
}
}