diff --git a/src/ext/java/org/opentripplanner/ext/flex/FlexAccessEgress.java b/src/ext/java/org/opentripplanner/ext/flex/FlexAccessEgress.java index 4a966b76d02..20b77154b90 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/FlexAccessEgress.java +++ b/src/ext/java/org/opentripplanner/ext/flex/FlexAccessEgress.java @@ -4,15 +4,15 @@ import org.opentripplanner.model.Stop; import org.opentripplanner.routing.core.State; -public class FlexAccessEgress { +public class FlexAccessEgress { public final Stop stop; public final int preFlexTime; public final int flexTime; public final int postFlexTime; - private final int fromStopIndex; - private final int toStopIndex; + private final T fromStopIndex; + private final T toStopIndex; private final int differenceFromStartOfTime; - private final FlexTrip trip; + private final FlexTrip trip; public final State lastState; public final boolean directToStop; @@ -21,10 +21,10 @@ public FlexAccessEgress( int preFlexTime, int flexTime, int postFlexTime, - int fromStopIndex, - int toStopIndex, + T fromStopIndex, + T toStopIndex, int differenceFromStartOfTime, - FlexTrip trip, + FlexTrip trip, State lastState, boolean directToStop ) { diff --git a/src/ext/java/org/opentripplanner/ext/flex/FlexIndex.java b/src/ext/java/org/opentripplanner/ext/flex/FlexIndex.java index e1c05946e1e..22878e405f4 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/FlexIndex.java +++ b/src/ext/java/org/opentripplanner/ext/flex/FlexIndex.java @@ -21,11 +21,11 @@ public class FlexIndex { public Multimap transfersToStop = ArrayListMultimap.create(); - public Multimap flexTripsByStop = HashMultimap.create(); + public Multimap> flexTripsByStop = HashMultimap.create(); public Multimap locationGroupsByStop = ArrayListMultimap.create(); - public HashGridSpatialIndex locationIndex = new HashGridSpatialIndex(); + public HashGridSpatialIndex locationIndex = new HashGridSpatialIndex<>(); public Map routeById = new HashMap<>(); @@ -35,7 +35,7 @@ public FlexIndex(Graph graph) { for (SimpleTransfer transfer : graph.transfersByStop.values()) { transfersToStop.put(transfer.to, transfer); } - for (FlexTrip flexTrip : graph.flexTripsById.values()) { + for (FlexTrip flexTrip : graph.flexTripsById.values()) { routeById.put(flexTrip.getTrip().getRoute().getId(), flexTrip.getTrip().getRoute()); tripById.put(flexTrip.getTrip().getId(), flexTrip.getTrip()); for (StopLocation stop : flexTrip.getStops()) { @@ -58,7 +58,7 @@ public FlexIndex(Graph graph) { } } - Stream getFlexTripsByStop(StopLocation stopLocation) { + Stream> getFlexTripsByStop(StopLocation stopLocation) { return flexTripsByStop.get(stopLocation).stream(); } } diff --git a/src/ext/java/org/opentripplanner/ext/flex/FlexLegMapper.java b/src/ext/java/org/opentripplanner/ext/flex/FlexLegMapper.java index a51c695dd98..b5b7865d4c4 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/FlexLegMapper.java +++ b/src/ext/java/org/opentripplanner/ext/flex/FlexLegMapper.java @@ -10,14 +10,18 @@ public class FlexLegMapper { - static public void fixFlexTripLeg(Leg leg, FlexTripEdge flexTripEdge) { + static public void fixFlexTripLeg(Leg leg, FlexTripEdge flexTripEdge) { leg.from.stopId = flexTripEdge.s1.getId(); // TODO: Should flex be of its own type leg.from.vertexType = flexTripEdge.s1 instanceof Stop ? VertexType.TRANSIT : VertexType.NORMAL; - leg.from.stopIndex = flexTripEdge.flexTemplate.fromStopIndex; + if (flexTripEdge.flexTemplate.fromStopIndex instanceof Integer) { + leg.from.stopIndex = (Integer) flexTripEdge.flexTemplate.fromStopIndex; + } leg.to.stopId = flexTripEdge.s2.getId(); leg.to.vertexType = flexTripEdge.s2 instanceof Stop ? VertexType.TRANSIT : VertexType.NORMAL; - leg.to.stopIndex = flexTripEdge.flexTemplate.toStopIndex; + if (flexTripEdge.flexTemplate.toStopIndex instanceof Integer) { + leg.to.stopIndex = (Integer) flexTripEdge.flexTemplate.toStopIndex; + } leg.intermediateStops = new ArrayList<>(); leg.distanceMeters = flexTripEdge.getDistanceMeters(); diff --git a/src/ext/java/org/opentripplanner/ext/flex/FlexRouter.java b/src/ext/java/org/opentripplanner/ext/flex/FlexRouter.java index 7bc9d07f987..b1503e835b2 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/FlexRouter.java +++ b/src/ext/java/org/opentripplanner/ext/flex/FlexRouter.java @@ -3,7 +3,6 @@ import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import org.opentripplanner.common.model.T2; -import org.opentripplanner.ext.flex.flexpathcalculator.DirectFlexPathCalculator; import org.opentripplanner.ext.flex.flexpathcalculator.FlexPathCalculator; import org.opentripplanner.ext.flex.flexpathcalculator.StreetFlexPathCalculator; import org.opentripplanner.ext.flex.template.FlexAccessTemplate; @@ -37,7 +36,7 @@ public class FlexRouter { private final Collection streetAccesses; private final Collection streetEgresses; private final FlexIndex flexIndex; - private final FlexPathCalculator flexPathCalculator; + private final FlexPathCalculator flexPathCalculator; /* Request data */ private final ZonedDateTime startOfTime; @@ -47,8 +46,8 @@ public class FlexRouter { private final FlexServiceDate[] dates; /* State */ - private List flexAccessTemplates = null; - private List flexEgressTemplates = null; + private List> flexAccessTemplates = null; + private List> flexEgressTemplates = null; public FlexRouter( Graph graph, @@ -98,7 +97,7 @@ public Collection createFlexOnlyItineraries() { Collection itineraries = new ArrayList<>(); - for (FlexAccessTemplate template : this.flexAccessTemplates) { + for (FlexAccessTemplate template : this.flexAccessTemplates) { StopLocation transferStop = template.getTransferStop(); if (egressStops.contains(transferStop)) { for(NearbyStop egress : streetEgressByStop.get(transferStop)) { @@ -113,7 +112,7 @@ public Collection createFlexOnlyItineraries() { return itineraries; } - public Collection createFlexAccesses() { + public Collection> createFlexAccesses() { calculateFlexAccessTemplates(); return this.flexAccessTemplates @@ -122,7 +121,7 @@ public Collection createFlexAccesses() { .collect(Collectors.toList()); } - public Collection createFlexEgresses() { + public Collection> createFlexEgresses() { calculateFlexEgressTemplates(); return this.flexEgressTemplates @@ -156,19 +155,19 @@ private void calculateFlexEgressTemplates() { .filter(date -> date.isFlexTripRunning(t2.second, this.graph)) // Create templates from trip, alighting at the nearbyStop .flatMap(date -> t2.second.getFlexEgressTemplates(t2.first, date, flexPathCalculator))) - .collect(Collectors.toList());; + .collect(Collectors.toList()); } - private Stream> getClosestFlexTrips(Collection nearbyStops) { + private Stream>> getClosestFlexTrips(Collection nearbyStops) { // Find all trips reachable from the nearbyStops - Stream> flexTripsReachableFromNearbyStops = nearbyStops + Stream>> flexTripsReachableFromNearbyStops = nearbyStops .stream() .flatMap(accessEgress -> flexIndex .getFlexTripsByStop(accessEgress.stop) .map(flexTrip -> new T2<>(accessEgress, flexTrip))); // Group all (NearbyStop, FlexTrip) tuples by flexTrip - Collection>> groupedReachableFlexTrips = flexTripsReachableFromNearbyStops + Collection>>> groupedReachableFlexTrips = flexTripsReachableFromNearbyStops .collect(Collectors.groupingBy(t2 -> t2.second)) .values(); diff --git a/src/ext/java/org/opentripplanner/ext/flex/FlexServiceDate.java b/src/ext/java/org/opentripplanner/ext/flex/FlexServiceDate.java index 9a20861167a..a26bce81742 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/FlexServiceDate.java +++ b/src/ext/java/org/opentripplanner/ext/flex/FlexServiceDate.java @@ -31,7 +31,7 @@ public class FlexServiceDate { this.servicesRunning = servicesRunning; } - boolean isFlexTripRunning(FlexTrip flexTrip, Graph graph) { + boolean isFlexTripRunning(FlexTrip flexTrip, Graph graph) { return servicesRunning != null && servicesRunning.contains(graph.getServiceCodes().get(flexTrip.getTrip().getServiceId())); } diff --git a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java index 6623508257d..ac026aa0cca 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java +++ b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java @@ -1,11 +1,14 @@ package org.opentripplanner.ext.flex; import org.opentripplanner.ext.flex.trip.FlexTrip; +import org.opentripplanner.ext.flex.trip.ContinuousPickupDropOffTrip; import org.opentripplanner.ext.flex.trip.ScheduledDeviatedTrip; import org.opentripplanner.ext.flex.trip.UnscheduledTrip; +import org.opentripplanner.graph_builder.module.map.StreetMatcher; import org.opentripplanner.model.StopTime; import org.opentripplanner.model.TripStopTimes; import org.opentripplanner.model.impl.OtpTransitServiceBuilder; +import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.util.ProgressTracker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,14 +16,12 @@ import java.util.ArrayList; import java.util.List; -import static org.opentripplanner.model.StopPattern.PICKDROP_NONE; - public class FlexTripsMapper { private static final Logger LOG = LoggerFactory.getLogger(FlexTripsMapper.class); - static public List createFlexTrips(OtpTransitServiceBuilder builder) { - List result = new ArrayList<>(); + static public List> createFlexTrips(OtpTransitServiceBuilder builder) { + List> result = new ArrayList<>(); TripStopTimes stopTimesByTrip = builder.getStopTimesSortedByTrip(); final int tripSize = stopTimesByTrip.size(); @@ -38,8 +39,8 @@ static public List createFlexTrips(OtpTransitServiceBuilder builder) { result.add(new UnscheduledTrip(trip, stopTimes)); } else if (ScheduledDeviatedTrip.isScheduledFlexTrip(stopTimes)) { result.add(new ScheduledDeviatedTrip(trip, stopTimes)); - } else if (hasContinuousStops(stopTimes)) { - // result.add(new ContinuousPickupDropOffTrip(trip, stopTimes)); + } else if (ContinuousPickupDropOffTrip.hasContinuousStops(stopTimes)) { + result.add(new ContinuousPickupDropOffTrip(trip, stopTimes)); } //Keep lambda! A method-ref would causes incorrect class and line number to be logged @@ -51,10 +52,16 @@ static public List createFlexTrips(OtpTransitServiceBuilder builder) { return result; } - private static boolean hasContinuousStops(List stopTimes) { - return stopTimes + public static void addGeometriesToContinuousStops(Graph graph) { + graph.index(); + + StreetMatcher matcher = new StreetMatcher(graph); + + graph.flexTripsById + .values() .stream() - .anyMatch(st -> st.getFlexContinuousPickup() != PICKDROP_NONE || st.getFlexContinuousDropOff() != PICKDROP_NONE); + .filter(ContinuousPickupDropOffTrip.class::isInstance) + .map(ContinuousPickupDropOffTrip.class::cast) + .forEach(continuousPickupDropOffTrip -> continuousPickupDropOffTrip.addGeometries(graph, matcher)); } - } diff --git a/src/ext/java/org/opentripplanner/ext/flex/edgetype/FlexTripEdge.java b/src/ext/java/org/opentripplanner/ext/flex/edgetype/FlexTripEdge.java index 6fd2e4c943f..4c7bb627a3b 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/edgetype/FlexTripEdge.java +++ b/src/ext/java/org/opentripplanner/ext/flex/edgetype/FlexTripEdge.java @@ -19,7 +19,7 @@ import java.util.Locale; -public class FlexTripEdge extends Edge { +public class FlexTripEdge extends Edge { private static final Logger LOG = LoggerFactory.getLogger(FlexTripEdge.class); @@ -27,13 +27,13 @@ public class FlexTripEdge extends Edge { public StopLocation s1; public StopLocation s2; - private FlexTrip trip; - public FlexAccessEgressTemplate flexTemplate; + private final FlexTrip trip; + public FlexAccessEgressTemplate flexTemplate; public FlexPath flexPath; public FlexTripEdge( - Vertex v1, Vertex v2, StopLocation s1, StopLocation s2, FlexTrip trip, - FlexAccessEgressTemplate flexTemplate, FlexPathCalculator calculator + Vertex v1, Vertex v2, StopLocation s1, StopLocation s2, FlexTrip trip, + FlexAccessEgressTemplate flexTemplate, FlexPathCalculator calculator ) { // Why is this code so dirty? Because we don't want this edge to be added to the edge lists. // The first parameter in Vertex constructor is graph. If it is null, the vertex isn't added to it. diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/ContinuousStopsFlexPathCalculator.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/ContinuousStopsFlexPathCalculator.java new file mode 100644 index 00000000000..f1761981236 --- /dev/null +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/ContinuousStopsFlexPathCalculator.java @@ -0,0 +1,31 @@ +package org.opentripplanner.ext.flex.flexpathcalculator; + +import org.opentripplanner.common.geometry.GeometryUtils; +import org.opentripplanner.ext.flex.trip.ContinuousPickupDropOffTrip; +import org.opentripplanner.routing.graph.Vertex; + +public class ContinuousStopsFlexPathCalculator implements FlexPathCalculator { + + private final ContinuousPickupDropOffTrip trip; + + public ContinuousStopsFlexPathCalculator(ContinuousPickupDropOffTrip trip) { + this.trip = trip; + } + + @Override + public FlexPath calculateFlexPath( + Vertex fromv, Vertex tov, Double fromStopIndex, Double toStopIndex + ) { + int distance = 0; + int departureTime = trip.earliestDepartureTime(Integer.MIN_VALUE, fromStopIndex, toStopIndex, 0); + int arrivalTime = trip.latestArrivalTime(Integer.MAX_VALUE, fromStopIndex, toStopIndex, 0); + + if (departureTime >= arrivalTime) return null; + //TODO: Generate geometry from edges + return new FlexPath( + distance, + arrivalTime - departureTime, + GeometryUtils.makeLineString(fromv.getLon(), fromv.getLat(), tov.getLon(), tov.getLat()) + ); + } +} diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DirectFlexPathCalculator.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DirectFlexPathCalculator.java index aa5e6fb2726..9a6eafbee45 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DirectFlexPathCalculator.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DirectFlexPathCalculator.java @@ -10,12 +10,12 @@ /** * Calculated driving times and distance based on direct distance and fixed average driving speed. */ -public class DirectFlexPathCalculator implements FlexPathCalculator { +public class DirectFlexPathCalculator implements FlexPathCalculator { public static final double FLEX_SPEED = 8.0; private static final int DIRECT_EXTRA_TIME = 5 * 60; - private double flexSpeed; + private final double flexSpeed; public DirectFlexPathCalculator(Graph graph) { this.flexSpeed = FLEX_SPEED; @@ -23,7 +23,7 @@ public DirectFlexPathCalculator(Graph graph) { @Override public FlexPath calculateFlexPath( - Vertex fromv, Vertex tov, int fromStopIndex, int toStopIndex + Vertex fromv, Vertex tov, Integer fromStopIndex, Integer toStopIndex ) { double distance = SphericalDistanceLibrary.distance(fromv.getCoordinate(), tov.getCoordinate()); LineString geometry = GeometryUtils.getGeometryFactory().createLineString( diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathCalculator.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathCalculator.java index 41ade3a0f90..0668db2a572 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathCalculator.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathCalculator.java @@ -7,9 +7,9 @@ /** * FlexPathCalculator is used to calculate the driving times and distances during flex routing */ -public interface FlexPathCalculator { +public interface FlexPathCalculator { @Nullable - FlexPath calculateFlexPath(Vertex fromv, Vertex tov, int fromStopIndex, int toStopIndex); + FlexPath calculateFlexPath(Vertex fromv, Vertex tov, T fromStopIndex, T toStopIndex); } diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/ScheduledFlexPathCalculator.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/ScheduledFlexPathCalculator.java index 0553217b802..4b11ee98d86 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/ScheduledFlexPathCalculator.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/ScheduledFlexPathCalculator.java @@ -6,18 +6,18 @@ /** * Calculate the driving times based on the shcheduled timetable for the route. */ -public class ScheduledFlexPathCalculator implements FlexPathCalculator { - private final FlexPathCalculator flexPathCalculator; - private final FlexTrip trip; +public class ScheduledFlexPathCalculator implements FlexPathCalculator { + private final FlexPathCalculator flexPathCalculator; + private final FlexTrip trip; - public ScheduledFlexPathCalculator(FlexPathCalculator flexPathCalculator, FlexTrip trip) { + public ScheduledFlexPathCalculator(FlexPathCalculator flexPathCalculator, FlexTrip trip) { this.flexPathCalculator = flexPathCalculator; this.trip = trip; } @Override public FlexPath calculateFlexPath( - Vertex fromv, Vertex tov, int fromStopIndex, int toStopIndex + Vertex fromv, Vertex tov, Integer fromStopIndex, Integer toStopIndex ) { FlexPath flexPath = flexPathCalculator.calculateFlexPath( fromv, diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/StreetFlexPathCalculator.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/StreetFlexPathCalculator.java index 089ab07482f..2af2cb1397b 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/StreetFlexPathCalculator.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/StreetFlexPathCalculator.java @@ -23,7 +23,7 @@ * Subsequents requests from the same fromVertex can fetch the path to the toVertex from the * existing ShortestPathTree. This one-to-many approach is needed to make the performance acceptable. */ -public class StreetFlexPathCalculator implements FlexPathCalculator { +public class StreetFlexPathCalculator implements FlexPathCalculator { private static final long MAX_FLEX_TRIP_DURATION_SECONDS = Duration.ofMinutes(45).toSeconds(); @@ -35,7 +35,7 @@ public StreetFlexPathCalculator(Graph graph) { } @Override - public FlexPath calculateFlexPath(Vertex fromv, Vertex tov, int fromStopIndex, int toStopIndex) { + public FlexPath calculateFlexPath(Vertex fromv, Vertex tov, Integer fromStopIndex, Integer toStopIndex) { ShortestPathTree shortestPathTree; if (cache.containsKey(fromv)) { diff --git a/src/ext/java/org/opentripplanner/ext/flex/template/FlexAccessEgressTemplate.java b/src/ext/java/org/opentripplanner/ext/flex/template/FlexAccessEgressTemplate.java index 826f12a4e5d..c5fe2035df3 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/template/FlexAccessEgressTemplate.java +++ b/src/ext/java/org/opentripplanner/ext/flex/template/FlexAccessEgressTemplate.java @@ -21,15 +21,15 @@ import java.util.List; import java.util.stream.Stream; -public abstract class FlexAccessEgressTemplate { +public abstract class FlexAccessEgressTemplate { protected final NearbyStop accessEgress; - protected final FlexTrip trip; - public final int fromStopIndex; - public final int toStopIndex; + protected final FlexTrip trip; + public final T fromStopIndex; + public final T toStopIndex; protected final StopLocation transferStop; protected final int secondsFromStartOfTime; public final ServiceDate serviceDate; - protected final FlexPathCalculator calculator; + protected final FlexPathCalculator calculator; /** * @@ -43,12 +43,12 @@ public abstract class FlexAccessEgressTemplate { */ FlexAccessEgressTemplate( NearbyStop accessEgress, - FlexTrip trip, - int fromStopIndex, - int toStopIndex, + FlexTrip trip, + T fromStopIndex, + T toStopIndex, StopLocation transferStop, FlexServiceDate date, - FlexPathCalculator calculator + FlexPathCalculator calculator ) { this.accessEgress = accessEgress; this.trip = trip; @@ -64,7 +64,7 @@ public StopLocation getTransferStop() { return transferStop; } - public FlexTrip getFlexTrip() { + public FlexTrip getFlexTrip() { return trip; } @@ -93,19 +93,19 @@ public FlexTrip getFlexTrip() { /** * Get the times in seconds, before during and after the flex ride. */ - abstract protected int[] getFlexTimes(FlexTripEdge flexEdge, State state); + abstract protected int[] getFlexTimes(FlexTripEdge flexEdge, State state); /** * Get the FlexTripEdge for the flex ride. */ - abstract protected FlexTripEdge getFlexEdge(Vertex flexFromVertex, StopLocation transferStop); + abstract protected FlexTripEdge getFlexEdge(Vertex flexFromVertex, StopLocation transferStop); /** * Checks whether the routing is possible */ abstract protected boolean isRouteable(Vertex flexVertex); - public Stream createFlexAccessEgressStream(Graph graph) { + public Stream> createFlexAccessEgressStream(Graph graph) { if (transferStop instanceof Stop) { TransitStopVertex flexVertex = graph.index.getStopVertexForStop().get(transferStop); if (isRouteable(flexVertex)) { @@ -129,8 +129,8 @@ public Stream createFlexAccessEgressStream(Graph graph) { } } - protected FlexAccessEgress getFlexAccessEgress(List transferEdges, Vertex flexVertex, Stop stop) { - FlexTripEdge flexEdge = getFlexEdge(flexVertex, transferStop); + protected FlexAccessEgress getFlexAccessEgress(List transferEdges, Vertex flexVertex, Stop stop) { + FlexTripEdge flexEdge = getFlexEdge(flexVertex, transferStop); State state = flexEdge.traverse(accessEgress.state); for (Edge e : transferEdges) { @@ -139,13 +139,14 @@ protected FlexAccessEgress getFlexAccessEgress(List transferEdges, Vertex int[] times = getFlexTimes(flexEdge, state); - return new FlexAccessEgress( + return new FlexAccessEgress<>( stop, times[0], times[1], times[2], fromStopIndex, - toStopIndex, secondsFromStartOfTime, + toStopIndex, + secondsFromStartOfTime, trip, state, transferEdges.isEmpty() diff --git a/src/ext/java/org/opentripplanner/ext/flex/template/FlexAccessTemplate.java b/src/ext/java/org/opentripplanner/ext/flex/template/FlexAccessTemplate.java index 65d64266a9e..dba5cc42a74 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/template/FlexAccessTemplate.java +++ b/src/ext/java/org/opentripplanner/ext/flex/template/FlexAccessTemplate.java @@ -23,12 +23,12 @@ import java.util.Locale; import java.util.TimeZone; -public class FlexAccessTemplate extends FlexAccessEgressTemplate { +public class FlexAccessTemplate extends FlexAccessEgressTemplate { public FlexAccessTemplate( - NearbyStop accessEgress, FlexTrip trip, int fromStopTime, int toStopTime, - StopLocation transferStop, FlexServiceDate date, FlexPathCalculator calculator + NearbyStop accessEgress, FlexTrip trip, T fromStopIndex, T toStopIndex, + StopLocation transferStop, FlexServiceDate date, FlexPathCalculator calculator ) { - super(accessEgress, trip, fromStopTime, toStopTime, transferStop, date, calculator); + super(accessEgress, trip, fromStopIndex, toStopIndex, transferStop, date, calculator); } public Itinerary createDirectItinerary( @@ -42,7 +42,7 @@ public Itinerary createDirectItinerary( return null; } - FlexTripEdge flexEdge = getFlexEdge(flexToVertex, egress.stop); + FlexTripEdge flexEdge = getFlexEdge(flexToVertex, egress.stop); State state = flexEdge.traverse(accessEgress.state); @@ -56,7 +56,7 @@ public Itinerary createDirectItinerary( int flexTime = flexTimes[1]; int postFlexTime = flexTimes[2]; - Integer timeShift = null; + int timeShift; if (arriveBy) { int lastStopArrivalTime = departureTime - postFlexTime - secondsFromStartOfTime; @@ -124,17 +124,17 @@ protected boolean isRouteable(Vertex flexVertex) { fromStopIndex, toStopIndex ) != null; - }; + } - protected int[] getFlexTimes(FlexTripEdge flexEdge, State state) { + protected int[] getFlexTimes(FlexTripEdge flexEdge, State state) { int preFlexTime = (int) accessEgress.state.getElapsedTimeSeconds(); int edgeTimeInSeconds = flexEdge.getTimeInSeconds(); int postFlexTime = (int) state.getElapsedTimeSeconds() - preFlexTime - edgeTimeInSeconds; return new int[]{ preFlexTime, edgeTimeInSeconds, postFlexTime }; } - protected FlexTripEdge getFlexEdge(Vertex flexToVertex, StopLocation transferStop) { - return new FlexTripEdge( + protected FlexTripEdge getFlexEdge(Vertex flexToVertex, StopLocation transferStop) { + return new FlexTripEdge<>( accessEgress.state.getVertex(), flexToVertex, accessEgress.stop, diff --git a/src/ext/java/org/opentripplanner/ext/flex/template/FlexEgressTemplate.java b/src/ext/java/org/opentripplanner/ext/flex/template/FlexEgressTemplate.java index 43cc08f0027..3c2957acefe 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/template/FlexEgressTemplate.java +++ b/src/ext/java/org/opentripplanner/ext/flex/template/FlexEgressTemplate.java @@ -17,12 +17,12 @@ import java.util.Collection; import java.util.List; -public class FlexEgressTemplate extends FlexAccessEgressTemplate { +public class FlexEgressTemplate extends FlexAccessEgressTemplate { public FlexEgressTemplate( - NearbyStop accessEgress, FlexTrip trip, int fromStopTime, int toStopTime, - StopLocation transferStop, FlexServiceDate date, FlexPathCalculator calculator + NearbyStop accessEgress, FlexTrip trip, T fromStopIndex, T toStopIndex, + StopLocation transferStop, FlexServiceDate date, FlexPathCalculator calculator ) { - super(accessEgress, trip, fromStopTime, toStopTime, transferStop, date, calculator); + super(accessEgress, trip, fromStopIndex, toStopIndex, transferStop, date, calculator); } protected List getTransferEdges(SimpleTransfer simpleTransfer) { @@ -50,17 +50,17 @@ protected boolean isRouteable(Vertex flexVertex) { fromStopIndex, toStopIndex ) != null; - }; + } - protected int[] getFlexTimes(FlexTripEdge flexEdge, State state) { + protected int[] getFlexTimes(FlexTripEdge flexEdge, State state) { int postFlexTime = (int) accessEgress.state.getElapsedTimeSeconds(); int edgeTimeInSeconds = flexEdge.getTimeInSeconds(); int preFlexTime = (int) state.getElapsedTimeSeconds() - postFlexTime - edgeTimeInSeconds; return new int[]{ preFlexTime, edgeTimeInSeconds, postFlexTime }; } - protected FlexTripEdge getFlexEdge(Vertex flexFromVertex, StopLocation transferStop) { - return new FlexTripEdge( + protected FlexTripEdge getFlexEdge(Vertex flexFromVertex, StopLocation transferStop) { + return new FlexTripEdge<>( flexFromVertex, accessEgress.state.getVertex(), transferStop, diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/ContinuousPickupDropOffTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/ContinuousPickupDropOffTrip.java new file mode 100644 index 00000000000..b3db20f9522 --- /dev/null +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/ContinuousPickupDropOffTrip.java @@ -0,0 +1,275 @@ +package org.opentripplanner.ext.flex.trip; + +import org.apache.commons.lang3.ArrayUtils; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.LineString; +import org.opentripplanner.common.geometry.GeometryUtils; +import org.opentripplanner.ext.flex.FlexServiceDate; +import org.opentripplanner.ext.flex.flexpathcalculator.ContinuousStopsFlexPathCalculator; +import org.opentripplanner.ext.flex.flexpathcalculator.FlexPathCalculator; +import org.opentripplanner.ext.flex.template.FlexAccessTemplate; +import org.opentripplanner.ext.flex.template.FlexEgressTemplate; +import org.opentripplanner.graph_builder.module.map.StreetMatcher; +import org.opentripplanner.model.*; +import org.opentripplanner.routing.edgetype.StreetEdge; +import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.routing.graph.Vertex; +import org.opentripplanner.routing.graphfinder.NearbyStop; +import org.opentripplanner.routing.vertextype.StreetVertex; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.stream.Stream; + +import static org.opentripplanner.model.StopLocation.expandStops; +import static org.opentripplanner.model.StopPattern.PICKDROP_NONE; + + +public class ContinuousPickupDropOffTrip extends FlexTrip { + + private final ContinuousPickupDropOffStopTime[] stopTimes; + private final FlexStopLocation[] continuousStops; + private Vertex[] vertices; + private double[] stopIndices; + private double[] distances; + private final BookingInfo[] bookingInfos; + + public ContinuousPickupDropOffTrip(Trip trip, List stopTimes) { + super(trip); + + if (!hasContinuousStops(stopTimes)) { + throw new IllegalArgumentException("Incompatible stopTimes for continuous stops flex trip"); + } + + int nStops = stopTimes.size(); + this.stopTimes = new ContinuousPickupDropOffStopTime[nStops]; + this.continuousStops = new FlexStopLocation[nStops - 1]; + this.bookingInfos = new BookingInfo[nStops]; + + for (int i = 0; i < nStops; i++) { + this.stopTimes[i] = new ContinuousPickupDropOffStopTime(stopTimes.get(i)); + this.bookingInfos[i] = stopTimes.get(i).getBookingInfo(); + } + } + + public static boolean hasContinuousStops(List stopTimes) { + return stopTimes + .stream() + .anyMatch(st -> st.getFlexContinuousPickup() != PICKDROP_NONE || st.getFlexContinuousDropOff() != PICKDROP_NONE); + } + + @Override + public Stream> getFlexAccessTemplates( + NearbyStop access, FlexServiceDate date, FlexPathCalculator calculator + ) { + int stopArrayIndex = ArrayUtils.indexOf(vertices, access.state.getVertex()); + if (stopArrayIndex == -1) { return Stream.empty(); } + double fromIndex = stopIndices[stopArrayIndex]; + + ArrayList> res = new ArrayList<>(); + + ContinuousStopsFlexPathCalculator continuousCalculator = new ContinuousStopsFlexPathCalculator(this); + + boolean isFromStop = Math.rint(fromIndex) == fromIndex; + + // This would return only trips which can be found by raptor + if (isFromStop) { return Stream.empty(); } + + for (int toIndex = (int) Math.ceil(fromIndex); toIndex < stopTimes.length; toIndex++) { + if (stopTimes[toIndex].dropOffType == PICKDROP_NONE) continue; + for (StopLocation stop : expandStops(stopTimes[toIndex].stop)) { + res.add(new FlexAccessTemplate<>(access, this, fromIndex, (double) toIndex, stop, date, continuousCalculator)); + } + } + + return res.stream(); + } + + @Override + public Stream> getFlexEgressTemplates( + NearbyStop egress, FlexServiceDate date, FlexPathCalculator calculator + ) { + int stopArrayIndex = ArrayUtils.indexOf(vertices, egress.state.getVertex()); + if (stopArrayIndex == -1) { return Stream.empty(); } + double toIndex = stopIndices[stopArrayIndex]; + + ArrayList> res = new ArrayList<>(); + + ContinuousStopsFlexPathCalculator continuousCalculator = new ContinuousStopsFlexPathCalculator(this); + + boolean isToStop = Math.rint(toIndex) == toIndex; + + // This would return only trips which can be found by raptor + if (isToStop) { return Stream.empty(); } + + for (int fromIndex = (int) Math.floor(toIndex); fromIndex >= 0; fromIndex--) { + if (stopTimes[fromIndex].pickupType == PICKDROP_NONE) continue; + for (StopLocation stop : expandStops(stopTimes[fromIndex].stop)) { + res.add(new FlexEgressTemplate<>(egress, this, (double) fromIndex, toIndex, stop, date, continuousCalculator)); + } + } + + return res.stream(); + } + + @Override + public int earliestDepartureTime( + int departureTime, Double fromStopIndex, Double toStopIndex, int flexTime + ) { + if (Math.rint(fromStopIndex) == fromStopIndex) { + return stopTimes[(int) (double) fromStopIndex].departureTime; + } + int stopTime = getStopTime(fromStopIndex); + return stopTime >= departureTime ? stopTime : -1; + } + + @Override + public int latestArrivalTime( + int arrivalTime, Double fromStopIndex, Double toStopIndex, int flexTime + ) { + if (Math.rint(toStopIndex) == toStopIndex) { + return stopTimes[(int) (double) toStopIndex].arrivalTime; + } + int stopTime = getStopTime(toStopIndex); + return stopTime <= arrivalTime ? stopTime : -1; + } + + private int getStopTime(Double stopIndex) { + int prevStop = (int) Math.floor(stopIndex); + int nextStop = (int) Math.ceil(stopIndex); + + int departureFromPrevious = stopTimes[prevStop].departureTime; + int arrivalToNext = stopTimes[nextStop].arrivalTime; + + return departureFromPrevious + (int) ((arrivalToNext - departureFromPrevious) * (stopIndex - prevStop)); + } + + @Override + public Collection getStops() { + List stops = new ArrayList<>(Arrays.asList(continuousStops)); + Arrays.stream(stopTimes).forEach(stopTime -> stops.add(stopTime.stop)); + + return stops; + } + + @Override + public BookingInfo getBookingInfo(int i) { + return bookingInfos[i]; + } + + public void addGeometries( + Graph graph, StreetMatcher matcher + ) { + TripPattern pattern = graph.index.getPatternForTrip().get(trip); + + if (pattern == null || pattern.getGeometry() == null) { return; } + + FeedScopedId tripId = trip.getId(); + + ArrayList tempVertices = new ArrayList<>(); + ArrayList tempStopIndices = new ArrayList<>(); + ArrayList tempDistances = new ArrayList<>(); + + double cumulativeDistance = 0; + + for (int i = 0; i < pattern.numHopGeometries(); i++) { + ContinuousPickupDropOffStopTime stopTime = stopTimes[i]; + if (stopTime.continuousPickupType == PICKDROP_NONE + && stopTime.continuousDropOffType == PICKDROP_NONE) { + continue; + } + + List edges = matcher.match(pattern.getHopGeometry(i)); + if (edges == null || edges.isEmpty()) { continue; } + + StopLocation stop = stopTime.stop; + // TODO: How to generate id? + String id = tripId.getId() + "_" + stop.getId().getId(); + FeedScopedId feedScopedId = new FeedScopedId(tripId.getFeedId(), id); + var location = new FlexStopLocation(feedScopedId); + location.setName(stop.getName() + " -> " + stopTimes[i + 1].stop.getName()); + continuousStops[i] = location; + graph.locationsById.put(feedScopedId, location); + + int size = edges.size(); + + List coordinates = new ArrayList<>(); + double[] times = new double[size]; + double cumulativeTime = 0; + tempDistances.add(cumulativeDistance); + + for (int j = 0; j < size; j++) { + StreetEdge e = edges.get(j); + coordinates.addAll(Arrays.asList(e.getGeometry().getCoordinates())); + double distanceMeters = e.getDistanceMeters(); + cumulativeDistance += distanceMeters; + tempDistances.add(cumulativeDistance); + double time = distanceMeters / e.getCarSpeed(); + cumulativeTime += time; + times[j] = cumulativeTime; + } + + Vertex vertex = edges.get(0).getFromVertex(); + StreetVertex sv; + if (vertex instanceof StreetVertex) { + sv = (StreetVertex) vertex; + if (sv.flexStopLocations == null) { + sv.flexStopLocations = new HashSet<>(); + } + sv.flexStopLocations.add(location); + } + + tempVertices.add(vertex); + tempStopIndices.add((double) i); + + for (int j = 0; j < size; j++) { + vertex = edges.get(j).getToVertex(); + if (vertex instanceof StreetVertex) { + sv = (StreetVertex) vertex; + if (sv.flexStopLocations == null) { + sv.flexStopLocations = new HashSet<>(); + } + sv.flexStopLocations.add(location); + } + tempVertices.add(vertex); + tempStopIndices.add(i + (times[j] / cumulativeTime)); + } + + Coordinate[] coordinateArray = new Coordinate[coordinates.size()]; + LineString ls = GeometryUtils.getGeometryFactory().createLineString(coordinates.toArray(coordinateArray)); + location.setGeometry(ls); + } + + this.vertices = tempVertices.toArray(new Vertex[0]); + this.stopIndices = tempStopIndices.stream().mapToDouble(i -> i).toArray(); + this.distances = tempDistances.stream().mapToDouble(i -> i).toArray(); + } + + private static class ContinuousPickupDropOffStopTime implements Serializable { + private final StopLocation stop; + private final int departureTime; + private final int arrivalTime; + private final int pickupType; + private final int dropOffType; + private final int continuousPickupType; + private final int continuousDropOffType; + + + private ContinuousPickupDropOffStopTime(StopTime st) { + this.stop = st.getStop(); + + this.arrivalTime = st.getArrivalTime(); + this.departureTime = st.getDepartureTime(); + + this.pickupType = st.getPickupType(); + this.dropOffType = st.getDropOffType(); + + this.continuousPickupType = st.getFlexContinuousPickup(); + this.continuousDropOffType = st.getFlexContinuousDropOff(); + } + } +} diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/FlexTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/FlexTrip.java index 2e17c8403e6..893442fe402 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/FlexTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/FlexTrip.java @@ -6,7 +6,6 @@ import org.opentripplanner.ext.flex.template.FlexEgressTemplate; import org.opentripplanner.model.BookingInfo; import org.opentripplanner.model.StopLocation; -import org.opentripplanner.model.StopTime; import org.opentripplanner.model.TransitEntity; import org.opentripplanner.model.Trip; import org.opentripplanner.routing.graphfinder.NearbyStop; @@ -19,7 +18,7 @@ * subclasses encapsulates the different business logic, which the different types of services * adhere to. */ -public abstract class FlexTrip extends TransitEntity { +public abstract class FlexTrip extends TransitEntity { protected final Trip trip; @@ -28,17 +27,17 @@ public FlexTrip(Trip trip) { this.trip = trip; } - public abstract Stream getFlexAccessTemplates( - NearbyStop access, FlexServiceDate date, FlexPathCalculator calculator + public abstract Stream> getFlexAccessTemplates( + NearbyStop access, FlexServiceDate date, FlexPathCalculator calculator ); - public abstract Stream getFlexEgressTemplates( - NearbyStop egress, FlexServiceDate date, FlexPathCalculator calculator + public abstract Stream> getFlexEgressTemplates( + NearbyStop egress, FlexServiceDate date, FlexPathCalculator calculator ); - public abstract int earliestDepartureTime(int departureTime, int fromStopIndex, int toStopIndex, int flexTime); + public abstract int earliestDepartureTime(int departureTime, T fromStopIndex, T toStopIndex, int flexTime); - public abstract int latestArrivalTime(int arrivalTime, int fromStopIndex, int toStopIndex, int flexTime); + public abstract int latestArrivalTime(int arrivalTime, T fromStopIndex, T toStopIndex, int flexTime); public abstract Collection getStops(); diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java index a0e6593071b..697bd0f66a4 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java @@ -17,12 +17,12 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.opentripplanner.model.StopLocation.expandStops; import static org.opentripplanner.model.StopPattern.PICKDROP_NONE; import static org.opentripplanner.model.StopTime.MISSING_VALUE; @@ -30,7 +30,7 @@ * A scheduled deviated trip is similar to a regular scheduled trip, except that is continues stop * locations, which are not stops, but other types, such as groups of stops or location areas. */ -public class ScheduledDeviatedTrip extends FlexTrip { +public class ScheduledDeviatedTrip extends FlexTrip { private final ScheduledDeviatedStopTime[] stopTimes; @@ -62,21 +62,21 @@ public ScheduledDeviatedTrip(Trip trip, List stopTimes) { } @Override - public Stream getFlexAccessTemplates( - NearbyStop access, FlexServiceDate date, FlexPathCalculator calculator + public Stream> getFlexAccessTemplates( + NearbyStop access, FlexServiceDate date, FlexPathCalculator calculator ) { - FlexPathCalculator scheduledCalculator = new ScheduledFlexPathCalculator(calculator, this); + FlexPathCalculator scheduledCalculator = new ScheduledFlexPathCalculator(calculator, this); int fromIndex = getFromIndex(access); if (fromIndex == -1) { return Stream.empty(); } - ArrayList res = new ArrayList<>(); + ArrayList> res = new ArrayList<>(); for (int toIndex = fromIndex + 1; toIndex < stopTimes.length; toIndex++) { if (stopTimes[toIndex].dropOffType == PICKDROP_NONE) continue; for (StopLocation stop : expandStops(stopTimes[toIndex].stop)) { - res.add(new FlexAccessTemplate(access, this, fromIndex, toIndex, stop, date, scheduledCalculator)); + res.add(new FlexAccessTemplate<>(access, this, fromIndex, toIndex, stop, date, scheduledCalculator)); } } @@ -84,21 +84,21 @@ public Stream getFlexAccessTemplates( } @Override - public Stream getFlexEgressTemplates( - NearbyStop egress, FlexServiceDate date, FlexPathCalculator calculator + public Stream> getFlexEgressTemplates( + NearbyStop egress, FlexServiceDate date, FlexPathCalculator calculator ) { - FlexPathCalculator scheduledCalculator = new ScheduledFlexPathCalculator(calculator, this); + FlexPathCalculator scheduledCalculator = new ScheduledFlexPathCalculator(calculator, this); int toIndex = getToIndex(egress); if (toIndex == -1) { return Stream.empty(); } - ArrayList res = new ArrayList<>(); + ArrayList> res = new ArrayList<>(); for (int fromIndex = toIndex - 1; fromIndex >= 0; fromIndex--) { if (stopTimes[fromIndex].pickupType == PICKDROP_NONE) continue; for (StopLocation stop : expandStops(stopTimes[fromIndex].stop)) { - res.add(new FlexEgressTemplate(egress, this, fromIndex, toIndex, stop, date, scheduledCalculator)); + res.add(new FlexEgressTemplate<>(egress, this, fromIndex, toIndex, stop, date, scheduledCalculator)); } } @@ -107,7 +107,7 @@ public Stream getFlexEgressTemplates( @Override public int earliestDepartureTime( - int departureTime, int fromStopIndex, int toStopIndex, int flexTime + int departureTime, Integer fromStopIndex, Integer toStopIndex, int flexTime ) { int stopTime = MISSING_VALUE; for (int i = fromStopIndex; stopTime == MISSING_VALUE && i >= 0; i--) { @@ -117,7 +117,7 @@ public int earliestDepartureTime( } @Override - public int latestArrivalTime(int arrivalTime, int fromStopIndex, int toStopIndex, int flexTime) { + public int latestArrivalTime(int arrivalTime, Integer fromStopIndex, Integer toStopIndex, int flexTime) { int stopTime = MISSING_VALUE; for (int i = toStopIndex; stopTime == MISSING_VALUE && i < stopTimes.length; i++) { stopTime = stopTimes[i].arrivalTime; @@ -138,12 +138,6 @@ public BookingInfo getBookingInfo(int i) { return bookingInfos[i]; } - private Collection expandStops(StopLocation stop) { - return stop instanceof FlexLocationGroup - ? ((FlexLocationGroup) stop).getLocations() - : Collections.singleton(stop); - } - private int getFromIndex(NearbyStop accessEgress) { for (int i = 0; i < stopTimes.length; i++) { if (stopTimes[i].pickupType == PICKDROP_NONE) continue; // No pickup allowed here diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java index d70116abfe4..d9d640cbfd7 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java @@ -15,12 +15,12 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.opentripplanner.model.StopLocation.expandStops; import static org.opentripplanner.model.StopPattern.PICKDROP_NONE; @@ -30,7 +30,7 @@ * on the driving time between the stops, with the schedule times being used just for deciding if a * trip is possible. */ -public class UnscheduledTrip extends FlexTrip { +public class UnscheduledTrip extends FlexTrip { private static final int N_STOPS = 2; private final UnscheduledStopTime[] stopTimes; @@ -63,36 +63,36 @@ public UnscheduledTrip(Trip trip, List stopTimes) { } @Override - public Stream getFlexAccessTemplates( - NearbyStop access, FlexServiceDate date, FlexPathCalculator calculator + public Stream> getFlexAccessTemplates( + NearbyStop access, FlexServiceDate date, FlexPathCalculator calculator ) { int fromIndex = getFromIndex(access); if (fromIndex != 0) { return Stream.empty(); } if (stopTimes[1].dropOffType == PICKDROP_NONE) { return Stream.empty(); } - ArrayList res = new ArrayList<>(); + ArrayList> res = new ArrayList<>(); for (StopLocation stop : expandStops(stopTimes[1].stop)) { - res.add(new FlexAccessTemplate(access, this, fromIndex, 1, stop, date, calculator)); + res.add(new FlexAccessTemplate<>(access, this, fromIndex, 1, stop, date, calculator)); } return res.stream(); } @Override - public Stream getFlexEgressTemplates( - NearbyStop egress, FlexServiceDate date, FlexPathCalculator calculator + public Stream> getFlexEgressTemplates( + NearbyStop egress, FlexServiceDate date, FlexPathCalculator calculator ) { int toIndex = getToIndex(egress); if (toIndex != 1) { return Stream.empty(); } if (stopTimes[0].pickupType == PICKDROP_NONE) { return Stream.empty(); } - ArrayList res = new ArrayList<>(); + ArrayList> res = new ArrayList<>(); for (StopLocation stop : expandStops(stopTimes[0].stop)) { - res.add(new FlexEgressTemplate(egress, this, 0, toIndex, stop, date, calculator)); + res.add(new FlexEgressTemplate<>(egress, this, 0, toIndex, stop, date, calculator)); } return res.stream(); @@ -100,7 +100,7 @@ public Stream getFlexEgressTemplates( @Override public int earliestDepartureTime( - int departureTime, int fromStopIndex, int toStopIndex, int flexTime + int departureTime, Integer fromStopIndex, Integer toStopIndex, int flexTime ) { UnscheduledStopTime fromStopTime = stopTimes[fromStopIndex]; UnscheduledStopTime toStopTime = stopTimes[toStopIndex]; @@ -114,7 +114,7 @@ public int earliestDepartureTime( } @Override - public int latestArrivalTime(int arrivalTime, int fromStopIndex, int toStopIndex, int flexTime) { + public int latestArrivalTime(int arrivalTime, Integer fromStopIndex, Integer toStopIndex, int flexTime) { UnscheduledStopTime fromStopTime = stopTimes[fromStopIndex]; UnscheduledStopTime toStopTime = stopTimes[toStopIndex]; if (toStopTime.flexWindowStart > arrivalTime || fromStopTime.flexWindowStart > ( @@ -139,12 +139,6 @@ public BookingInfo getBookingInfo(int i) { return bookingInfos[i]; } - private Collection expandStops(StopLocation stop) { - return stop instanceof FlexLocationGroup - ? ((FlexLocationGroup) stop).getLocations() - : Collections.singleton(stop); - } - private int getFromIndex(NearbyStop accessEgress) { for (int i = 0; i < stopTimes.length; i++) { if (stopTimes[i].pickupType == PICKDROP_NONE) continue; diff --git a/src/main/java/org/opentripplanner/graph_builder/module/AddTransitModelEntitiesToGraph.java b/src/main/java/org/opentripplanner/graph_builder/module/AddTransitModelEntitiesToGraph.java index 4240cd61c27..3cfd4be4f83 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/AddTransitModelEntitiesToGraph.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/AddTransitModelEntitiesToGraph.java @@ -358,7 +358,7 @@ private void addTransfersToGraph(Graph graph) { } private void addFlexTripsToGraph(Graph graph) { - for(FlexTrip flexTrip : transitService.getAllFlexTrips()) + for(FlexTrip flexTrip : transitService.getAllFlexTrips()) graph.flexTripsById.put(flexTrip.getId(), flexTrip); } diff --git a/src/main/java/org/opentripplanner/graph_builder/module/GtfsModule.java b/src/main/java/org/opentripplanner/graph_builder/module/GtfsModule.java index 4344f7dc132..1e3a9aed762 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/GtfsModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/GtfsModule.java @@ -151,6 +151,10 @@ public void buildGraph( addTransitModelToGraph(graph, gtfsBundle, transitModel); createGeometryAndBlockProcessor(gtfsBundle, transitModel).run(graph, issueStore); + + if (OTPFeature.FlexRouting.isOn()) { + FlexTripsMapper.addGeometriesToContinuousStops(graph); + } } } catch (IOException e) { throw new RuntimeException(e); diff --git a/src/main/java/org/opentripplanner/graph_builder/module/map/BusRouteStreetMatcher.java b/src/main/java/org/opentripplanner/graph_builder/module/map/BusRouteStreetMatcher.java index 723d8f427a1..1b06f674067 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/map/BusRouteStreetMatcher.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/map/BusRouteStreetMatcher.java @@ -7,6 +7,7 @@ import org.opentripplanner.graph_builder.services.GraphBuilderModule; import org.opentripplanner.model.Route; import org.opentripplanner.model.TripPattern; +import org.opentripplanner.routing.edgetype.StreetEdge; import org.opentripplanner.routing.graph.Edge; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.model.TransitMode; @@ -71,7 +72,7 @@ public void buildGraph( for (int i = 0; i < pattern.numHopGeometries(); i++) { LineString hopGeometry = pattern.getHopGeometry(i); - List edges = matcher.match(hopGeometry); + List edges = matcher.match(hopGeometry); if (edges == null || edges.isEmpty()) { log.warn("Could not match to street network: {}", pattern); continue; diff --git a/src/main/java/org/opentripplanner/graph_builder/module/map/MatchState.java b/src/main/java/org/opentripplanner/graph_builder/module/map/MatchState.java index 8f4629e58a0..c5413276d00 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/map/MatchState.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/map/MatchState.java @@ -1,19 +1,18 @@ package org.opentripplanner.graph_builder.module.map; -import java.util.ArrayList; -import java.util.List; - +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.linearref.LinearLocation; import org.opentripplanner.common.geometry.SphericalDistanceLibrary; +import org.opentripplanner.routing.api.request.RoutingRequest; import org.opentripplanner.routing.core.State; import org.opentripplanner.routing.core.TraverseMode; -import org.opentripplanner.routing.api.request.RoutingRequest; import org.opentripplanner.routing.edgetype.StreetEdge; import org.opentripplanner.routing.graph.Edge; import org.opentripplanner.routing.graph.Vertex; -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.linearref.LinearLocation; +import java.util.ArrayList; +import java.util.List; public abstract class MatchState { private static final RoutingRequest traverseOptions = new RoutingRequest(TraverseMode.CAR); @@ -28,11 +27,11 @@ public abstract class MatchState { public MatchState parent; - protected Edge edge; + protected StreetEdge edge; private double distanceAlongRoute = 0; - public MatchState(MatchState parent, Edge edge, double distanceAlongRoute) { + public MatchState(MatchState parent, StreetEdge edge, double distanceAlongRoute) { this.distanceAlongRoute = distanceAlongRoute; this.parent = parent; this.edge = edge; @@ -44,7 +43,7 @@ public MatchState(MatchState parent, Edge edge, double distanceAlongRoute) { public abstract List getNextStates(); - public Edge getEdge() { + public StreetEdge getEdge() { return edge; } @@ -59,8 +58,8 @@ protected boolean carsCanTraverse(Edge edge) { return s1 != null; } - protected List getOutgoingMatchableEdges(Vertex vertex) { - List edges = new ArrayList(); + protected List getOutgoingMatchableEdges(Vertex vertex) { + List edges = new ArrayList<>(); for (Edge e : vertex.getOutgoing()) { if (!(e instanceof StreetEdge)) { continue; @@ -68,7 +67,7 @@ protected List getOutgoingMatchableEdges(Vertex vertex) { if (e.getGeometry() == null) { continue; } - edges.add(e); + edges.add((StreetEdge) e); } return edges; } diff --git a/src/main/java/org/opentripplanner/graph_builder/module/map/MidblockMatchState.java b/src/main/java/org/opentripplanner/graph_builder/module/map/MidblockMatchState.java index e63816100e0..31329252e58 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/map/MidblockMatchState.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/map/MidblockMatchState.java @@ -1,34 +1,33 @@ package org.opentripplanner.graph_builder.module.map; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.opentripplanner.routing.graph.Edge; -import org.opentripplanner.routing.graph.Vertex; - import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.linearref.LinearLocation; import org.locationtech.jts.linearref.LocationIndexedLine; import org.locationtech.jts.util.AssertionFailedException; +import org.opentripplanner.routing.edgetype.StreetEdge; +import org.opentripplanner.routing.graph.Vertex; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; public class MidblockMatchState extends MatchState { private static final double MAX_ERROR = 1000; - private LinearLocation edgeIndex; + private final LinearLocation edgeIndex; public LinearLocation routeIndex; Geometry routeGeometry; - private Geometry edgeGeometry; + private final Geometry edgeGeometry; - private LocationIndexedLine indexedEdge; + private final LocationIndexedLine indexedEdge; - public MidblockMatchState(MatchState parent, Geometry routeGeometry, Edge edge, + public MidblockMatchState(MatchState parent, Geometry routeGeometry, StreetEdge edge, LinearLocation routeIndex, LinearLocation edgeIndex, double error, double distanceAlongRoute) { super(parent, edge, distanceAlongRoute); @@ -134,7 +133,7 @@ public List getNextStates() { return nextStates; } - for (Edge e : getOutgoingMatchableEdges(toVertex)) { + for (StreetEdge e : getOutgoingMatchableEdges(toVertex)) { double cost = error + NEW_SEGMENT_PENALTY; if (!carsCanTraverse(e)) { cost += NO_TRAVERSE_PENALTY; @@ -168,7 +167,7 @@ public List getNextStates() { Vertex toVertex = edge.getToVertex(); double travelAlongOldEdge = distanceAlongGeometry(edgeGeometry, edgeIndex, null); - for (Edge e : getOutgoingMatchableEdges(toVertex)) { + for (StreetEdge e : getOutgoingMatchableEdges(toVertex)) { Geometry newEdgeGeometry = e.getGeometry(); LocationIndexedLine newIndexedEdge = new LocationIndexedLine(newEdgeGeometry); newEdgeIndex = newIndexedEdge.project(newRouteCoord); diff --git a/src/main/java/org/opentripplanner/graph_builder/module/map/StreetMatcher.java b/src/main/java/org/opentripplanner/graph_builder/module/map/StreetMatcher.java index 770103e2cd8..fca0cc0381a 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/map/StreetMatcher.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/map/StreetMatcher.java @@ -1,18 +1,5 @@ package org.opentripplanner.graph_builder.module.map; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; - -import org.opentripplanner.routing.edgetype.StreetEdge; -import org.opentripplanner.routing.graph.Edge; -import org.opentripplanner.routing.graph.Graph; -import org.opentripplanner.routing.graph.Vertex; -import org.opentripplanner.common.pqueue.BinHeap; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; @@ -20,6 +7,18 @@ import org.locationtech.jts.linearref.LinearLocation; import org.locationtech.jts.linearref.LocationIndexedLine; import org.locationtech.jts.simplify.DouglasPeuckerSimplifier; +import org.opentripplanner.common.pqueue.BinHeap; +import org.opentripplanner.routing.edgetype.StreetEdge; +import org.opentripplanner.routing.graph.Edge; +import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.routing.graph.Vertex; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; /** * This Performs most of the work for the MapBuilder graph builder module. @@ -57,7 +56,7 @@ public StreetMatcher(Graph graph) { } @SuppressWarnings("unchecked") - public List match(Geometry routeGeometry) { + public List match(Geometry routeGeometry) { routeGeometry = removeDuplicatePoints(routeGeometry); @@ -78,7 +77,7 @@ public List match(Geometry routeGeometry) { envelope.expandBy(distanceThreshold); BinHeap states = new BinHeap(); - List nearbyEdges = index.query(envelope); + List nearbyEdges = index.query(envelope); while (nearbyEdges.isEmpty()) { envelope.expandBy(distanceThreshold); distanceThreshold *= 2; @@ -86,7 +85,7 @@ public List match(Geometry routeGeometry) { } // compute initial states - for (Edge initialEdge : nearbyEdges) { + for (StreetEdge initialEdge : nearbyEdges) { Geometry edgeGeometry = initialEdge.getGeometry(); LocationIndexedLine indexedEdge = new LocationIndexedLine(edgeGeometry); @@ -144,11 +143,11 @@ private Geometry removeDuplicatePoints(Geometry routeGeometry) { return routeGeometry.getFactory().createLineString(coords.toArray(coordArray)); } - private List toEdgeList(MatchState next) { - ArrayList edges = new ArrayList(); + private List toEdgeList(MatchState next) { + ArrayList edges = new ArrayList<>(); Edge lastEdge = null; while (next != null) { - Edge edge = next.getEdge(); + StreetEdge edge = next.getEdge(); if (edge != lastEdge) { edges.add(edge); lastEdge = edge; diff --git a/src/main/java/org/opentripplanner/model/OtpTransitService.java b/src/main/java/org/opentripplanner/model/OtpTransitService.java index bb3dbe40c7e..b82201fe0ee 100644 --- a/src/main/java/org/opentripplanner/model/OtpTransitService.java +++ b/src/main/java/org/opentripplanner/model/OtpTransitService.java @@ -76,5 +76,5 @@ public interface OtpTransitService { Collection getAllTrips(); - Collection getAllFlexTrips(); + Collection> getAllFlexTrips(); } diff --git a/src/main/java/org/opentripplanner/model/StopLocation.java b/src/main/java/org/opentripplanner/model/StopLocation.java index fb35768d0d9..a8b9630c8f6 100644 --- a/src/main/java/org/opentripplanner/model/StopLocation.java +++ b/src/main/java/org/opentripplanner/model/StopLocation.java @@ -1,5 +1,8 @@ package org.opentripplanner.model; +import java.util.Collection; +import java.util.Collections; + /** * A StopLocation describes a place where a vehicle can be boarded or alighted, which is not * necessarily a marked stop, but can be of other shapes, such as a service area for flexible @@ -21,4 +24,11 @@ public interface StopLocation { * the centroid of an area or line. */ WgsCoordinate getCoordinate(); + + static Collection expandStops(StopLocation stop) { + return stop instanceof FlexLocationGroup + ? ((FlexLocationGroup) stop).getLocations() + : Collections.singleton(stop); + } + } diff --git a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java index 50f9f37c8a0..85b7d81f4a8 100644 --- a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java +++ b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java @@ -106,7 +106,7 @@ public class OtpTransitServiceBuilder { private final Multimap tripPatterns = ArrayListMultimap.create(); - private final EntityById flexTripsById = new EntityById<>(); + private final EntityById> flexTripsById = new EntityById<>(); public OtpTransitServiceBuilder() { } @@ -219,7 +219,7 @@ public Multimap getTripPatterns() { return tripPatterns; } - public EntityById getFlexTripsById() { + public EntityById> getFlexTripsById() { return flexTripsById; } diff --git a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceImpl.java b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceImpl.java index 8ab38c86ad6..14ed970a5ce 100644 --- a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceImpl.java +++ b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceImpl.java @@ -96,7 +96,7 @@ class OtpTransitServiceImpl implements OtpTransitService { private final Collection trips; - private final Collection flexTrips; + private final Collection> flexTrips; /** * Create a read only version of the {@link OtpTransitService}. @@ -259,7 +259,7 @@ public Collection getAllTrips() { } @Override - public Collection getAllFlexTrips() { + public Collection> getAllFlexTrips() { return flexTrips; } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/RoutingWorker.java b/src/main/java/org/opentripplanner/routing/algorithm/RoutingWorker.java index ea7cc9f45b4..38aaafc8802 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/RoutingWorker.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/RoutingWorker.java @@ -156,7 +156,7 @@ private Collection routeTransit(Router router) { // Special handling of flex accesses if (OTPFeature.FlexRouting.isOn() && request.modes.accessMode.equals(StreetMode.FLEXIBLE)) { - Collection flexAccessList = FlexAccessEgressRouter.routeAccessEgress( + Collection> flexAccessList = FlexAccessEgressRouter.routeAccessEgress( request, false ); @@ -175,7 +175,7 @@ private Collection routeTransit(Router router) { // Special handling of flex egresses if (OTPFeature.FlexRouting.isOn() && request.modes.egressMode.equals(StreetMode.FLEXIBLE)) { - Collection flexEgressList = FlexAccessEgressRouter.routeAccessEgress( + Collection> flexEgressList = FlexAccessEgressRouter.routeAccessEgress( request, true ); diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptor/router/street/FlexAccessEgressRouter.java b/src/main/java/org/opentripplanner/routing/algorithm/raptor/router/street/FlexAccessEgressRouter.java index 7447ff2a54c..971df4528f6 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptor/router/street/FlexAccessEgressRouter.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptor/router/street/FlexAccessEgressRouter.java @@ -13,7 +13,7 @@ public class FlexAccessEgressRouter { private FlexAccessEgressRouter() {} - public static Collection routeAccessEgress( + public static Collection> routeAccessEgress( RoutingRequest request, boolean isEgress ) { diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/mappers/AccessEgressMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/mappers/AccessEgressMapper.java index 5e98aa8db36..725d275bff4 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/mappers/AccessEgressMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/mappers/AccessEgressMapper.java @@ -38,7 +38,7 @@ public List mapNearbyStops(Collection accessStops, boo } public Collection mapFlexAccessEgresses( - Collection flexAccessEgresses + Collection> flexAccessEgresses ) { return flexAccessEgresses.stream() .map(flexAccessEgress -> new FlexAccessEgressAdapter(flexAccessEgress, stopIndex)) diff --git a/src/main/java/org/opentripplanner/routing/graph/Graph.java b/src/main/java/org/opentripplanner/routing/graph/Graph.java index 107e4709e63..46c706e8e62 100644 --- a/src/main/java/org/opentripplanner/routing/graph/Graph.java +++ b/src/main/java/org/opentripplanner/routing/graph/Graph.java @@ -244,7 +244,7 @@ public class Graph implements Serializable { public Map locationGroupsById = new HashMap<>(); - public Map flexTripsById = new HashMap<>(); + public Map> flexTripsById = new HashMap<>(); /** The distance between elevation samples used in CompactElevationProfile. */ private double distanceBetweenElevationSamples; diff --git a/src/test/java/org/opentripplanner/graph_builder/module/map/TestStreetMatcher.java b/src/test/java/org/opentripplanner/graph_builder/module/map/TestStreetMatcher.java index b62344f548c..d6d850f1f21 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/map/TestStreetMatcher.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/map/TestStreetMatcher.java @@ -1,31 +1,29 @@ package org.opentripplanner.graph_builder.module.map; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import java.util.List; - import org.junit.Before; import org.junit.Test; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.LineString; import org.opentripplanner.common.geometry.PackedCoordinateSequence; +import org.opentripplanner.common.geometry.SphericalDistanceLibrary; import org.opentripplanner.routing.core.State; import org.opentripplanner.routing.core.StateEditor; import org.opentripplanner.routing.core.TraverseMode; import org.opentripplanner.routing.core.TraverseModeSet; import org.opentripplanner.routing.edgetype.StreetEdge; import org.opentripplanner.routing.edgetype.StreetTraversalPermission; -import org.opentripplanner.routing.graph.Edge; import org.opentripplanner.routing.graph.Graph; -import org.opentripplanner.common.geometry.SphericalDistanceLibrary; import org.opentripplanner.routing.vertextype.StreetVertex; - -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.GeometryFactory; -import org.locationtech.jts.geom.LineString; -import java.util.Locale; import org.opentripplanner.util.I18NString; import org.opentripplanner.util.NonLocalizedString; +import java.util.List; +import java.util.Locale; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + public class TestStreetMatcher { static GeometryFactory gf = new GeometryFactory(); @@ -89,7 +87,7 @@ public void testStreetMatcher() { StreetMatcher matcher = new StreetMatcher(graph); - List match = matcher.match(geometry); + List match = matcher.match(geometry); assertNotNull(match); assertEquals(1, match.size()); assertEquals("56th_24th", match.get(0).getToVertex().getLabel());