Skip to content

Commit

Permalink
Add EnumSet of StreetModes to PathTransfers and implement mode-specif…
Browse files Browse the repository at this point in the history
…ic transfers.
  • Loading branch information
VillePihlava committed Nov 29, 2024
1 parent aa4927e commit 4c882f4
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
import com.google.common.collect.Multimap;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.opentripplanner.ext.flex.trip.FlexTrip;
import org.opentripplanner.model.PathTransfer;
import org.opentripplanner.routing.api.request.StreetMode;
import org.opentripplanner.transit.model.framework.FeedScopedId;
import org.opentripplanner.transit.model.network.Route;
import org.opentripplanner.transit.model.site.GroupStop;
Expand All @@ -18,15 +20,21 @@ public class FlexIndex {

private final Multimap<StopLocation, PathTransfer> transfersToStop = ArrayListMultimap.create();

private final Multimap<StopLocation, PathTransfer> transfersFromStop = ArrayListMultimap.create();

private final Multimap<StopLocation, FlexTrip<?, ?>> flexTripsByStop = HashMultimap.create();

private final Map<FeedScopedId, Route> routeById = new HashMap<>();

private final Map<FeedScopedId, FlexTrip<?, ?>> tripById = new HashMap<>();

public FlexIndex(TimetableRepository timetableRepository) {
for (PathTransfer transfer : timetableRepository.getAllPathTransfers()) {
// Flex transfers should only use WALK mode transfers.
StreetMode mode = StreetMode.WALK;
List<PathTransfer> filteredPathTransfers = timetableRepository.getAllPathTransfers().stream().filter(pathTransfer -> pathTransfer.getModes().contains(mode)).toList();
for (PathTransfer transfer : filteredPathTransfers) {
transfersToStop.put(transfer.to, transfer);
transfersFromStop.put(transfer.from, transfer);
}
for (FlexTrip<?, ?> flexTrip : timetableRepository.getAllFlexTrips()) {
routeById.put(flexTrip.getTrip().getRoute().getId(), flexTrip.getTrip().getRoute());
Expand All @@ -47,6 +55,10 @@ public Collection<PathTransfer> getTransfersToStop(StopLocation stopLocation) {
return transfersToStop.get(stopLocation);
}

public Collection<PathTransfer> getTransfersFromStop(StopLocation stopLocation) {
return transfersFromStop.get(stopLocation);
}

public Collection<FlexTrip<?, ?>> getFlexTripsByStop(StopLocation stopLocation) {
return flexTripsByStop.get(stopLocation);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ public TransitStopVertex getStopVertexForStopId(FeedScopedId stopId) {

@Override
public Collection<PathTransfer> getTransfersFromStop(StopLocation stop) {
return transitService.findPathTransfers(stop);
return transitService.getFlexIndex().getTransfersFromStop(stop);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimaps;
import java.time.Duration;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import org.opentripplanner.framework.application.OTPFeature;
import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore;
Expand All @@ -17,6 +20,7 @@
import org.opentripplanner.graph_builder.module.nearbystops.StreetNearbyStopFinder;
import org.opentripplanner.model.PathTransfer;
import org.opentripplanner.routing.api.request.RouteRequest;
import org.opentripplanner.routing.api.request.StreetMode;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.routing.graphfinder.NearbyStop;
import org.opentripplanner.street.model.edge.Edge;
Expand Down Expand Up @@ -102,6 +106,7 @@ public void buildGraph() {
LOG.debug("Linking stop '{}' {}", stop, ts0);

for (RouteRequest transferProfile : transferRequests) {
StreetMode mode = transferProfile.journey().transfer().mode();
for (NearbyStop sd : nearbyStopFinder.findNearbyStops(
ts0,
transferProfile,
Expand All @@ -115,12 +120,12 @@ public void buildGraph() {
if (sd.stop.transfersNotAllowed()) {
continue;
}
distinctTransfers.put(
new TransferKey(stop, sd.stop, sd.edges),
new PathTransfer(stop, sd.stop, sd.distance, sd.edges)
);
createPathTransfer(distinctTransfers, sd, stop, mode);
}
if (OTPFeature.FlexRouting.isOn()) {
}
if (OTPFeature.FlexRouting.isOn()) {
for (RouteRequest transferProfile : transferRequests) {
StreetMode mode = transferProfile.journey().transfer().mode();
// This code is for finding transfers from AreaStops to Stops, transfers
// from Stops to AreaStops and between Stops are already covered above.
for (NearbyStop sd : nearbyStopFinder.findNearbyStops(
Expand All @@ -136,10 +141,7 @@ public void buildGraph() {
if (sd.stop instanceof RegularStop) {
continue;
}
distinctTransfers.put(
new TransferKey(sd.stop, stop, sd.edges),
new PathTransfer(sd.stop, stop, sd.distance, sd.edges)
);
createPathTransfer(distinctTransfers, sd, stop, mode);
}
}
}
Expand Down Expand Up @@ -174,6 +176,28 @@ public void buildGraph() {
);
}

/**
* Factory method for creating a PathTransfer.
*/
private void createPathTransfer(
Map<TransferKey, PathTransfer> distinctTransfers,
NearbyStop sd,
RegularStop stop,
StreetMode mode
) {
TransferKey transferKey = new TransferKey(stop, sd.stop, sd.edges);
PathTransfer pathTransfer = distinctTransfers.get(transferKey);
if (pathTransfer == null) {
EnumSet<StreetMode> modes = EnumSet.of(mode);
distinctTransfers.put(
transferKey,
new PathTransfer(stop, sd.stop, sd.distance, sd.edges, modes)
);
} else {
pathTransfer.addMode(mode);
}
}

/**
* Factory method for creating a NearbyStopFinder. Will create different finders depending on
* whether the graph has a street network and if ConsiderPatternsForDirectTransfers feature is
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
package org.opentripplanner.model;

import java.io.Serializable;
import java.util.EnumSet;
import java.util.List;
import org.opentripplanner.model.transfer.ConstrainedTransfer;
import org.opentripplanner.routing.api.request.StreetMode;
import org.opentripplanner.street.model.edge.Edge;
import org.opentripplanner.transit.model.site.StopLocation;
import org.opentripplanner.utils.tostring.ToStringBuilder;

/**
* Represents a transfer between stops with the street network path attatched to it.
* Represents a transfer for a set of modes between stops with the street network path attached to it.
* <p>
* Do not confuse this with {@link ConstrainedTransfer}.
*
* <p>
* TODO these should really have a set of valid modes in case bike vs. walk transfers are different
* TODO Should we just store the NearbyStop as a field here, or even switch to using it instead
* where this class is used
*/
Expand All @@ -27,11 +28,20 @@ public class PathTransfer implements Serializable {

private final List<Edge> edges;

public PathTransfer(StopLocation from, StopLocation to, double distanceMeters, List<Edge> edges) {
private EnumSet<StreetMode> modes;

public PathTransfer(
StopLocation from,
StopLocation to,
double distanceMeters,
List<Edge> edges,
EnumSet<StreetMode> modes
) {
this.from = from;
this.to = to;
this.distanceMeters = distanceMeters;
this.edges = edges;
this.modes = modes;
}

public String getName() {
Expand All @@ -46,6 +56,14 @@ public List<Edge> getEdges() {
return this.edges;
}

public EnumSet<StreetMode> getModes() {
return this.modes;
}

public boolean addMode(StreetMode mode) {
return this.modes.add(mode);
}

@Override
public String toString() {
return ToStringBuilder
Expand All @@ -54,6 +72,7 @@ public String toString() {
.addObj("to", to)
.addNum("distance", distanceMeters)
.addColSize("edges", edges)
.addColSize("modes", modes)
.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.List;
import java.util.function.Function;
import org.opentripplanner.raptor.api.model.RaptorTransfer;
import org.opentripplanner.routing.api.request.StreetMode;
import org.opentripplanner.street.search.request.StreetSearchRequest;

public class RaptorTransferIndex {
Expand All @@ -29,6 +30,7 @@ public static RaptorTransferIndex create(
) {
var forwardTransfers = new ArrayList<List<RaptorTransfer>>(transfersByStopIndex.size());
var reversedTransfers = new ArrayList<List<RaptorTransfer>>(transfersByStopIndex.size());
StreetMode mode = request.mode();

for (int i = 0; i < transfersByStopIndex.size(); i++) {
forwardTransfers.add(new ArrayList<>());
Expand All @@ -41,6 +43,7 @@ public static RaptorTransferIndex create(
var transfers = transfersByStopIndex
.get(fromStop)
.stream()
.filter(transfer -> transfer.getModes().contains(mode))
.flatMap(s -> s.asRaptorTransfer(request).stream())
.collect(
toMap(RaptorTransfer::stop, Function.identity(), (a, b) -> a.c1() < b.c1() ? a : b)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.locationtech.jts.geom.Coordinate;
import org.opentripplanner.raptor.api.model.RaptorCostConverter;
import org.opentripplanner.raptor.api.model.RaptorTransfer;
import org.opentripplanner.routing.api.request.StreetMode;
import org.opentripplanner.routing.api.request.preference.WalkPreferences;
import org.opentripplanner.street.model.edge.Edge;
import org.opentripplanner.street.search.request.StreetSearchRequest;
Expand All @@ -31,16 +32,20 @@ public class Transfer {

private final List<Edge> edges;

public Transfer(int toStop, List<Edge> edges) {
private final Set<StreetMode> modes;

public Transfer(int toStop, List<Edge> edges, Set<StreetMode> modes) {
this.toStop = toStop;
this.edges = edges;
this.distanceMeters = (int) edges.stream().mapToDouble(Edge::getDistanceMeters).sum();
this.modes = modes;
}

public Transfer(int toStopIndex, int distanceMeters) {
public Transfer(int toStopIndex, int distanceMeters, Set<StreetMode> modes) {
this.toStop = toStopIndex;
this.distanceMeters = distanceMeters;
this.edges = null;
this.modes = modes;
}

public List<Coordinate> getCoordinates() {
Expand Down Expand Up @@ -68,6 +73,10 @@ public List<Edge> getEdges() {
return edges;
}

public Set<StreetMode> getModes() {
return modes;
}

public Optional<RaptorTransfer> asRaptorTransfer(StreetSearchRequest request) {
WalkPreferences walkPreferences = request.preferences().walk();
if (edges == null || edges.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,15 @@ static List<List<Transfer>> mapTransfers(
int toStopIndex = pathTransfer.to.getIndex();
Transfer newTransfer;
if (pathTransfer.getEdges() != null) {
newTransfer = new Transfer(toStopIndex, pathTransfer.getEdges());
newTransfer =
new Transfer(toStopIndex, pathTransfer.getEdges(), pathTransfer.getModes());
} else {
newTransfer =
new Transfer(toStopIndex, (int) Math.ceil(pathTransfer.getDistanceMeters()));
new Transfer(
toStopIndex,
(int) Math.ceil(pathTransfer.getDistanceMeters()),
pathTransfer.getModes()
);
}

list.add(newTransfer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
Expand Down Expand Up @@ -54,6 +55,7 @@
import org.opentripplanner.routing.algorithm.raptoradapter.transit.TransitLayer;
import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.DefaultCostCalculator;
import org.opentripplanner.routing.api.request.RouteRequest;
import org.opentripplanner.routing.api.request.StreetMode;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.street.search.state.State;
import org.opentripplanner.street.search.state.TestStateBuilder;
Expand Down Expand Up @@ -199,7 +201,7 @@ void createItineraryWithOnBoardFlexAccess() {
flexAccessEgress,
AccessEgressType.ACCESS
);
Transfer transfer = new Transfer(S2.getIndex(), 0);
Transfer transfer = new Transfer(S2.getIndex(), 0, Set.of(StreetMode.WALK));
RaptorTransfer raptorTransfer = new DefaultRaptorTransfer(S1.getIndex(), 0, 0, transfer);
RaptorAccessEgress egress = new DefaultAccessEgress(S2.getIndex(), state);
PathLeg<RaptorTripSchedule> egressLeg = new EgressPathLeg<>(egress, 0, 0, 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@

import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.opentripplanner._support.geometry.Coordinates;
import org.opentripplanner.raptor.api.model.RaptorCostConverter;
import org.opentripplanner.raptor.api.model.RaptorTransfer;
import org.opentripplanner.routing.api.request.StreetMode;
import org.opentripplanner.street.model._data.StreetModelForTest;
import org.opentripplanner.street.model.vertex.IntersectionVertex;
import org.opentripplanner.street.search.request.StreetSearchRequest;
Expand All @@ -34,7 +36,7 @@ void limitMaxCost() {
// very long edge from Berlin to Boston that has of course a huge cost to traverse
var edge = StreetModelForTest.streetEdge(BERLIN_V, BOSTON_V);

var veryLongTransfer = new Transfer(0, List.of(edge));
var veryLongTransfer = new Transfer(0, List.of(edge), Set.of(StreetMode.WALK));
assertTrue(veryLongTransfer.getDistanceMeters() > 1_000_000);
// cost would be too high, so it should be capped to a maximum value
assertMaxCost(veryLongTransfer.asRaptorTransfer(StreetSearchRequest.of().build()).get());
Expand All @@ -43,7 +45,7 @@ void limitMaxCost() {
@Test
void allowLowCost() {
var edge = StreetModelForTest.streetEdge(BERLIN_V, BRANDENBURG_GATE_V);
var transfer = new Transfer(0, List.of(edge));
var transfer = new Transfer(0, List.of(edge), Set.of(StreetMode.WALK));
assertTrue(transfer.getDistanceMeters() < 4000);
final Optional<RaptorTransfer> raptorTransfer = transfer.asRaptorTransfer(
StreetSearchRequest.of().build()
Expand All @@ -58,26 +60,26 @@ class WithoutEdges {

@Test
void overflow() {
var veryLongTransfer = new Transfer(0, Integer.MAX_VALUE);
var veryLongTransfer = new Transfer(0, Integer.MAX_VALUE, Set.of(StreetMode.WALK));
assertMaxCost(veryLongTransfer.asRaptorTransfer(StreetSearchRequest.of().build()).get());
}

@Test
void negativeCost() {
var veryLongTransfer = new Transfer(0, -5);
var veryLongTransfer = new Transfer(0, -5, Set.of(StreetMode.WALK));
assertMaxCost(veryLongTransfer.asRaptorTransfer(StreetSearchRequest.of().build()).get());
}

@Test
void limitMaxCost() {
var veryLongTransfer = new Transfer(0, 8_000_000);
var veryLongTransfer = new Transfer(0, 8_000_000, Set.of(StreetMode.WALK));
// cost would be too high, so it will be capped before passing to RAPTOR
assertMaxCost(veryLongTransfer.asRaptorTransfer(StreetSearchRequest.of().build()).get());
}

@Test
void allowLowCost() {
var transfer = new Transfer(0, 200);
var transfer = new Transfer(0, 200, Set.of(StreetMode.WALK));
final Optional<RaptorTransfer> raptorTransfer = transfer.asRaptorTransfer(
StreetSearchRequest.of().build()
);
Expand Down

0 comments on commit 4c882f4

Please sign in to comment.