diff --git a/src/ext/java/org/opentripplanner/ext/reportapi/model/CsvReportBuilder.java b/src/ext/java/org/opentripplanner/ext/reportapi/model/CsvReportBuilder.java index f9d3d8b8cf9..cfd01782b24 100644 --- a/src/ext/java/org/opentripplanner/ext/reportapi/model/CsvReportBuilder.java +++ b/src/ext/java/org/opentripplanner/ext/reportapi/model/CsvReportBuilder.java @@ -6,7 +6,7 @@ /** * A very simple CSV builder to create CSV reports. *

- * This class helps formatting common types like time, duration and enums. + * This class helps to format common types like time, duration and enums. */ class CsvReportBuilder { private final String sep; @@ -51,12 +51,12 @@ void addText(String text) { } void addNumber(Number num) { - buf.append(num.toString()); + buf.append(num == null ? "" : num.toString()); sep(); } void addBoolean(Boolean b) { - buf.append(b.toString()); + buf.append(b == null ? "" : b.toString()); sep(); } diff --git a/src/ext/java/org/opentripplanner/ext/reportapi/model/TransfersReport.java b/src/ext/java/org/opentripplanner/ext/reportapi/model/TransfersReport.java index 6a6652dad56..2bb48f1eb68 100644 --- a/src/ext/java/org/opentripplanner/ext/reportapi/model/TransfersReport.java +++ b/src/ext/java/org/opentripplanner/ext/reportapi/model/TransfersReport.java @@ -6,17 +6,23 @@ import java.util.List; import org.locationtech.jts.geom.Coordinate; import org.opentripplanner.common.geometry.SphericalDistanceLibrary; +import org.opentripplanner.model.Station; import org.opentripplanner.model.Stop; +import org.opentripplanner.model.Trip; +import org.opentripplanner.model.TripPattern; import org.opentripplanner.model.transfer.ConstrainedTransfer; +import org.opentripplanner.model.transfer.RouteTransferPoint; +import org.opentripplanner.model.transfer.StationTransferPoint; import org.opentripplanner.model.transfer.StopTransferPoint; import org.opentripplanner.model.transfer.TransferPoint; +import org.opentripplanner.model.transfer.TripTransferPoint; import org.opentripplanner.routing.graph.GraphIndex; /** * This class is used to export transfers for human verification to a CSV file. This is useful * when trying to debug the rather complicated NeTEx data format or to get the GTFS transfers in a - * more human readable form. It can also be used to test transfer functionality, since it is easy + * more human-readable form. It can also be used to test transfer functionality, since it is easy * to read and find special test-cases when needed. */ public class TransfersReport { @@ -41,32 +47,39 @@ public static String export(List transfers, GraphIndex inde String export() { buf.addHeader( - "Id", "Operator", "FromTripId", "FromTrip", "FromStop", - "ToTripId", "ToTrip", "ToStop", "ArrivalTime", "DepartureTime", "TransferTime", - "Walk", "Priority", "MaxWaitTime", "StaySeated", "Guaranteed" + "Id", "Operator", "From", "FromId", "FromRoute", "FromTrip", "FromStop", + "FromSpecificity", "To", "ToId", "ToRoute", "ToTrip", "ToStop", "ToSpecificity", + "ArrivalTime", "DepartureTime", "TransferTime", "Walk", "Priority", "MaxWaitTime", + "StaySeated", "Guaranteed" ); transfers.forEach(t -> { var from = pointInfo(t.getFrom(), true); var to = pointInfo(t.getTo(), false); - var dist = (from.c == null || to.c == null) + var dist = (from.coordinate == null || to.coordinate == null) ? "" : String.format( "%.0fm", - SphericalDistanceLibrary.fastDistance(from.c, to.c) + SphericalDistanceLibrary.fastDistance(from.coordinate, to.coordinate) ); var duration = (from.time == NOT_SET || to.time == NOT_SET) ? "" : durationToStr(to.time - from.time); var c = t.getTransferConstraint(); buf.addText(t.getId() == null ? "" : t.getId().getId()); - buf.addText(t.getFrom().getTrip().getOperator().getId().getId()); - buf.addText(from.tripId); + buf.addText((from.operator.isEmpty() ? to : from).operator); + buf.addText(from.type); + buf.addText(from.entityId); + buf.addText(from.route); buf.addText(from.trip); buf.addText(from.loc); - buf.addText(to.tripId); + buf.addNumber(from.specificity); + buf.addText(to.type); + buf.addText(to.entityId); + buf.addText(to.route); buf.addText(to.trip); buf.addText(to.loc); + buf.addNumber(to.specificity); buf.addTime(from.time, NOT_SET); buf.addTime(to.time, NOT_SET); buf.addText(duration); @@ -85,42 +98,83 @@ private TxPoint pointInfo( boolean arrival ) { var r = new TxPoint(); - if (p instanceof StopTransferPoint) { - r.loc = p.getStop().getName(); - return r; - } - var ptn = index.getPatternForTrip().get(p.getTrip()); - var trip = p.getTrip(); - var route = trip.getRoute(); - - r.tripId = trip.getId().getId(); - r.trip = route.getName() + " " + route.getMode() + " " + route.getLongName() - + " " + trip.getTripHeadsign(); - r.c = null; - + if(p instanceof TripTransferPoint) { + var tp = (TripTransferPoint)p; + var trip = tp.getTrip(); + var route = trip.getRoute(); + var ptn = index.getPatternForTrip().get(trip); + r.operator = trip.getOperator().getId().getId(); + r.type = "Trip"; + r.entityId = trip.getId().getId(); + r.route = route.getName() + " " + route.getMode() + " " + route.getLongName(); + r.trip = trip.getTripHeadsign(); + addLocation(r, ptn, tp.getStopPositionInPattern(), trip, arrival); + } + else if(p instanceof RouteTransferPoint) { + var rp = (RouteTransferPoint)p; + var route = rp.getRoute(); + var ptn = index.getPatternsForRoute().get(route).stream().findFirst().orElse(null); + r.operator = route.getOperator().getId().getId(); + r.type = "Route"; + r.entityId = route.getId().getId(); + r.route = route.getName() + " " + route.getMode() + " " + route.getLongName(); + addLocation(r, ptn, rp.getStopPositionInPattern(), null, arrival); + } + else if(p instanceof StopTransferPoint) { + var sp = (StopTransferPoint)p; + Stop stop = sp.getStop(); + r.type = "Stop"; + r.entityId = stop.getId().getId(); + r.loc = stop.getName(); + r.coordinate = stop.getCoordinate().asJtsCoordinate(); + } + else if(p instanceof StationTransferPoint) { + var sp = (StationTransferPoint)p; + Station station = sp.getStation(); + r.type = "Station"; + r.entityId = station.getId().getId(); + r.loc = station.getName(); + r.coordinate = station.getCoordinate().asJtsCoordinate(); + } + r.specificity = p.getSpecificityRanking(); + r.coordinate = null; + return r; + } - if (ptn.getStops().size() > p.getStopPosition()) { - int pos = p.getStopPosition(); - Stop stop = ptn.getStops().get(pos); - var tt = ptn.getScheduledTimetable().getTripTimes(trip); - r.loc += stop.getName() + " [" + pos + "]" + " " + stop.getCoordinate(); - r.time = arrival ? tt.getScheduledArrivalTime(pos) : tt.getScheduledDepartureTime(pos); - r.c = stop.getCoordinate().asJtsCoordinate(); + private static void addLocation( + TxPoint r, + TripPattern pattern, + int stopPosition, + Trip trip, + boolean arrival + ) { + if(pattern == null || stopPosition >= pattern.getStopPattern().getSize()) { + r.loc += "[Stop position not found: " + stopPosition + "]"; + return; } - else { - r.loc += "[Stop index not found: " + p.getStopPosition() + "]"; + Stop stop = pattern.getStops().get(stopPosition); + r.loc += stop.getName() + " [" + stopPosition + "]" + " " + stop.getCoordinate(); + r.coordinate = stop.getCoordinate().asJtsCoordinate(); + + if(trip != null) { + var tt = pattern.getScheduledTimetable().getTripTimes(trip); + r.time = arrival + ? tt.getScheduledArrivalTime(stopPosition) + : tt.getScheduledDepartureTime(stopPosition); } - r.loc += " " + p.getSpecificityRanking(); - return r; } static class TxPoint { + private String operator = ""; + private String type = ""; + private String entityId = ""; private String loc = ""; - private String tripId = ""; private String trip = ""; - private Coordinate c = null; + private String route = ""; + private Integer specificity = null; + private Coordinate coordinate = null; private int time = NOT_SET; } } diff --git a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/timetable/InterchangeType.java b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/timetable/InterchangeType.java index 506186272f8..563ffb95c20 100644 --- a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/timetable/InterchangeType.java +++ b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/timetable/InterchangeType.java @@ -5,9 +5,13 @@ import graphql.schema.GraphQLFieldDefinition; import graphql.schema.GraphQLObjectType; import graphql.schema.GraphQLOutputType; +import java.util.function.Function; import org.opentripplanner.ext.transmodelapi.model.EnumTypes; +import org.opentripplanner.model.Route; +import org.opentripplanner.model.Trip; import org.opentripplanner.model.transfer.ConstrainedTransfer; import org.opentripplanner.model.transfer.TransferConstraint; +import org.opentripplanner.model.transfer.TransferPoint; public class InterchangeType { @@ -52,36 +56,36 @@ public static GraphQLObjectType create( .deprecate( "This is the same as using the `fromServiceJourney { line }` field.") .type(lineType) - .dataFetcher(env -> transfer(env).getFrom().getTrip().getRoute()) + .dataFetcher(env -> transferRoute(env, ConstrainedTransfer::getFrom)) .build()) .field(GraphQLFieldDefinition.newFieldDefinition() .name("ToLine") .deprecate( "This is the same as using the `toServiceJourney { line }` field.") .type(lineType) - .dataFetcher(env -> transfer(env).getTo().getTrip().getRoute()) + .dataFetcher(env -> transferRoute(env, ConstrainedTransfer::getTo)) .build()) .field(GraphQLFieldDefinition.newFieldDefinition() - .name("FromServiceJourney") + .name("fromServiceJourney") .type(serviceJourneyType) - .deprecate("Use fromServiceJourney instead") - .dataFetcher(env -> transfer(env).getFrom().getTrip()) + .dataFetcher(env -> transferTrip(env, ConstrainedTransfer::getFrom)) .build()) .field(GraphQLFieldDefinition.newFieldDefinition() - .name("ToServiceJourney") + .name("toServiceJourney") .type(serviceJourneyType) - .deprecate("Use toServiceJourney instead") - .dataFetcher(env -> transfer(env).getTo().getTrip()) + .dataFetcher(env -> transferTrip(env, ConstrainedTransfer::getTo)) .build()) .field(GraphQLFieldDefinition.newFieldDefinition() - .name("fromServiceJourney") + .name("FromServiceJourney") .type(serviceJourneyType) - .dataFetcher(env -> transfer(env).getFrom().getTrip()) + .deprecate("Use fromServiceJourney instead") + .dataFetcher(env -> transferTrip(env, ConstrainedTransfer::getFrom)) .build()) .field(GraphQLFieldDefinition.newFieldDefinition() - .name("toServiceJourney") + .name("ToServiceJourney") .type(serviceJourneyType) - .dataFetcher(env -> transfer(env).getTo().getTrip()) + .deprecate("Use toServiceJourney instead") + .dataFetcher(env -> transferTrip(env, ConstrainedTransfer::getTo)) .build()) .build(); } @@ -90,6 +94,27 @@ private static ConstrainedTransfer transfer(DataFetchingEnvironment environment) return environment.getSource(); } + private static TransferPoint transferPoint( + DataFetchingEnvironment environment, + Function fromTo + ) { + return fromTo.apply(transfer(environment)); + } + + private static Trip transferTrip( + DataFetchingEnvironment environment, + Function fromTo + ) { + return TransferPoint.getTrip(transferPoint(environment, fromTo)); + } + + private static Route transferRoute( + DataFetchingEnvironment environment, + Function fromTo + ) { + return TransferPoint.getRoute(transferPoint(environment, fromTo)); + } + private static TransferConstraint constraint(DataFetchingEnvironment environment) { return transfer(environment).getTransferConstraint(); } diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java index ca55fe7a926..8aca5642c8f 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java @@ -56,8 +56,6 @@ public class GTFSToOtpTransitServiceMapper { private final TripMapper tripMapper; - private final BookingRuleMapper bookingRuleMapper; - private final StopTimeMapper stopTimeMapper; private final FrequencyMapper frequencyMapper; @@ -82,8 +80,13 @@ public GTFSToOtpTransitServiceMapper( agencyMapper = new AgencyMapper(feedId); routeMapper = new RouteMapper(agencyMapper); tripMapper = new TripMapper(routeMapper); - bookingRuleMapper = new BookingRuleMapper(); - stopTimeMapper = new StopTimeMapper(stopMapper, locationMapper, locationGroupMapper, tripMapper, bookingRuleMapper); + stopTimeMapper = new StopTimeMapper( + stopMapper, + locationMapper, + locationGroupMapper, + tripMapper, + new BookingRuleMapper() + ); frequencyMapper = new FrequencyMapper(tripMapper); fareRuleMapper = new FareRuleMapper( routeMapper, fareAttributeMapper @@ -95,7 +98,6 @@ public OtpTransitServiceBuilder getBuilder() { } public void mapStopTripAndRouteDatantoBuilder() { - builder.getAgenciesById().addAll(agencyMapper.map(data.getAllAgencies())); builder.getCalendarDates().addAll(serviceCalendarDateMapper.map(data.getAllCalendarDates())); builder.getCalendars().addAll(serviceCalendarMapper.map(data.getAllCalendars())); diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/TransferMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/TransferMapper.java index cc02fa75043..20a3b07a491 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/TransferMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/TransferMapper.java @@ -1,20 +1,23 @@ package org.opentripplanner.gtfs.mapping; -import java.util.ArrayList; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; import java.util.Collection; -import java.util.Collections; import java.util.List; -import java.util.Map; -import java.util.function.BiFunction; +import java.util.Objects; +import java.util.function.Predicate; import java.util.stream.Collectors; -import javax.annotation.Nullable; import org.onebusaway.gtfs.model.Transfer; import org.opentripplanner.model.Route; +import org.opentripplanner.model.Station; import org.opentripplanner.model.Stop; +import org.opentripplanner.model.StopLocation; import org.opentripplanner.model.StopTime; import org.opentripplanner.model.Trip; import org.opentripplanner.model.TripStopTimes; import org.opentripplanner.model.transfer.ConstrainedTransfer; +import org.opentripplanner.model.transfer.RouteTransferPoint; +import org.opentripplanner.model.transfer.StationTransferPoint; import org.opentripplanner.model.transfer.StopTransferPoint; import org.opentripplanner.model.transfer.TransferConstraint; import org.opentripplanner.model.transfer.TransferPoint; @@ -69,6 +72,9 @@ class TransferMapper { private final TripStopTimes stopTimesByTrip; + private final Multimap tripsByRoute = ArrayListMultimap.create(); + + TransferMapper( RouteMapper routeMapper, StationMapper stationMapper, @@ -97,27 +103,17 @@ static TransferPriority mapTypeToPriority(int type) { } Collection map(Collection allTransfers) { - List result = new ArrayList<>(); + setup(!allTransfers.isEmpty()); - for (org.onebusaway.gtfs.model.Transfer it : allTransfers) { - result.addAll(map(it)); - } - return result; + return allTransfers.stream().map(this::map) + .filter(Objects::nonNull) + .collect(Collectors.toList()); } - /** - * Map from GTFS to OTP model, {@code null} safe. - */ - Collection map(org.onebusaway.gtfs.model.Transfer original) { - return original == null ? List.of() : doMap(original); - } - - private Collection doMap(org.onebusaway.gtfs.model.Transfer rhs) { - + ConstrainedTransfer map(org.onebusaway.gtfs.model.Transfer rhs) { Trip fromTrip = tripMapper.map(rhs.getFromTrip()); Trip toTrip = tripMapper.map(rhs.getToTrip()); - Route fromRoute = routeMapper.map(rhs.getFromRoute()); - Route toRoute = routeMapper.map(rhs.getToRoute()); + TransferConstraint constraint = mapConstraint(rhs, fromTrip, toTrip); // TODO TGR - Create a transfer for this se issue #3369 @@ -131,35 +127,21 @@ private Collection doMap(org.onebusaway.gtfs.model.Transfer else { LOG.warn("Transfer skipped - no effect on routing: " + rhs); } - return List.of(); + return null; } - // Transfers may be specified using parent stations - // (https://developers.google.com/transit/gtfs/reference/transfers-file) - // "If the stop ID refers to a station that contains multiple stops, this transfer rule - // applies to all stops in that station." we thus expand transfers that use parent stations - // to all the member stops. - - Collection fromStops = getStopOrChildStops(rhs.getFromStop()); - Collection toStops = getStopOrChildStops(rhs.getToStop()); - - Collection fromPoints = mapTransferPoints(fromStops, fromTrip, fromRoute); - Collection toPoints = mapTransferPoints(toStops, toTrip, toRoute); - - Collection result = new ArrayList<>(); - - for (TransferPoint fromPoint : fromPoints) { - for (TransferPoint toPoint : toPoints) { - var transfer = new ConstrainedTransfer( - null, - fromPoint, - toPoint, - constraint - ); - result.add(transfer); - } + TransferPoint fromPoint = mapTransferPoint(rhs.getFromStop(), rhs.getFromRoute(), fromTrip, false); + TransferPoint toPoint = mapTransferPoint(rhs.getToStop(), rhs.getToRoute(), toTrip, true); + + return new ConstrainedTransfer(null, fromPoint, toPoint, constraint); + } + + private void setup(boolean run) { + if(!run) { return; } + + for (Trip trip : tripMapper.mappedTrips()) { + tripsByRoute.put(trip.getRoute(), trip); } - return result; } private TransferConstraint mapConstraint(Transfer rhs, Trip fromTrip, Trip toTrip) { @@ -172,64 +154,90 @@ private TransferConstraint mapConstraint(Transfer rhs, Trip fromTrip, Trip toTri return builder.build(); } - private Collection mapTransferPoints( - Collection stops, - Trip trip, - Route route + private TransferPoint mapTransferPoint( + org.onebusaway.gtfs.model.Stop rhsStopOrStation, + org.onebusaway.gtfs.model.Route rhsRoute, + Trip trip, + boolean boardTrip ) { - Collection result = new ArrayList<>(); - if (trip != null) { - result.addAll(createTransferPointForTrip(stops, trip, TripTransferPoint::new)); - } - else if (route != null) { - /* - TODO - This code result in a OutOfMemory exception, fin out why and fix it - - See issue https://github.com/opentripplanner/OpenTripPlanner/issues/3429 - for (Trip tripInRoute : tripsByRoute.get(route)) { - result.addAll( - createTransferPointForTrip( - stops, - tripInRoute, - (t,i) -> new RouteTransferPoint(route, t, i) - ) - ); - } - */ + Route route = routeMapper.map(rhsRoute); + Station station = null; + Stop stop = null; + + // A transfer is specified using Stops and/or Station, according to the GTFS specification: + // + // If the stop ID refers to a station that contains multiple stops, this transfer rule + // applies to all stops in that station. + // + // Source: https://developers.google.com/transit/gtfs/reference/transfers-file + + if (rhsStopOrStation.getLocationType() == 0) { + stop = stopMapper.map(rhsStopOrStation); } else { - for (Stop stop : stops) { - result.add(new StopTransferPoint(stop)); - } + station = stationMapper.map(rhsStopOrStation); + } + if(trip != null) { + int stopPositionInPattern = stopPosition(trip, stop, station, boardTrip); + return stopPositionInPattern < 0 ? null : new TripTransferPoint(trip, stopPositionInPattern); + } + else if(route != null) { + var trips = tripsByRoute.get(route); + if(trips.isEmpty()) { throw new IllegalStateException("No trips found fr route: " + route); } + int stopPositionInPattern = stopPosition(route, stop, station, boardTrip); + return new RouteTransferPoint(route, stopPositionInPattern); + } + else if(stop != null) { + return new StopTransferPoint(stop); + } + else if(station != null) { + return new StationTransferPoint(station); } - return result; + + throw new IllegalStateException("Should not get here!"); } - private Collection createTransferPointForTrip( - Collection stops, - Trip trip, - BiFunction createPoint - ) { - Collection result = new ArrayList<>(); + + private int stopPosition(Route route, Stop stop, Station station, boolean boardTrip) { + var stopPosList = tripsByRoute.get(route).stream() + .map(t -> stopPosition(t, stop, station, boardTrip)) + .distinct() + .collect(Collectors.toList()); + + if(stopPosList.size() == 1) { return stopPosList.get(0); } + + LOG.error( + "In GTFS 'transfers.txt' a transfer-point can be a combination of route and stop/station!" + + "OTP only support this case, if the stop/station have the same stop point in trip-" + + "pattern for all trips in the route. Route: " + route + ); + return -1; + } + + + private int stopPosition(Trip trip, Stop stop, Station station, boolean boardTrip) { List stopTimes = stopTimesByTrip.get(trip); - for (int i = 0; i < stopTimes.size(); ++i) { + + // We can board at the first stop, but not alight. + final int firstStopPos = boardTrip ? 0 : 1; + // We can alight at the last stop, but not board, the lastStopPos is exclusive + final int lastStopPos = stopTimes.size() - (boardTrip ? 1 : 0); + + Predicate stopMatches = station != null + ? (s) -> (s instanceof Stop && ((Stop)s).getParentStation() == station) + : (s) -> s == stop; + + for (int i = firstStopPos; i < lastStopPos; i++) { StopTime stopTime = stopTimes.get(i); + if(boardTrip && !stopTime.getPickupType().isRoutable()) { continue; } + if(!boardTrip && !stopTime.getDropOffType().isRoutable()) { continue; } - //noinspection SuspiciousMethodCalls - if (stops.contains(stopTime.getStop())) { - result.add(createPoint.apply(trip, i)); + if(stopMatches.test(stopTime.getStop())) { + return i; } } - return result; - } - - private Collection getStopOrChildStops(org.onebusaway.gtfs.model.Stop gtfsStop) { - if (gtfsStop.getLocationType() == 0) { - return Collections.singletonList(stopMapper.map(gtfsStop)); - } - else { - return stationMapper.map(gtfsStop).getChildStops(); - } + return -1; } private boolean sameBlockId(Trip a, Trip b) { @@ -238,16 +246,4 @@ private boolean sameBlockId(Trip a, Trip b) { } return a.getBlockId() != null && a.getBlockId().equals(b.getBlockId()); } - - @Nullable - private Map> createTripsByRouteMapIfRouteTransfersExist( - Collection trips, - Collection allTransfers - ) { - if(allTransfers.stream().anyMatch(t -> t.getFromRoute() != null || t.getToRoute() != null)) { - return trips.stream().collect(Collectors.groupingBy(Trip::getRoute)); - } - // Return null, not an empty map to enforce NPE if used when no Route exist - return null; - } } diff --git a/src/main/java/org/opentripplanner/model/TripPattern.java b/src/main/java/org/opentripplanner/model/TripPattern.java index ec5a9d5b016..d417c33ce81 100644 --- a/src/main/java/org/opentripplanner/model/TripPattern.java +++ b/src/main/java/org/opentripplanner/model/TripPattern.java @@ -7,17 +7,6 @@ import com.google.common.hash.HashFunction; import com.google.common.hash.Hashing; import com.google.common.io.BaseEncoding; -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.LineString; -import org.opentripplanner.common.geometry.CompactLineString; -import org.opentripplanner.common.geometry.GeometryUtils; -import org.opentripplanner.graph_builder.DataImportIssueStore; -import org.opentripplanner.graph_builder.issues.NonUniqueRouteName; -import org.opentripplanner.routing.trippattern.FrequencyEntry; -import org.opentripplanner.routing.trippattern.TripTimes; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; @@ -31,6 +20,16 @@ import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.LineString; +import org.opentripplanner.common.geometry.CompactLineString; +import org.opentripplanner.common.geometry.GeometryUtils; +import org.opentripplanner.graph_builder.DataImportIssueStore; +import org.opentripplanner.graph_builder.issues.NonUniqueRouteName; +import org.opentripplanner.routing.trippattern.FrequencyEntry; +import org.opentripplanner.routing.trippattern.TripTimes; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Represents a group of trips on a route, with the same direction id that all call at the same @@ -241,9 +240,10 @@ public PickDrop getBoardType(int stopIndex) { * trip as one of the scheduled trips on this pattern. */ public void add(TripTimes tt) { - // Only scheduled trips (added at graph build time, rather than directly to the timetable via updates) are in this list. - getTrips().add(tt.getTrip()); + // Only scheduled trips (added at graph build time, rather than directly to the timetable + // via updates) are in this list. scheduledTimetable.addTripTimes(tt); + // Check that all trips added to this pattern are on the initially declared route. // Identity equality is valid on GTFS entity objects. if (this.route != tt.getTrip().getRoute()) { @@ -307,7 +307,7 @@ public Direction getDirection() { * to search for trips/TripIds in the Timetable rather than the enclosing TripPattern. */ public List getTrips() { - return scheduledTimetable.getTripTimes().stream().map(t -> t.getTrip()).collect(Collectors.toList()); + return scheduledTimetable.getTripTimes().stream().map(TripTimes::getTrip).collect(Collectors.toList()); } /** The human-readable, unique name for this trip pattern. */ diff --git a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java index d20401aeeb7..1f750f2c566 100644 --- a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java +++ b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java @@ -332,19 +332,24 @@ private void fixOrRemovePatternsWhichReferenceNoneExistingTrips() { /** Remove all transfers witch reference none existing trips */ private void removeTransfersForNoneExistingTrips() { int orgSize = transfers.size(); - transfers.removeIf(this::transferTripsDoesNotExist); + transfers.removeIf(this::transferTripReferencesDoNotExist); logRemove("Trip", orgSize, transfers.size(), "Transfer to/from trip does not exist."); } /** Return {@code true} if the from/to trip reference is none null, but do not exist. */ - private boolean transferTripsDoesNotExist(ConstrainedTransfer t) { - return transferTripPointDoesNotExist(t.getFrom()) - || transferTripPointDoesNotExist(t.getTo()); + private boolean transferTripReferencesDoNotExist(ConstrainedTransfer t) { + return transferPointTripReferenceDoesNotExist(t.getFrom()) + || transferPointTripReferenceDoesNotExist(t.getTo()); } - /** Return true if the trip is a valid reference; {@code null} or exist. */ - private boolean transferTripPointDoesNotExist(TransferPoint p) { - return p.getTrip() != null && !tripsById.containsKey(p.getTrip().getId()); + /** + * Return {@code true} if the the point is a trip-transfer-point and the trip reference + * is missing. + */ + private boolean transferPointTripReferenceDoesNotExist(TransferPoint point) { + if(!point.isTripTransferPoint()) { return false; } + var trip = point.asTripTransferPoint().getTrip(); + return !tripsById.containsKey(trip.getId()); } private static void logRemove(String type, int orgSize, int newSize, String reason) { diff --git a/src/main/java/org/opentripplanner/model/transfer/ConstrainedTransfer.java b/src/main/java/org/opentripplanner/model/transfer/ConstrainedTransfer.java index 5a6be6d9d15..44569e5aa72 100644 --- a/src/main/java/org/opentripplanner/model/transfer/ConstrainedTransfer.java +++ b/src/main/java/org/opentripplanner/model/transfer/ConstrainedTransfer.java @@ -18,6 +18,8 @@ public final class ConstrainedTransfer implements RaptorConstrainedTransfer, Serializable { private static final long serialVersionUID = 1L; + private static final int FROM_RANKING_COEFFICIENT = 11; + private static final int TO_RANKING_COEFFICIENT = 10; private final FeedScopedId id; @@ -67,17 +69,43 @@ public boolean noConstraints() { return constraint.isRegularTransfer(); } - public boolean matchesStopPos(int fromStopPos, int toStopPos) { - return from.getStopPosition() == fromStopPos && to.getStopPosition() == toStopPos; - } - /** * * Specificity of a transfer * + * + * The ranking implemented here is slightly modified: + *

*/ public int getSpecificityRanking() { - return from.getSpecificityRanking() + to.getSpecificityRanking(); + return from.getSpecificityRanking() * FROM_RANKING_COEFFICIENT + + to.getSpecificityRanking() * TO_RANKING_COEFFICIENT; } @Override diff --git a/src/main/java/org/opentripplanner/model/transfer/RouteTransferPoint.java b/src/main/java/org/opentripplanner/model/transfer/RouteTransferPoint.java index 6faa133cf00..d0a4b2a8ad5 100644 --- a/src/main/java/org/opentripplanner/model/transfer/RouteTransferPoint.java +++ b/src/main/java/org/opentripplanner/model/transfer/RouteTransferPoint.java @@ -2,7 +2,7 @@ import java.io.Serializable; import org.opentripplanner.model.Route; -import org.opentripplanner.model.Trip; +import org.opentripplanner.model.base.ValueObjectToStringBuilder; /** * This is a specialized version of the {@link TripTransferPoint}, it represent a @@ -12,15 +12,24 @@ * By expanding a route into trips, we can drop expanded-trips(lower specificity ranking) * if a "real" trip-transfers-point exist. */ -public class RouteTransferPoint extends TripTransferPoint implements Serializable { +public final class RouteTransferPoint implements TransferPoint, Serializable { private static final long serialVersionUID = 1L; private final Route route; + private final int stopPositionInPattern; - public RouteTransferPoint(Route route, Trip trip, int stopPosition) { - super(trip, stopPosition); + public RouteTransferPoint(Route route, int stopPositionInPattern) { this.route = route; + this.stopPositionInPattern = stopPositionInPattern; + } + + public Route getRoute() { + return route; + } + + public int getStopPositionInPattern() { + return stopPositionInPattern; } @Override @@ -29,12 +38,19 @@ public boolean applyToAllTrips() { } @Override - public int getSpecificityRanking() { return 1; } + public int getSpecificityRanking() { return 2; } + + @Override + public boolean isRouteTransferPoint() { return true; } @Override public String toString() { - return ""; + return ValueObjectToStringBuilder.of() + .addText("") + .toString(); } } diff --git a/src/main/java/org/opentripplanner/model/transfer/StationTransferPoint.java b/src/main/java/org/opentripplanner/model/transfer/StationTransferPoint.java new file mode 100644 index 00000000000..dece0b8886d --- /dev/null +++ b/src/main/java/org/opentripplanner/model/transfer/StationTransferPoint.java @@ -0,0 +1,42 @@ +package org.opentripplanner.model.transfer; + +import java.io.Serializable; +import org.opentripplanner.model.Station; +import org.opentripplanner.model.base.ValueObjectToStringBuilder; + +public final class StationTransferPoint implements TransferPoint, Serializable { + + private static final long serialVersionUID = 1L; + + private final Station station; + + + public StationTransferPoint(Station station) { + this.station = station; + } + + public Station getStation() { + return station; + } + + @Override + public boolean applyToAllTrips() { + return true; + } + + @Override + public int getSpecificityRanking() { + return 0; + } + + @Override + public boolean isStationTransferPoint() { return true; } + + public String toString() { + return ValueObjectToStringBuilder.of() + .addText("") + .toString(); + } +} diff --git a/src/main/java/org/opentripplanner/model/transfer/StopTransferPoint.java b/src/main/java/org/opentripplanner/model/transfer/StopTransferPoint.java index 9a5e5bd30aa..b0d9217f620 100644 --- a/src/main/java/org/opentripplanner/model/transfer/StopTransferPoint.java +++ b/src/main/java/org/opentripplanner/model/transfer/StopTransferPoint.java @@ -1,7 +1,6 @@ package org.opentripplanner.model.transfer; import java.io.Serializable; -import java.util.Objects; import org.opentripplanner.model.Stop; public class StopTransferPoint implements TransferPoint, Serializable { @@ -10,11 +9,11 @@ public class StopTransferPoint implements TransferPoint, Serializable { private final Stop stop; + public StopTransferPoint(Stop stop) { this.stop = stop; } - @Override public Stop getStop() { return stop; } @@ -26,24 +25,13 @@ public boolean applyToAllTrips() { @Override public int getSpecificityRanking() { - return 0; + return 1; } @Override + public boolean isStopTransferPoint() { return true; } + public String toString() { return ""; } - - @Override - public boolean equals(Object o) { - if (this == o) { return true; } - if (!(o instanceof StopTransferPoint)) { return false; } - final StopTransferPoint that = (StopTransferPoint) o; - return Objects.equals(stop.getId(), that.stop.getId()); - } - - @Override - public int hashCode() { - return Objects.hash(stop.getId()); - } } diff --git a/src/main/java/org/opentripplanner/model/transfer/TransferConstraint.java b/src/main/java/org/opentripplanner/model/transfer/TransferConstraint.java index 6e0f276c51d..3f54dc5473e 100644 --- a/src/main/java/org/opentripplanner/model/transfer/TransferConstraint.java +++ b/src/main/java/org/opentripplanner/model/transfer/TransferConstraint.java @@ -110,12 +110,16 @@ public boolean isGuaranteed() { * if the alight-slack or board-slack is too tight. We ignore slack for facilitated transfers. *

* This is an aggregated field, which encapsulates an OTP specific rule. A facilitated transfer - * is either stay-seated or guaranteed. High priority transfers are not. + * is either stay-seated or guaranteed. High priority transfers are not facilitated. */ public boolean isFacilitated() { return staySeated || guaranteed; } + public boolean useInRaptorRouting() { + return isStaySeated() || isGuaranteed() || isNotAllowed(); + } + @Override public boolean isNotAllowed() { return priority == NOT_ALLOWED; diff --git a/src/main/java/org/opentripplanner/model/transfer/TransferPoint.java b/src/main/java/org/opentripplanner/model/transfer/TransferPoint.java index ec20c5bd54b..656bbe7c927 100644 --- a/src/main/java/org/opentripplanner/model/transfer/TransferPoint.java +++ b/src/main/java/org/opentripplanner/model/transfer/TransferPoint.java @@ -1,9 +1,11 @@ package org.opentripplanner.model.transfer; +import javax.annotation.Nullable; +import org.opentripplanner.model.Route; +import org.opentripplanner.model.Station; import org.opentripplanner.model.Stop; import org.opentripplanner.model.Trip; - /** * This interface is used to represent a point or location where a transfer start from or end. * @@ -46,26 +48,19 @@ public interface TransferPoint { int NOT_AVAILABLE = -1; - default Stop getStop() { - return null; - } + /* * + * If this do not {@link #applyToAllTrips()} then this method can be used to return the + * trip for witch the {@link ConstrainedTransfer} apply to. + * / + @Nullable default Trip getTrip() { return null; - } + }/* /** Return {@code true} if this transfer point apply to all trips in pattern */ boolean applyToAllTrips(); - /** - * If the given transfer point is a {@link TripTransferPoint}, this method return the stop - * position in the trip pattern. If this transfer point is just a stop or a stop+route this - * method return {@link #NOT_AVAILABLE}. - */ - default int getStopPosition() { - return NOT_AVAILABLE; - } - /** * * Specificity of a transfer @@ -73,8 +68,62 @@ default int getStopPosition() { */ int getSpecificityRanking(); - default boolean matches(Trip trip, int stopPos) { - // Note! We use "==" here since there should not be duplicate instances of trips - return getStopPosition() == stopPos && getTrip() == trip; + default boolean isTripTransferPoint() { return false; } + + default TripTransferPoint asTripTransferPoint() { return (TripTransferPoint) this; } + + default boolean isRouteTransferPoint() { return false; } + + default RouteTransferPoint asRouteTransferPoint() { return (RouteTransferPoint) this; } + + default boolean isStopTransferPoint() { return false; } + + default StopTransferPoint asStopTransferPoint() { return (StopTransferPoint) this; } + + default boolean isStationTransferPoint() { return false; } + + default StationTransferPoint asStationTransferPoint() { return (StationTransferPoint) this; } + + + @Nullable + static Station getStation(TransferPoint point) { + if(point.isStationTransferPoint()) { + return point.asStationTransferPoint().getStation(); + } + if(point.isStopTransferPoint()) { + return point.asStopTransferPoint().getStop().getParentStation(); + } + return null; + } + + @Nullable + static Stop getStop(TransferPoint point) { + return point.isStopTransferPoint() ? point.asStopTransferPoint().getStop() : null; + } + + @Nullable + static Trip getTrip(TransferPoint point) { + return point.isTripTransferPoint() ? point.asTripTransferPoint().getTrip() : null; + } + + @Nullable + static Route getRoute(TransferPoint point) { + if(point.isTripTransferPoint()) { + return point.asTripTransferPoint().getTrip().getRoute(); + } + if(point.isRouteTransferPoint()) { + return point.asRouteTransferPoint().getRoute(); + } + return null; + } + + static int getStopPositionInPattern(TransferPoint point) { + if(point.isTripTransferPoint()) { + return point.asTripTransferPoint().getStopPositionInPattern(); + } + if(point.isRouteTransferPoint()) { + return point.asRouteTransferPoint().getStopPositionInPattern(); + } + return NOT_AVAILABLE; } } diff --git a/src/main/java/org/opentripplanner/model/transfer/TransferPointMap.java b/src/main/java/org/opentripplanner/model/transfer/TransferPointMap.java new file mode 100644 index 00000000000..d2acb3ff4bb --- /dev/null +++ b/src/main/java/org/opentripplanner/model/transfer/TransferPointMap.java @@ -0,0 +1,81 @@ +package org.opentripplanner.model.transfer; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.opentripplanner.common.model.T2; +import org.opentripplanner.model.Route; +import org.opentripplanner.model.Station; +import org.opentripplanner.model.Stop; +import org.opentripplanner.model.Trip; + +class TransferPointMap { + private final Map, E> tripMap = new HashMap<>(); + private final Map, E> routeMap = new HashMap<>(); + private final Map stopMap = new HashMap<>(); + private final Map stationMap = new HashMap<>(); + + void put(TransferPoint point, E e) { + if(point.isTripTransferPoint()) { + var tp = point.asTripTransferPoint(); + tripMap.put(tripKey(tp.getTrip(), tp.getStopPositionInPattern()), e); + } + else if(point.isRouteTransferPoint()) { + var rp = point.asRouteTransferPoint(); + routeMap.put(routeKey(rp.getRoute(), rp.getStopPositionInPattern()), e); + } + else if(point.isStopTransferPoint()) { + stopMap.put(point.asStopTransferPoint().getStop(), e); + } + else if(point.isStationTransferPoint()) { + stationMap.put(point.asStationTransferPoint().getStation(), e); + } + else { + throw new IllegalArgumentException("Unknown TransferPoint type: " + point); + } + } + + E computeIfAbsent(TransferPoint point, Supplier creator) { + if(point.isTripTransferPoint()) { + var tp = point.asTripTransferPoint(); + return tripMap.computeIfAbsent(tripKey(tp.getTrip(), tp.getStopPositionInPattern()), k -> creator.get()); + } + else if(point.isRouteTransferPoint()) { + var rp = point.asRouteTransferPoint(); + return routeMap.computeIfAbsent(routeKey(rp.getRoute(), rp.getStopPositionInPattern()), k -> creator.get()); + } + else if(point.isStopTransferPoint()) { + var sp = point.asStopTransferPoint(); + return stopMap.computeIfAbsent(sp.getStop(), k -> creator.get()); + } + else if(point.isStationTransferPoint()) { + var sp = point.asStationTransferPoint(); + return stationMap.computeIfAbsent(sp.getStation(), k -> creator.get()); + } + throw new IllegalArgumentException("Unknown TransferPoint type: " + point); + } + + + List get(Trip trip, Stop stop, int stopPointInPattern) { + return Stream.of( + tripMap.get(tripKey(trip, stopPointInPattern)), + routeMap.get(routeKey(trip.getRoute(), stopPointInPattern)), + stopMap.get(stop), + stationMap.get(stop.getParentStation()) + ) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + private static T2 tripKey(Trip trip, int stopPositionInPattern) { + return new T2<>(trip, stopPositionInPattern); + } + + private static T2 routeKey(Route route, int stopPositionInPattern) { + return new T2<>(route, stopPositionInPattern); + } +} diff --git a/src/main/java/org/opentripplanner/model/transfer/TransferService.java b/src/main/java/org/opentripplanner/model/transfer/TransferService.java index b9ecc975d8f..26cccfb32c0 100644 --- a/src/main/java/org/opentripplanner/model/transfer/TransferService.java +++ b/src/main/java/org/opentripplanner/model/transfer/TransferService.java @@ -1,20 +1,14 @@ package org.opentripplanner.model.transfer; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Multimap; +import static java.util.Comparator.comparingInt; + import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.List; -import java.util.Map; import javax.annotation.Nullable; -import org.opentripplanner.common.model.P2; -import org.opentripplanner.common.model.T2; import org.opentripplanner.model.Stop; import org.opentripplanner.model.Trip; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * This class represents all transfer information in the graph. Transfers are grouped by @@ -25,37 +19,19 @@ */ public class TransferService implements Serializable { - private static final Logger LOG = LoggerFactory.getLogger(TransferService.class); - - /** Index of constrained transfers with an effect on routing by the to/destination point. */ - private final Multimap constrainedTransferByToPoint; - - /** - * Table which contains transfers between two trips/routes - */ - private final Map, ConstrainedTransfer> trip2tripTransfers; + private final List transfersList; /** - * Table which contains transfers between a trip/route and a stops + * A map of map may seem a bit odd, but the first map have the FROM-transfer-point + * as its key, while the second map have the TO-transfer-point as its key. This allows us to + * support all combination of (Trip, Route, Stop and Station) in total 16 possible combination + * of keys to the ConstrainedTransfer. */ - private final Map, ConstrainedTransfer> trip2StopTransfers; - - /** - * Table which contains transfers between a stop and a trip/route - */ - private final Map, ConstrainedTransfer> stop2TripTransfers; - - /** - * Table which contains transfers between two stops - */ - private final Map, ConstrainedTransfer> stop2StopTransfers; + private final TransferPointMap> transfersMap; public TransferService() { - this.constrainedTransferByToPoint = ArrayListMultimap.create(); - this.trip2tripTransfers = new HashMap<>(); - this.trip2StopTransfers = new HashMap<>(); - this.stop2TripTransfers = new HashMap<>(); - this.stop2StopTransfers = new HashMap<>(); + this.transfersList = new ArrayList<>(); + this.transfersMap = new TransferPointMap<>(); } public void addAll(Collection transfers) { @@ -65,16 +41,7 @@ public void addAll(Collection transfers) { } public List listAll() { - var list = new ArrayList(); - list.addAll(trip2tripTransfers.values()); - list.addAll(trip2StopTransfers.values()); - list.addAll(stop2TripTransfers.values()); - list.addAll(stop2StopTransfers.values()); - return list; - } - - public Collection listConstrainedTransfersTo(Trip toTrip, int toStopIndex) { - return constrainedTransferByToPoint.get(new TripTransferPoint(toTrip, toStopIndex)); + return transfersList; } @Nullable @@ -86,105 +53,20 @@ public ConstrainedTransfer findTransfer( int fromStopPosition, int toStopPosition ) { - var fromTripKey = new TripTransferPoint(fromTrip, fromStopPosition); - var toTripKey = new TripTransferPoint(toTrip, toStopPosition); - ConstrainedTransfer result; - - // Check the highest specificity ranked transfers first (trip-2-trip) - result = trip2tripTransfers.get(new P2<>(fromTripKey, toTripKey)); - if (result != null) { return result; } - - // Then check the next specificity ranked transfers (trip-2-stop and stop-2-trip) - result = trip2StopTransfers.get(new T2<>(fromTripKey, toStop)); - if (result != null) { return result; } - - // Then check the next specificity ranked transfers (trip-2-stop and stop-2-trip) - result = stop2TripTransfers.get(new T2<>(fromStop, toTripKey)); - if (result != null) { return result; } - - // If no specificity ranked transfers found return stop-2-stop transfers (lowest ranking) - return stop2StopTransfers.get(new P2<>(fromStop, toStop)); - } - - void add(ConstrainedTransfer transfer) { - TransferPoint from = transfer.getFrom(); - TransferPoint to = transfer.getTo(); - - addFacilitatedTransfer(transfer); - - if (from instanceof TripTransferPoint) { - var fromTrip = (TripTransferPoint) from; - if (to instanceof TripTransferPoint) { - var key = new P2<>(fromTrip, (TripTransferPoint) to); - if (doAddTransferBasedOnSpecificityRanking(transfer, trip2tripTransfers.get(key))) { - trip2tripTransfers.put(key, transfer); - } - } - else { - var key = new T2<>(fromTrip, to.getStop()); - if (doAddTransferBasedOnSpecificityRanking(transfer, trip2StopTransfers.get(key))) { - trip2StopTransfers.put(key, transfer); - } - } - } - else if (to instanceof TripTransferPoint) { - var key = new T2<>(from.getStop(), (TripTransferPoint) to); - if (doAddTransferBasedOnSpecificityRanking(transfer, stop2TripTransfers.get(key))) { - stop2TripTransfers.put(key, transfer); - } - } - else { - var key = new P2<>(from.getStop(), to.getStop()); - if (doAddTransferBasedOnSpecificityRanking(transfer, stop2StopTransfers.get(key))) { - stop2StopTransfers.put(key, transfer); - } - } - } - - /** - * A transfer goes from/to a stop, route* or trip. Route transfers are expanded to all trips - * using the special {@link RouteTransferPoint} subtype of {@link TripTransferPoint}. This - * expansion make sure that there can only be one match for each combination of from and to - * combination (from -> to): - *

    - *
  1. trip -> trip - *
  2. trip -> stop - *
  3. stop -> trip - *
  4. stop -> stop - *
- * For each pair of the above combination we can drop the transfers that have a the lowest - * specificity-ranking, thus using maps instead of multi-maps. - */ - private boolean doAddTransferBasedOnSpecificityRanking( - ConstrainedTransfer newTransfer, - ConstrainedTransfer existingTransfer - ) { - if (existingTransfer == null) { return true; } - - if (existingTransfer.getSpecificityRanking() < newTransfer.getSpecificityRanking()) { - return true; - } - if (existingTransfer.getSpecificityRanking() > newTransfer.getSpecificityRanking()) { - return false; - } - if (existingTransfer.equals(newTransfer)) { - return false; - } - LOG.warn( - "Two colliding transfers A and B with the same specificity-ranking is imported, B is " - + "dropped. A={}, B={}", existingTransfer, newTransfer - ); - return false; + return transfersMap.get(fromTrip, fromStop, fromStopPosition).stream() + .map(map2 -> map2.get(toTrip, toStop, toStopPosition)) + .flatMap(Collection::stream) + .max(comparingInt(ConstrainedTransfer::getSpecificityRanking)) + .stream() + .findFirst() + .orElse(null); } - private void addFacilitatedTransfer(ConstrainedTransfer transfer) { - var c = transfer.getTransferConstraint(); - var toPoint = transfer.getTo(); + private void add(ConstrainedTransfer transfer) { + var from = transfer.getFrom(); + var to = transfer.getTo(); - if(c.isFacilitated()) { - if(toPoint instanceof TripTransferPoint) { - constrainedTransferByToPoint.put((TripTransferPoint) toPoint, transfer); - } - } + transfersMap.computeIfAbsent(from, TransferPointMap::new).put(to, transfer); + transfersList.add(transfer); } } diff --git a/src/main/java/org/opentripplanner/model/transfer/TransferType.java b/src/main/java/org/opentripplanner/model/transfer/TransferType.java deleted file mode 100644 index af62c5ac0e3..00000000000 --- a/src/main/java/org/opentripplanner/model/transfer/TransferType.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.opentripplanner.model.transfer; - -public enum TransferType { - /** - * This transfer is recommended over other transfers. The routing algorithm should prefer - * this transfer compared to other transfers, for example by assigning a lower weight to it. - */ - RECOMMENDED(0), - /** - * This means the departing vehicle will wait for the arriving one and leave sufficient time - * for a rider to transfer between routes. - */ - GUARANTEED(1), - /** - * This is a regular transfer that is defined in the transit data (as opposed to - * OpenStreetMap data). In the case that both are present, this should take precedence. - * Because the the duration of the transfer is given and not the distance, walk speed will - * have no effect on this. - */ - MIN_TIME(2), - /** - * Transfers between these stops (and route/trip) is not possible (or not allowed), even if - * a transfer is already defined via OpenStreetMap data or in transit data. - */ - FORBIDDEN(3); - - TransferType(int gtfsCode) { - this.gtfsCode = gtfsCode; - } - - public final int gtfsCode; - - public static TransferType valueOfGtfsCode(int gtfsCode) { - for (TransferType value : values()) { - if (value.gtfsCode == gtfsCode) { - return value; - } - } - throw new IllegalArgumentException("Unknown GTFS TransferType: " + gtfsCode); - } -} diff --git a/src/main/java/org/opentripplanner/model/transfer/TripTransferPoint.java b/src/main/java/org/opentripplanner/model/transfer/TripTransferPoint.java index 12bde682375..8df22d712a6 100644 --- a/src/main/java/org/opentripplanner/model/transfer/TripTransferPoint.java +++ b/src/main/java/org/opentripplanner/model/transfer/TripTransferPoint.java @@ -1,30 +1,28 @@ package org.opentripplanner.model.transfer; import java.io.Serializable; -import java.util.Objects; import org.opentripplanner.model.Trip; +import org.opentripplanner.model.base.ValueObjectToStringBuilder; -public class TripTransferPoint implements TransferPoint, Serializable { +public final class TripTransferPoint implements TransferPoint, Serializable { private static final long serialVersionUID = 1L; private final Trip trip; - private final int stopPosition; + private final int stopPositionInPattern; - public TripTransferPoint(Trip trip, int stopPosition) { + public TripTransferPoint(Trip trip, int stopPositionInPattern) { this.trip = trip; - this.stopPosition = stopPosition; + this.stopPositionInPattern = stopPositionInPattern; } - @Override - public final Trip getTrip() { + public Trip getTrip() { return trip; } - @Override - public final int getStopPosition() { - return stopPosition; + public int getStopPositionInPattern() { + return stopPositionInPattern; } @Override @@ -32,36 +30,20 @@ public boolean applyToAllTrips() { return false; } - /** - * - * GTFS Specificity of a transfer - * - * {@link #equals(Object)} - */ @Override - public int getSpecificityRanking() { return 2; } + public int getSpecificityRanking() { return 3; } @Override - public String toString() { - return ""; - } + public boolean isTripTransferPoint() { return true; } - /** - * This equals is intentionally final and enforce equality based on the *trip* and - * *stop-position*. Any sub-type is equal if the trip and stop-position match, the type is not - * used. This allow us to create sub-types and override the {@link #getSpecificityRanking()}. - */ @Override - public final boolean equals(Object o) { - if (this == o) { return true; } - if (!(o instanceof TripTransferPoint)) { return false; } - - TripTransferPoint that = (TripTransferPoint) o; - return stopPosition == that.stopPosition && trip.getId().equals(that.trip.getId()); - } - - @Override - public final int hashCode() { - return Objects.hash(trip.getId(), stopPosition); + public String toString() { + return ValueObjectToStringBuilder.of() + .addText("") + .toString(); } } diff --git a/src/main/java/org/opentripplanner/netex/mapping/StationMapper.java b/src/main/java/org/opentripplanner/netex/mapping/StationMapper.java index 1eeaf424238..90400c80932 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/StationMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/StationMapper.java @@ -26,7 +26,7 @@ Station map(StopPlace stopPlace) { stopPlace.getDescription() != null ? stopPlace.getDescription().getValue() : null, null, null, - TransferPriorityMapper.mapToDomain(stopPlace.getWeighting()) + StopTransferPriorityMapper.mapToDomain(stopPlace.getWeighting()) ); if (station.getCoordinate() == null) { diff --git a/src/main/java/org/opentripplanner/netex/mapping/TransferPriorityMapper.java b/src/main/java/org/opentripplanner/netex/mapping/StopTransferPriorityMapper.java similarity index 95% rename from src/main/java/org/opentripplanner/netex/mapping/TransferPriorityMapper.java rename to src/main/java/org/opentripplanner/netex/mapping/StopTransferPriorityMapper.java index 4a4c22af136..53a0e22b50e 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/TransferPriorityMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/StopTransferPriorityMapper.java @@ -4,7 +4,7 @@ import org.opentripplanner.model.StopTransferPriority; import org.rutebanken.netex.model.InterchangeWeightingEnumeration; -class TransferPriorityMapper { +class StopTransferPriorityMapper { @Nullable static StopTransferPriority mapToDomain(InterchangeWeightingEnumeration value) { diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/FlexAccessEgressAdapter.java b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/FlexAccessEgressAdapter.java index ce8c7f1b4af..a4296a54f5a 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/FlexAccessEgressAdapter.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/FlexAccessEgressAdapter.java @@ -12,7 +12,7 @@ public FlexAccessEgressAdapter( FlexAccessEgress flexAccessEgress, boolean isEgress, StopIndexForRaptor stopIndex ) { super( - stopIndex.indexByStop.get(flexAccessEgress.stop), + stopIndex.indexOf(flexAccessEgress.stop), isEgress ? flexAccessEgress.lastState.reverse() : flexAccessEgress.lastState ); diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/StopIndexForRaptor.java b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/StopIndexForRaptor.java index b7c4c25fcae..35313455dbf 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/StopIndexForRaptor.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/StopIndexForRaptor.java @@ -1,6 +1,5 @@ package org.opentripplanner.routing.algorithm.raptor.transit; -import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -26,24 +25,27 @@ * The scope of instances of this class is limited to the mapping process, the final state is * stored in the {@link TransitLayer}. */ -public class StopIndexForRaptor { - public final List stopsByIndex; - public final Map indexByStop = new HashMap<>(); +public final class StopIndexForRaptor { + private final List stopsByIndex; + private final Map indexByStop = new HashMap<>(); public final int[] stopBoardAlightCosts; public StopIndexForRaptor(Collection stops, TransitTuningParameters tuningParameters) { - this.stopsByIndex = new ArrayList<>(stops); + this.stopsByIndex = List.copyOf(stops); initializeIndexByStop(); this.stopBoardAlightCosts = createStopBoardAlightCosts(stopsByIndex, tuningParameters); } - /** - * Create map between stop and index used by Raptor to stop objects in original graph - */ - void initializeIndexByStop() { - for(int i = 0; i< stopsByIndex.size(); ++i) { - indexByStop.put(stopsByIndex.get(i), i); - } + public Stop stopByIndex(int index) { + return stopsByIndex.get(index); + } + + public int indexOf(Stop stop) { + return indexByStop.get(stop); + } + + public int size() { + return stopsByIndex.size(); } /** @@ -58,6 +60,15 @@ public int[] listStopIndexesForStops(Stop[] stops) { return stopIndex; } + /** + * Create map between stop and index used by Raptor to stop objects in original graph + */ + private void initializeIndexByStop() { + for(int i = 0; i< stopsByIndex.size(); ++i) { + indexByStop.put(stopsByIndex.get(i), i); + } + } + /** * Create static board/alight cost for Raptor to include for each stop. */ diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/TransitLayer.java b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/TransitLayer.java index 69e3f4436cf..74cfa35ab41 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/TransitLayer.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/TransitLayer.java @@ -75,12 +75,12 @@ public TransitLayer( } public int getIndexByStop(Stop stop) { - return stopIndex.indexByStop.get(stop); + return stopIndex.indexOf(stop); } @Nullable public Stop getStopByIndex(int stop) { - return stop != -1 ? this.stopIndex.stopsByIndex.get(stop) : null; + return stop == -1 ? null : this.stopIndex.stopByIndex(stop); } public StopIndexForRaptor getStopIndex() { @@ -103,7 +103,7 @@ public ZoneId getTransitDataZoneId() { } public int getStopCount() { - return stopIndex.stopsByIndex.size(); + return stopIndex.size(); } @Nullable diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/TripPatternWithRaptorStopIndexes.java b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/TripPatternWithRaptorStopIndexes.java index af812371686..4f2d2b3366e 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/TripPatternWithRaptorStopIndexes.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/TripPatternWithRaptorStopIndexes.java @@ -1,47 +1,44 @@ package org.opentripplanner.routing.algorithm.raptor.transit; -import gnu.trove.map.TIntObjectMap; -import gnu.trove.map.hash.TIntObjectHashMap; -import java.util.ArrayList; -import java.util.List; import java.util.Objects; import org.opentripplanner.model.FeedScopedId; import org.opentripplanner.model.TransitMode; import org.opentripplanner.model.TripPattern; -import org.opentripplanner.model.transfer.ConstrainedTransfer; -import org.opentripplanner.routing.algorithm.raptor.transit.request.ConstrainedBoardingSearch; +import org.opentripplanner.routing.algorithm.raptor.transit.constrainedtransfer.ConstrainedBoardingSearch; +import org.opentripplanner.routing.algorithm.raptor.transit.constrainedtransfer.TransferForPattern; +import org.opentripplanner.routing.algorithm.raptor.transit.constrainedtransfer.TransferForPatternByStopPos; import org.opentripplanner.transit.raptor.api.transit.RaptorConstrainedTripScheduleBoardingSearch; import org.opentripplanner.transit.raptor.api.transit.RaptorTripPattern; public class TripPatternWithRaptorStopIndexes { - private final TripPattern pattern; + private final TripPattern pattern; private final int[] stopIndexes; /** - * List of transfers TO this pattern for each stop position in pattern used by Raptor during - * the FORWARD search. + * List of transfers TO this pattern for each stop position in pattern used by Raptor during the + * FORWARD search. */ - private final TIntObjectMap> constrainedTransfersForwardSearch = - new TIntObjectHashMap<>(); + private final TransferForPatternByStopPos + constrainedTransfersForwardSearch = new TransferForPatternByStopPos(); /** * List of transfers FROM this pattern for each stop position in pattern used by Raptor during * the REVERSE search. */ - private final TIntObjectMap> constrainedTransfersReverseSearch = - new TIntObjectHashMap<>(); + private final TransferForPatternByStopPos + constrainedTransfersReverseSearch = new TransferForPatternByStopPos(); public TripPatternWithRaptorStopIndexes( - int[] stopIndexes, - TripPattern pattern + TripPattern pattern, + int[] stopIndexes ) { - this.stopIndexes = stopIndexes; this.pattern = pattern; + this.stopIndexes = stopIndexes; } - public FeedScopedId getId() { return pattern.getId(); } + public FeedScopedId getId() {return pattern.getId();} public TransitMode getTransitMode() { return pattern.getMode(); @@ -70,20 +67,19 @@ public RaptorConstrainedTripScheduleBoardingSearch constrainedTran return new ConstrainedBoardingSearch(false, constrainedTransfersReverseSearch); } + @Override + public int hashCode() { + return Objects.hash(getId()); + } @Override public boolean equals(Object o) { - if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { return false; } + if (this == o) {return true;} + if (o == null || getClass() != o.getClass()) {return false;} TripPatternWithRaptorStopIndexes that = (TripPatternWithRaptorStopIndexes) o; return getId() == that.getId(); } - @Override - public int hashCode() { - return Objects.hash(getId()); - } - @Override public String toString() { return "TripPattern{" + @@ -92,26 +88,32 @@ public String toString() { '}'; } - /** This is public to allow the mappers to inject transfers */ - public void addTransferConstraintsForwardSearch(ConstrainedTransfer tx) { - // In the Raptor search the transfer is looked up using the target - // trip, the trip boarded after the transfer is done for a forward search. - add(constrainedTransfersForwardSearch, tx, tx.getTo().getStopPosition()); + /** + * This is public to allow the mappers to inject transfers + */ + public void addTransferConstraintsForwardSearch( + int targetStopPosition, + TransferForPattern transferForPattern + ) { + constrainedTransfersForwardSearch.add(targetStopPosition, transferForPattern); } - /** This is public to allow the mappers to inject transfers */ - public void addTransferConstraintsReverseSearch(ConstrainedTransfer tx) { - // In the Raptor search the transfer is looked up using the target - // trip. Thus, the transfer "from trip" should be used in a reverse search. - add(constrainedTransfersReverseSearch, tx, tx.getFrom().getStopPosition()); + /** + * This is public to allow the mappers to inject transfers + */ + public void addTransferConstraintsReverseSearch( + int targetStopPosition, + TransferForPattern transferForPattern + ) { + constrainedTransfersReverseSearch.add(targetStopPosition, transferForPattern); } - private static void add(TIntObjectMap> index, T e, int pos) { - var list = index.get(pos); - if(list == null) { - list = new ArrayList<>(); - index.put(pos, list); - } - list.add(e); + /** + * This method should be called AFTER all transfers are added, and before the + * pattern is used in a Raptor search. + */ + public void sortConstrainedTransfers() { + constrainedTransfersForwardSearch.sortOnSpecificityRanking(); + constrainedTransfersReverseSearch.sortOnSpecificityRanking(); } } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/request/ConstrainedBoardingSearch.java b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/ConstrainedBoardingSearch.java similarity index 53% rename from src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/request/ConstrainedBoardingSearch.java rename to src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/ConstrainedBoardingSearch.java index 9c9d5b7527b..a51a8ed358a 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/request/ConstrainedBoardingSearch.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/ConstrainedBoardingSearch.java @@ -1,15 +1,14 @@ -package org.opentripplanner.routing.algorithm.raptor.transit.request; +package org.opentripplanner.routing.algorithm.raptor.transit.constrainedtransfer; -import gnu.trove.map.TIntObjectMap; -import java.util.Collection; import java.util.List; import java.util.stream.Collectors; +import org.opentripplanner.common.model.T2; import org.opentripplanner.model.Trip; -import org.opentripplanner.model.transfer.ConstrainedTransfer; import org.opentripplanner.model.transfer.TransferConstraint; import org.opentripplanner.routing.algorithm.raptor.transit.TripSchedule; import org.opentripplanner.transit.raptor.api.transit.RaptorConstrainedTripScheduleBoardingSearch; import org.opentripplanner.transit.raptor.api.transit.RaptorTimeTable; +import org.opentripplanner.transit.raptor.api.transit.RaptorTransferConstraint; import org.opentripplanner.transit.raptor.api.transit.RaptorTripScheduleBoardOrAlightEvent; @@ -26,22 +25,23 @@ public final class ConstrainedBoardingSearch private static final ConstrainedBoardingSearchStrategy FORWARD_STRATEGY = new ConstrainedBoardingSearchForward(); private static final ConstrainedBoardingSearchStrategy REVERSE_STRATEGY = new ConstrainedBoardingSearchReverse(); + /** Handle forward and reverse specific tasks */ private final ConstrainedBoardingSearchStrategy translator; /** * List of transfers for each stop position in pattern */ - private final TIntObjectMap> transfers; + private final TransferForPatternByStopPos transfers; - private List currentTransfers; + private List currentTransfers; private int currentTargetStopPos; public ConstrainedBoardingSearch( boolean forwardSearch, - TIntObjectMap> transfers + TransferForPatternByStopPos transfers ) { - this.translator = forwardSearch ? FORWARD_STRATEGY : REVERSE_STRATEGY; this.transfers = transfers; + this.translator = forwardSearch ? FORWARD_STRATEGY : REVERSE_STRATEGY; } @Override @@ -61,18 +61,13 @@ public RaptorTripScheduleBoardOrAlightEvent find( int sourceStopIndex, int sourceArrivalTime ) { - final Trip sourceTrip = sourceTripSchedule.getOriginalTripTimes().getTrip(); - final int sourceStopPos = translator.findSourceStopPosition( - sourceTripSchedule, sourceArrivalTime, sourceStopIndex - ); + var transfers = findMatchingTransfers(sourceTripSchedule, sourceStopIndex); - var list = findMatchingTransfers(sourceTrip, sourceStopPos); + if(transfers.isEmpty()) { return null; } - if(list.isEmpty()) { return null; } - - var tripInfo = translator.findTimetableTripInfo( + T2 tripInfo = findTimetableTripInfo( timetable, - list, + transfers, currentTargetStopPos, sourceArrivalTime ); @@ -80,7 +75,7 @@ public RaptorTripScheduleBoardOrAlightEvent find( if(tripInfo == null) { return null; } final int tripIndex = tripInfo.first; - final TransferConstraint transferConstraint = tripInfo.second; + final var transferConstraint = tripInfo.second; var trip = timetable.getTripSchedule(tripIndex); int departureTime = translator.time(trip, currentTargetStopPos); @@ -90,12 +85,56 @@ public RaptorTripScheduleBoardOrAlightEvent find( ); } - private Collection findMatchingTransfers( - Trip sourceTrip, - int sourceStopPos + private List findMatchingTransfers( + TripSchedule tripSchedule, + int stopIndex ) { + final Trip trip = tripSchedule.getOriginalTripTimes().getTrip(); return currentTransfers.stream() - .filter(tx -> translator.source(tx).matches(sourceTrip, sourceStopPos)) + .filter(t -> t.matchesSourcePoint(stopIndex, trip)) .collect(Collectors.toList()); } + + /** + * Find the trip to board (trip index) and the transfer constraint + */ + public T2 findTimetableTripInfo( + RaptorTimeTable timetable, + List transfers, + int stopPos, + int sourceTime + ) { + // Abort after 6 hours + boolean useNextNormalTrip = false; + + var index = translator.scheduleIndexIterator(timetable); + outer: + while (index.hasNext()) { + final int i = index.next(); + var it = timetable.getTripSchedule(i); + int time = translator.time(it, stopPos); + if (translator.timeIsBefore(time, sourceTime)) { continue; } + + var targetTrip = it.getOriginalTripTimes().getTrip(); + + for (TransferForPattern tx : transfers) { + if (tx.applyToAllTargetTrips()) { + return new T2<>(i, tx.getTransferConstraint()); + } + else if (tx.applyToTargetTrip(targetTrip)) { + if (tx.getTransferConstraint().isNotAllowed()) { + useNextNormalTrip = true; + continue outer; + } + else { + return new T2<>(i, tx.getTransferConstraint()); + } + } + } + if (useNextNormalTrip) { + return new T2<>(i, TransferConstraint.REGULAR_TRANSFER); + } + } + return null; + } } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/ConstrainedBoardingSearchForward.java b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/ConstrainedBoardingSearchForward.java new file mode 100644 index 00000000000..9493baec4b4 --- /dev/null +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/ConstrainedBoardingSearchForward.java @@ -0,0 +1,27 @@ +package org.opentripplanner.routing.algorithm.raptor.transit.constrainedtransfer; + +import org.opentripplanner.routing.algorithm.raptor.transit.TripSchedule; +import org.opentripplanner.transit.raptor.api.transit.IntIterator; +import org.opentripplanner.transit.raptor.api.transit.RaptorTimeTable; +import org.opentripplanner.transit.raptor.api.transit.RaptorTripSchedule; +import org.opentripplanner.transit.raptor.util.IntIterators; + +class ConstrainedBoardingSearchForward + implements ConstrainedBoardingSearchStrategy { + + @Override + public int time(RaptorTripSchedule schedule, int stopPos) { + return schedule.departure(stopPos); + } + + @Override + public boolean timeIsBefore(int time0, int time1) { + return time0 < time1; + } + + @Override + public IntIterator scheduleIndexIterator(RaptorTimeTable timetable) { + return IntIterators.intIncIterator(0, timetable.numberOfTripSchedules()); + } +} + diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/ConstrainedBoardingSearchReverse.java b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/ConstrainedBoardingSearchReverse.java new file mode 100644 index 00000000000..274977c9b6e --- /dev/null +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/ConstrainedBoardingSearchReverse.java @@ -0,0 +1,26 @@ +package org.opentripplanner.routing.algorithm.raptor.transit.constrainedtransfer; + +import org.opentripplanner.routing.algorithm.raptor.transit.TripSchedule; +import org.opentripplanner.transit.raptor.api.transit.IntIterator; +import org.opentripplanner.transit.raptor.api.transit.RaptorTimeTable; +import org.opentripplanner.transit.raptor.api.transit.RaptorTripSchedule; +import org.opentripplanner.transit.raptor.util.IntIterators; + +class ConstrainedBoardingSearchReverse implements ConstrainedBoardingSearchStrategy { + + @Override + public int time(RaptorTripSchedule schedule, int stopPos) { + return schedule.arrival(stopPos); + } + + @Override + public boolean timeIsBefore(int time0, int time1) { + return time0 > time1; + } + + @Override + public IntIterator scheduleIndexIterator(RaptorTimeTable timetable) { + return IntIterators.intDecIterator(timetable.numberOfTripSchedules(), 0); + } + +} diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/ConstrainedBoardingSearchStrategy.java b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/ConstrainedBoardingSearchStrategy.java new file mode 100644 index 00000000000..2f81bf1dc93 --- /dev/null +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/ConstrainedBoardingSearchStrategy.java @@ -0,0 +1,17 @@ +package org.opentripplanner.routing.algorithm.raptor.transit.constrainedtransfer; + +import org.opentripplanner.routing.algorithm.raptor.transit.TripSchedule; +import org.opentripplanner.transit.raptor.api.transit.IntIterator; +import org.opentripplanner.transit.raptor.api.transit.RaptorTimeTable; +import org.opentripplanner.transit.raptor.api.transit.RaptorTripSchedule; + +interface ConstrainedBoardingSearchStrategy { + + int time(RaptorTripSchedule schedule, int stopPos); + + boolean timeIsBefore(int time0, int time1); + + //int findSourceStopPosition(RaptorTripSchedule schedule, int timeLimit, int stop); + + IntIterator scheduleIndexIterator(RaptorTimeTable timetable); +} diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/request/ConstrainedTransferBoarding.java b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/ConstrainedTransferBoarding.java similarity index 72% rename from src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/request/ConstrainedTransferBoarding.java rename to src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/ConstrainedTransferBoarding.java index a205e30242f..6022b37cf65 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/request/ConstrainedTransferBoarding.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/ConstrainedTransferBoarding.java @@ -1,22 +1,24 @@ -package org.opentripplanner.routing.algorithm.raptor.transit.request; +package org.opentripplanner.routing.algorithm.raptor.transit.constrainedtransfer; import javax.validation.constraints.NotNull; -import org.opentripplanner.model.transfer.TransferConstraint; +import org.opentripplanner.transit.raptor.api.transit.RaptorTransferConstraint; import org.opentripplanner.transit.raptor.api.transit.RaptorTripSchedule; import org.opentripplanner.transit.raptor.api.transit.RaptorTripScheduleBoardOrAlightEvent; - +/** + * A boarding event passed to Raptor to perform a boarding. + */ public class ConstrainedTransferBoarding implements RaptorTripScheduleBoardOrAlightEvent { - private final TransferConstraint constraint; + private final RaptorTransferConstraint constraint; private final int tripIndex; private final T trip; private final int stopPositionInPattern; private final int time; ConstrainedTransferBoarding( - @NotNull TransferConstraint constraint, + @NotNull RaptorTransferConstraint constraint, int tripIndex, @NotNull T trip, int stopPositionInPattern, @@ -44,7 +46,5 @@ public class ConstrainedTransferBoarding @Override @NotNull - public TransferConstraint getTransferConstraint() { - return constraint; - } + public RaptorTransferConstraint getTransferConstraint() { return constraint; } } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/TransferForPattern.java b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/TransferForPattern.java new file mode 100644 index 00000000000..25211761020 --- /dev/null +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/TransferForPattern.java @@ -0,0 +1,70 @@ +package org.opentripplanner.routing.algorithm.raptor.transit.constrainedtransfer; + +import javax.annotation.Nullable; +import org.opentripplanner.model.Trip; +import org.opentripplanner.transit.raptor.api.transit.RaptorTransferConstraint; + + +/** + * + */ +public class TransferForPattern implements Comparable { + + /** + * Used to filter transfers based on the source-stop-arrival. + */ + private final TransferPointForPattern sourcePoint; + + /** + * If {@code null} the constraint apply to all trips + */ + @Nullable + private final Trip targetTrip; + private final RaptorTransferConstraint transferConstraint; + private final int specificityRanking; + + TransferForPattern( + TransferPointForPattern sourcePoint, + @Nullable Trip targetTrip, + int specificityRanking, + RaptorTransferConstraint transferConstraint + ) { + this.sourcePoint = sourcePoint; + this.targetTrip = targetTrip; + this.specificityRanking = specificityRanking; + this.transferConstraint = transferConstraint; + } + + public RaptorTransferConstraint getTransferConstraint() { + return transferConstraint; + } + + public boolean matchesSourcePoint(int stopIndex, Trip trip) { + return sourcePoint.match(stopIndex, trip); + } + + /** + * A transfer either apply to all target-trips (station-, stop- and route-transfer-points) or + * to a specific trip (trip-transfer-point). + */ + public boolean applyToAllTargetTrips() { + return targetTrip == null; + } + + /** + * return {@code true} if this transfer apply to the specified trip, and only that trip. + * @see #applyToAllTargetTrips() + */ + public boolean applyToTargetTrip(Trip targetTrip) { + return this.targetTrip == targetTrip; + } + + /** + * Transfers should be sorted after the specificityRanking, this make sure the + * transfer with the highest ranking is used by raptor. + */ + @Override + public int compareTo(TransferForPattern o) { + return o.specificityRanking - specificityRanking; + } +} diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/TransferForPatternByStopPos.java b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/TransferForPatternByStopPos.java new file mode 100644 index 00000000000..e83c46f01c4 --- /dev/null +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/TransferForPatternByStopPos.java @@ -0,0 +1,34 @@ +package org.opentripplanner.routing.algorithm.raptor.transit.constrainedtransfer; + + +import gnu.trove.map.TIntObjectMap; +import gnu.trove.map.hash.TIntObjectHashMap; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class TransferForPatternByStopPos { + + private final TIntObjectMap> transfers = new TIntObjectHashMap<>(); + + + /** + * Sort in decreasing specificityRanking order + */ + public void sortOnSpecificityRanking() { + transfers.forEachValue(it -> { Collections.sort(it); return true; }); + } + + public void add(int targetStopPos, TransferForPattern transfer) { + var c = transfers.get(targetStopPos); + if(c == null) { + c = new ArrayList<>(); + transfers.put(targetStopPos, c); + } + c.add(transfer); + } + + public List get(int targetStopPos) { + return transfers.get(targetStopPos); + } +} \ No newline at end of file diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/TransferIndexGenerator.java b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/TransferIndexGenerator.java new file mode 100644 index 00000000000..c19df86c8ab --- /dev/null +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/TransferIndexGenerator.java @@ -0,0 +1,229 @@ +package org.opentripplanner.routing.algorithm.raptor.transit.constrainedtransfer; + +import static org.opentripplanner.routing.algorithm.raptor.transit.constrainedtransfer.TransferPointForPatternFactory.createTransferPointForPattern; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +import org.opentripplanner.model.Route; +import org.opentripplanner.model.Station; +import org.opentripplanner.model.Stop; +import org.opentripplanner.model.Trip; +import org.opentripplanner.model.TripPattern; +import org.opentripplanner.model.transfer.ConstrainedTransfer; +import org.opentripplanner.model.transfer.RouteTransferPoint; +import org.opentripplanner.model.transfer.StationTransferPoint; +import org.opentripplanner.model.transfer.StopTransferPoint; +import org.opentripplanner.model.transfer.TransferPoint; +import org.opentripplanner.model.transfer.TripTransferPoint; +import org.opentripplanner.routing.algorithm.raptor.transit.StopIndexForRaptor; +import org.opentripplanner.routing.algorithm.raptor.transit.TripPatternWithRaptorStopIndexes; + +public class TransferIndexGenerator { + + private final Collection constrainedTransfers; + private final Map> patternsByStation = new HashMap<>(); + private final Map> patternsByStop = new HashMap<>(); + private final Map> patternsByRoute = new HashMap<>(); + private final Map> patternsByTrip = new HashMap<>(); + private final StopIndexForRaptor stopIndex; + + public TransferIndexGenerator( + Collection constrainedTransfers, + Collection tripPatterns, + StopIndexForRaptor stopIndex + ) { + this.constrainedTransfers = constrainedTransfers; + this.stopIndex = stopIndex; + setupPatternByTripIndex(tripPatterns); + } + + public void generateTransfers() { + for (ConstrainedTransfer tx : constrainedTransfers) { + var c = tx.getTransferConstraint(); + // Only add transfers witch have an effect on the Raptor routing here. + // Some transfers only have the priority set, and that is used in optimized- + // transfers, but not in Raptor. + if (!c.useInRaptorRouting()) { continue; } + + for (var fromPoint : findTPoints(tx.getFrom())) { + if (fromPoint.canAlight()) { + for (var toPoint : findTPoints(tx.getTo())) { + if (toPoint.canBoard() && !fromPoint.equals(toPoint)) { + fromPoint.addTransferConstraints(tx, toPoint); + } + } + } + } + } + sortAllTransfersByRanking(); + } + + private void sortAllTransfersByRanking() { + for (var patterns : patternsByRoute.values()) { + for (var pattern : patterns) { + pattern.sortConstrainedTransfers(); + } + } + } + + private void setupPatternByTripIndex(Collection tripPatterns) { + for (TripPatternWithRaptorStopIndexes pattern : tripPatterns) { + TripPattern tripPattern = pattern.getPattern(); + + patternsByRoute + .computeIfAbsent(tripPattern.getRoute(), t -> new ArrayList<>()) + .add(pattern); + + for (Trip trip : tripPattern.getTrips()) { + patternsByTrip.computeIfAbsent(trip, t -> new ArrayList<>()).add(pattern); + } + + for (Stop stop : tripPattern.getStops()) { + patternsByStop.computeIfAbsent(stop, t -> new ArrayList<>()).add(pattern); + Station station = stop.getParentStation(); + if (station != null) { + patternsByStation.computeIfAbsent(station, t -> new ArrayList<>()).add(pattern); + } + } + } + } + + private Collection findTPoints(TransferPoint txPoint) { + if (txPoint.isStationTransferPoint()) { + return findTPoints(txPoint.asStationTransferPoint()); + } + else if (txPoint.isStopTransferPoint()) { + return findTPoints(txPoint.asStopTransferPoint()); + } + else if (txPoint.isRouteTransferPoint()) { + return findTPoint(txPoint.asRouteTransferPoint()); + } + else { + return findTPoints(txPoint.asTripTransferPoint()); + } + } + + private List findTPoints(StationTransferPoint point) { + var station = point.getStation(); + var patterns = patternsByStation.get(station); + var sourcePoint = createTransferPointForPattern(station, stopIndex); + var result = new ArrayList(); + + for (TripPatternWithRaptorStopIndexes pattern : patterns) { + Stop[] stops = pattern.getPattern().getStopPattern().getStops(); + for (int pos = 0; pos < stops.length; ++pos) { + if (point.getStation() == stops[pos].getParentStation()) { + result.add(new TPoint(pattern, sourcePoint, null, pos)); + } + } + } + return result; + } + + private List findTPoints(StopTransferPoint point) { + var stop = point.asStopTransferPoint().getStop(); + var patterns = patternsByStop.get(stop); + var sourcePoint = createTransferPointForPattern(stopIndex.indexOf(stop)); + + var result = new ArrayList(); + for (TripPatternWithRaptorStopIndexes pattern : patterns) { + Stop[] stops = pattern.getPattern().getStopPattern().getStops(); + for (int pos = 0; pos < stops.length; ++pos) { + if (point.getStop() == stops[pos]) { + result.add(new TPoint(pattern, sourcePoint, null, pos)); + } + } + } + return result; + } + + private List findTPoint(RouteTransferPoint point) { + var route = point.getRoute(); + var patterns = patternsByRoute.get(route); + int stopPosInPattern = point.getStopPositionInPattern(); + int stopIndex = patterns.get(0).stopIndex(stopPosInPattern); + var sourcePoint = createTransferPointForPattern(route, stopIndex); + return patterns.stream() + .map(p -> new TPoint(p, sourcePoint, null, stopPosInPattern)) + .collect(Collectors.toList()); + } + + private List findTPoints(TripTransferPoint point) { + var trip = point.getTrip(); + var patterns = patternsByTrip.get(trip); + int stopPosInPattern = point.getStopPositionInPattern(); + int stopIndex = patterns.get(0).stopIndex(stopPosInPattern); + var sourcePoint = createTransferPointForPattern(trip, stopIndex); + return patterns.stream() + .map(p -> new TPoint(p, sourcePoint, trip, stopPosInPattern)) + .collect(Collectors.toList()); + } + + private static class TPoint { + TripPatternWithRaptorStopIndexes pattern; + TransferPointForPattern sourcePoint; + Trip trip; + int stopPosition; + + private TPoint( + TripPatternWithRaptorStopIndexes pattern, + TransferPointForPattern sourcePoint, + Trip trip, + int stopPosition + ) { + this.pattern = pattern; + this.sourcePoint = sourcePoint; + this.trip = trip; + this.stopPosition = stopPosition; + } + + boolean canBoard() { + // We prevent boarding at the last stop, this might be enforced by the + // canBoard method, but we do not trust it here. + int lastStopPosition = pattern.getPattern().getStopPattern().getSize() - 1; + return stopPosition != lastStopPosition && pattern.getPattern().canBoard(stopPosition); + } + + boolean canAlight() { + // We prevent alighting at the first stop, this might be enforced by the + // canAlight method, but we do not trust it here. + return stopPosition != 0 && pattern.getPattern().canAlight(stopPosition); + } + + void addTransferConstraints(ConstrainedTransfer tx, TPoint to) { + int rank = tx.getSpecificityRanking(); + var c = tx.getTransferConstraint(); + + // Forward search + to.pattern.addTransferConstraintsForwardSearch( + to.stopPosition, + new TransferForPattern(sourcePoint, to.trip, rank, c) + ); + // Reverse search + pattern.addTransferConstraintsReverseSearch( + stopPosition, + new TransferForPattern(to.sourcePoint, trip, rank, c) + ); + } + + @Override + public boolean equals(Object o) { + if (this == o) {return true;} + if (!(o instanceof TPoint)) {return false;} + final TPoint tPoint = (TPoint) o; + return stopPosition == tPoint.stopPosition + && Objects.equals(pattern, tPoint.pattern) + && Objects.equals(trip, tPoint.trip); + } + + @Override + public int hashCode() { + return Objects.hash(pattern, trip, stopPosition); + } + } +} diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/TransferPointForPattern.java b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/TransferPointForPattern.java new file mode 100644 index 00000000000..d9882a0a81d --- /dev/null +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/TransferPointForPattern.java @@ -0,0 +1,8 @@ +package org.opentripplanner.routing.algorithm.raptor.transit.constrainedtransfer; + +import org.opentripplanner.model.Trip; + +interface TransferPointForPattern { + + boolean match(int stopIndex, Trip trip); +} diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/TransferPointForPatternFactory.java b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/TransferPointForPatternFactory.java new file mode 100644 index 00000000000..4365e214aa7 --- /dev/null +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/TransferPointForPatternFactory.java @@ -0,0 +1,105 @@ +package org.opentripplanner.routing.algorithm.raptor.transit.constrainedtransfer; + +import java.util.function.IntFunction; +import org.opentripplanner.model.Route; +import org.opentripplanner.model.Station; +import org.opentripplanner.model.Stop; +import org.opentripplanner.model.Trip; +import org.opentripplanner.routing.algorithm.raptor.transit.StopIndexForRaptor; + + +/** + * This class generate TransferPoints adapted to Raptor. The internal model + * {@link org.opentripplanner.model.transfer.TransferPoint} can not be used by Raptor as is, so + * we transform them into {@link TransferPointForPattern}. For example to speed ut the search in + * Raptor we avoid fetching Stops from memory and instead uses a {@code stopIndex}. This index is + * not necessarily fixed, but generated for the + * {@link org.opentripplanner.routing.algorithm.raptor.transit.TransitLayer}, so we need to + * generate + */ + + +final class TransferPointForPatternFactory { + + /** private constructor to prevent utility class from instantiation */ + private TransferPointForPatternFactory() { /* empty */ } + + static TransferPointForPattern createTransferPointForPattern( + Station station, + StopIndexForRaptor stopIndex + ) { + return new StationSP(stopIndex::stopByIndex, station); + } + + static TransferPointForPattern createTransferPointForPattern(int stopIndex) { + return new StopSP(stopIndex); + } + + static TransferPointForPattern createTransferPointForPattern(Route route, int sourceStopIndex) { + return new RouteSP(route, sourceStopIndex); + } + + static TransferPointForPattern createTransferPointForPattern(Trip trip, int sourceStopIndex) { + return new TripSP(trip, sourceStopIndex); + } + + private static class StationSP implements TransferPointForPattern { + + private final IntFunction toStop; + private final Station station; + + private StationSP(IntFunction toStop, Station station) { + this.toStop = toStop; + this.station = station; + } + + @Override + public boolean match(int stopIndex, Trip trip) { + return station == toStop.apply(stopIndex).getParentStation(); + } + } + + private static class StopSP implements TransferPointForPattern { + + private final int stopIndex; + + private StopSP(int stopIndex) {this.stopIndex = stopIndex;} + + @Override + public boolean match(int stopIndex, Trip trip) { + return this.stopIndex == stopIndex; + } + } + + private static class RouteSP implements TransferPointForPattern { + + private final Route route; + private final int stopIndex; + + private RouteSP(Route route, int stopIndex) { + this.route = route; + this.stopIndex = stopIndex; + } + + @Override + public boolean match(int stopIndex, Trip trip) { + return this.stopIndex == stopIndex && this.route == trip.getRoute(); + } + } + + private static class TripSP implements TransferPointForPattern { + + private final Trip trip; + private final int stopIndex; + + private TripSP(Trip trip, int stopIndex) { + this.trip = trip; + this.stopIndex = stopIndex; + } + + @Override + public boolean match(int stopIndex, Trip trip) { + return this.stopIndex == stopIndex && this.trip == trip; + } + } +} 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 882ced15f3a..c41fc8090d8 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 @@ -1,5 +1,9 @@ package org.opentripplanner.routing.algorithm.raptor.transit.mappers; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; import org.opentripplanner.ext.flex.FlexAccessEgress; import org.opentripplanner.model.Stop; import org.opentripplanner.routing.algorithm.raptor.transit.AccessEgress; @@ -7,11 +11,6 @@ import org.opentripplanner.routing.algorithm.raptor.transit.StopIndexForRaptor; import org.opentripplanner.routing.graphfinder.NearbyStop; -import java.util.Collection; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - public class AccessEgressMapper { private final StopIndexForRaptor stopIndex; @@ -23,7 +22,9 @@ public AccessEgressMapper(StopIndexForRaptor stopIndex) { public AccessEgress mapNearbyStop(NearbyStop nearbyStop, boolean isEgress) { if (!(nearbyStop.stop instanceof Stop)) { return null; } return new AccessEgress( - stopIndex.indexByStop.get(nearbyStop.stop), + // TODO: This cast is potentially causing problems look into if it is possible + // to improve the code and get rid of the cast + stopIndex.indexOf((Stop)nearbyStop.stop), isEgress ? nearbyStop.state.reverse() : nearbyStop.state ); } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/mappers/TransferIndexGenerator.java b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/mappers/TransferIndexGenerator.java deleted file mode 100644 index f024ea9c8a2..00000000000 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/mappers/TransferIndexGenerator.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.opentripplanner.routing.algorithm.raptor.transit.mappers; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import org.opentripplanner.model.Trip; -import org.opentripplanner.model.transfer.ConstrainedTransfer; -import org.opentripplanner.model.transfer.TransferService; -import org.opentripplanner.routing.algorithm.raptor.transit.TripPatternWithRaptorStopIndexes; - -public class TransferIndexGenerator { - private final TransferService transferService; - private final Map patternByTrip = new HashMap<>(); - - private TransferIndexGenerator(TransferService transferService) { - this.transferService = transferService; - } - - public static void generateTransfers( - TransferService transferService, - Collection tripPatterns - ) { - var generator = new TransferIndexGenerator(transferService); - - generator.setupPatternByTripIndex(tripPatterns); - - for (TripPatternWithRaptorStopIndexes pattern : tripPatterns) { - generator.generateTransfers(pattern); - } - } - - private void setupPatternByTripIndex(Collection tripPatterns) { - for (TripPatternWithRaptorStopIndexes pattern : tripPatterns) { - for (Trip trip : pattern.getPattern().getTrips()) { - patternByTrip.put(trip, pattern); - } - } - } - - private void generateTransfers(TripPatternWithRaptorStopIndexes pattern) { - for (Trip trip : pattern.getPattern().getTrips()) { - int nStops = pattern.getPattern().getStops().size(); - for (int stopPos=0; stopPos < nStops; ++stopPos) { - var transfers= transferService.listConstrainedTransfersTo(trip, stopPos); - for (ConstrainedTransfer tx : transfers) { - var c = tx.getTransferConstraint(); - if(c.isFacilitated()) { - var fromTrip = tx.getFrom().getTrip(); - var toTrip = tx.getTo().getTrip(); - if (fromTrip != null && toTrip != null) { - var fromPattern = patternByTrip.get(fromTrip); - if (fromPattern != null) { - pattern.addTransferConstraintsForwardSearch(tx); - fromPattern.addTransferConstraintsReverseSearch(tx); - } - } - } - } - } - } - } -} diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/mappers/TransfersMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/mappers/TransfersMapper.java index 47ec4e5a97f..955979ed4a2 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/mappers/TransfersMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/mappers/TransfersMapper.java @@ -21,14 +21,14 @@ static List> mapTransfers( List> transferByStopIndex = new ArrayList<>(); - for (int i = 0; i < stopIndex.stopsByIndex.size(); ++i) { - Stop stop = stopIndex.stopsByIndex.get(i); + for (int i = 0; i < stopIndex.size(); ++i) { + Stop stop = stopIndex.stopByIndex(i); ArrayList list = new ArrayList<>(); transferByStopIndex.add(list); for (PathTransfer pathTransfer : transfersByStop.get(stop)) { if (pathTransfer.to instanceof Stop) { - int toStopIndex = stopIndex.indexByStop.get(pathTransfer.to); + int toStopIndex = stopIndex.indexOf((Stop)pathTransfer.to); Transfer newTransfer; if (pathTransfer.getEdges() != null) { newTransfer = new Transfer( diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/mappers/TransitLayerMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/mappers/TransitLayerMapper.java index 7eb269f352f..77362128a66 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/mappers/TransitLayerMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/mappers/TransitLayerMapper.java @@ -23,6 +23,7 @@ import org.opentripplanner.routing.algorithm.raptor.transit.TransitTuningParameters; import org.opentripplanner.routing.algorithm.raptor.transit.TripPatternForDate; import org.opentripplanner.routing.algorithm.raptor.transit.TripPatternWithRaptorStopIndexes; +import org.opentripplanner.routing.algorithm.raptor.transit.constrainedtransfer.TransferIndexGenerator; import org.opentripplanner.routing.algorithm.raptor.transit.request.RaptorRequestTransferCache; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.trippattern.TripTimes; @@ -76,10 +77,11 @@ private TransitLayer map(TransitTuningParameters tuningParameters) { transferByStopIndex = mapTransfers(stopIndex, graph.transfersByStop); if(OTPFeature.TransferConstraints.isOn()) { - TransferIndexGenerator.generateTransfers( - graph.getTransferService(), - newTripPatternForOld.values() - ); + new TransferIndexGenerator( + graph.getTransferService().listAll(), + newTripPatternForOld.values(), + stopIndex + ).generateTransfers(); } var transferCache = new RaptorRequestTransferCache(tuningParameters.transferCacheMaxSize()); diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/mappers/TripPatternMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/mappers/TripPatternMapper.java index 6aaf89bef0a..fbd10852057 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/mappers/TripPatternMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/mappers/TripPatternMapper.java @@ -1,12 +1,11 @@ package org.opentripplanner.routing.algorithm.raptor.transit.mappers; -import org.opentripplanner.model.TripPattern; -import org.opentripplanner.routing.algorithm.raptor.transit.StopIndexForRaptor; -import org.opentripplanner.routing.algorithm.raptor.transit.TripPatternWithRaptorStopIndexes; - import java.util.Collection; import java.util.HashMap; import java.util.Map; +import org.opentripplanner.model.TripPattern; +import org.opentripplanner.routing.algorithm.raptor.transit.StopIndexForRaptor; +import org.opentripplanner.routing.algorithm.raptor.transit.TripPatternWithRaptorStopIndexes; public class TripPatternMapper { @@ -23,8 +22,8 @@ static Map mapOldTripPatternToRap for (TripPattern oldTripPattern : oldTripPatterns) { TripPatternWithRaptorStopIndexes newTripPattern = new TripPatternWithRaptorStopIndexes( - stopIndex.listStopIndexesForStops(oldTripPattern.getStopPattern().getStops()), - oldTripPattern + oldTripPattern, + stopIndex.listStopIndexesForStops(oldTripPattern.getStopPattern().getStops()) ); newTripPatternForOld.put(oldTripPattern, newTripPattern); } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/request/ConstrainedBoardingSearchForward.java b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/request/ConstrainedBoardingSearchForward.java deleted file mode 100644 index 98c497c17b2..00000000000 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/request/ConstrainedBoardingSearchForward.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.opentripplanner.routing.algorithm.raptor.transit.request; - -import java.util.Collection; -import org.opentripplanner.common.model.T2; -import org.opentripplanner.model.transfer.ConstrainedTransfer; -import org.opentripplanner.model.transfer.TransferConstraint; -import org.opentripplanner.model.transfer.TransferPoint; -import org.opentripplanner.routing.algorithm.raptor.transit.TripSchedule; -import org.opentripplanner.transit.raptor.api.transit.RaptorTimeTable; -import org.opentripplanner.transit.raptor.api.transit.RaptorTripSchedule; - -class ConstrainedBoardingSearchForward - implements ConstrainedBoardingSearchStrategy { - - @Override - public TransferPoint source(ConstrainedTransfer tx) {return tx.getFrom();} - - @Override - public int time(RaptorTripSchedule schedule, int stopPos) { - return schedule.departure(stopPos); - } - - @Override - public int findSourceStopPosition(RaptorTripSchedule schedule, int timeLimit, int stop) { - return schedule.findArrivalStopPosition(timeLimit, stop); - } - - @Override - public T2 findTimetableTripInfo( - RaptorTimeTable timetable, - Collection transfers, - int stopPos, - int sourceArrivalTime - ) { - // Abort after 6 hours - boolean boardNextNormalTrip = false; - - outer: - for (int i = 0; i < timetable.numberOfTripSchedules(); ++i) { - var it = timetable.getTripSchedule(i); - int departureTime = it.departure(stopPos); - if (departureTime < sourceArrivalTime) { continue; } - - var targetTrip = it.getOriginalTripTimes().getTrip(); - - for (ConstrainedTransfer tx : transfers) { - if (tx.getTo().applyToAllTrips()) { - return new T2<>(i, tx.getTransferConstraint()); - } - if (targetTrip == tx.getTo().getTrip()) { - if (tx.getTransferConstraint().isNotAllowed()) { - boardNextNormalTrip = true; - continue outer; - } - else { - return new T2<>(i, tx.getTransferConstraint()); - } - } - } - if (boardNextNormalTrip) { - return new T2<>(i, TransferConstraint.REGULAR_TRANSFER); - } - } - return null; - } -} diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/request/ConstrainedBoardingSearchReverse.java b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/request/ConstrainedBoardingSearchReverse.java deleted file mode 100644 index dc2c004fef9..00000000000 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/request/ConstrainedBoardingSearchReverse.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.opentripplanner.routing.algorithm.raptor.transit.request; - -import java.util.Collection; -import org.opentripplanner.common.model.T2; -import org.opentripplanner.model.transfer.ConstrainedTransfer; -import org.opentripplanner.model.transfer.TransferConstraint; -import org.opentripplanner.model.transfer.TransferPoint; -import org.opentripplanner.routing.algorithm.raptor.transit.TripSchedule; -import org.opentripplanner.transit.raptor.api.transit.RaptorTimeTable; -import org.opentripplanner.transit.raptor.api.transit.RaptorTripSchedule; - -class ConstrainedBoardingSearchReverse - implements ConstrainedBoardingSearchStrategy { - - @Override - public TransferPoint source(ConstrainedTransfer tx) {return tx.getTo();} - - @Override - public int time(RaptorTripSchedule schedule, int stopPos) { - return schedule.arrival(stopPos); - } - - @Override - public int findSourceStopPosition(RaptorTripSchedule schedule, int timeLimit, int stop) { - return schedule.findDepartureStopPosition(timeLimit, stop); - } - - @Override - public T2 findTimetableTripInfo( - RaptorTimeTable timetable, - Collection transfers, - int stopPos, - int sourceDepartureTime - ) { - // Abort after 6 hours - boolean alightPrevNormalTrip = false; - - outer: - for (int i = timetable.numberOfTripSchedules()-1; i >= 0; --i) { - var it = timetable.getTripSchedule(i); - int arrivalTime = it.arrival(stopPos); - if (arrivalTime > sourceDepartureTime) { continue; } - - var targetTrip = it.getOriginalTripTimes().getTrip(); - - for (ConstrainedTransfer tx : transfers) { - if (tx.getFrom().applyToAllTrips()) { - return new T2<>(i, tx.getTransferConstraint()); - } - if (targetTrip == tx.getFrom().getTrip()) { - if (tx.getTransferConstraint().isNotAllowed()) { - alightPrevNormalTrip = true; - continue outer; - } - else { - return new T2<>(i, tx.getTransferConstraint()); - } - } - } - if (alightPrevNormalTrip) { - return new T2<>(i, TransferConstraint.REGULAR_TRANSFER); - } - } - return null; - } -} diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/request/ConstrainedBoardingSearchStrategy.java b/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/request/ConstrainedBoardingSearchStrategy.java deleted file mode 100644 index e4c0ff29d1b..00000000000 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptor/transit/request/ConstrainedBoardingSearchStrategy.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.opentripplanner.routing.algorithm.raptor.transit.request; - -import java.util.Collection; -import org.opentripplanner.common.model.T2; -import org.opentripplanner.model.transfer.ConstrainedTransfer; -import org.opentripplanner.model.transfer.TransferConstraint; -import org.opentripplanner.model.transfer.TransferPoint; -import org.opentripplanner.routing.algorithm.raptor.transit.TripSchedule; -import org.opentripplanner.transit.raptor.api.transit.RaptorTimeTable; -import org.opentripplanner.transit.raptor.api.transit.RaptorTripSchedule; - -interface ConstrainedBoardingSearchStrategy { - - TransferPoint source(ConstrainedTransfer tx); - - int time(RaptorTripSchedule schedule, int stopPos); - - int findSourceStopPosition(RaptorTripSchedule schedule, int timeLimit, int stop); - - /** - * Find the trip to board (trip index) and the transfer constraint - */ - T2 findTimetableTripInfo( - RaptorTimeTable timetable, - Collection transfers, - int stopPos, - int sourceTime - ); -} diff --git a/src/main/java/org/opentripplanner/transit/raptor/rangeraptor/RangeRaptorWorker.java b/src/main/java/org/opentripplanner/transit/raptor/rangeraptor/RangeRaptorWorker.java index 211bbb8b467..ef6c97b1ede 100644 --- a/src/main/java/org/opentripplanner/transit/raptor/rangeraptor/RangeRaptorWorker.java +++ b/src/main/java/org/opentripplanner/transit/raptor/rangeraptor/RangeRaptorWorker.java @@ -214,7 +214,7 @@ private void findAllTransitForRound() { var route = routeIterator.next(); var pattern = route.pattern(); var tripSearch = createTripSearch(route.timetable()); - var txService = enableTransferConstraints + var txSearch = enableTransferConstraints ? calculator.transferConstraintsSearch(route) : null; int alightSlack = slackProvider.alightSlack(pattern); @@ -239,7 +239,7 @@ private void findAllTransitForRound() { transitWorker.forEachBoarding(stopIndex, (int prevArrivalTime) -> { boolean handled = boardWithConstrainedTransfer( - txService, route.timetable(), stopIndex, stopPos + txSearch, route.timetable(), stopIndex, stopPos ); // Find the best trip and board [no guaranteed transfer exist] @@ -282,14 +282,14 @@ private void boardWithRegularTransfer( * trip search from execution. */ private boolean boardWithConstrainedTransfer( - RaptorConstrainedTripScheduleBoardingSearch txService, + RaptorConstrainedTripScheduleBoardingSearch txSearch, RaptorTimeTable targetTimetable, int targetStopIndex, int targetStopPos ) { if(!enableTransferConstraints) { return false; } - if(!txService.transferExist(targetStopPos)) { return false; } + if(!txSearch.transferExist(targetStopPos)) { return false; } // Get the previous transit stop arrival (transfer source) TransitArrival sourceStopArrival = transitWorker.previousTransit(targetStopIndex); @@ -302,7 +302,7 @@ private boolean boardWithConstrainedTransfer( slackProvider.alightSlack(sourceStopArrival.trip().pattern()) ); - var result = txService.find( + var result = txSearch.find( targetTimetable, sourceStopArrival.trip(), sourceStopArrival.stop(), diff --git a/src/test/java/org/opentripplanner/model/impl/OtpTransitServiceImplTest.java b/src/test/java/org/opentripplanner/model/impl/OtpTransitServiceImplTest.java index 250a7a90ce4..4320ed95500 100644 --- a/src/test/java/org/opentripplanner/model/impl/OtpTransitServiceImplTest.java +++ b/src/test/java/org/opentripplanner/model/impl/OtpTransitServiceImplTest.java @@ -105,12 +105,9 @@ public void testGetAllTransfers() { .collect(joining("\n")) ); - // There is 9 transfers, but because of the route to trip we get more - // TODO TGR - Support Route to trip expansion assertEquals( - //"Transfer{from: (route: 2, trip: 2.1, stopPos: 2), to: (route: 5, trip: 5.1, stopPos: 0), constraint: {guaranteed}}\n" - //+ "Transfer{from: (route: 2, trip: 2.2, stopPos: 2), to: (route: 5, trip: 5.1, stopPos: 0), constraint: {guaranteed}}\n" - "ConstrainedTransfer{from: , to: , constraint: {priority: RECOMMENDED}}\n" + "ConstrainedTransfer{from: , to: , constraint: {guaranteed}}\n" + + "ConstrainedTransfer{from: , to: , constraint: {priority: RECOMMENDED}}\n" + "ConstrainedTransfer{from: , to: , constraint: {priority: NOT_ALLOWED}}\n" + "ConstrainedTransfer{from: , to: , constraint: {priority: RECOMMENDED}}\n" + "ConstrainedTransfer{from: , to: , constraint: {priority: NOT_ALLOWED}}\n" diff --git a/src/test/java/org/opentripplanner/model/transfer/ConstrainedTransferTest.java b/src/test/java/org/opentripplanner/model/transfer/ConstrainedTransferTest.java index e741ff15a1f..3ebe606fd29 100644 --- a/src/test/java/org/opentripplanner/model/transfer/ConstrainedTransferTest.java +++ b/src/test/java/org/opentripplanner/model/transfer/ConstrainedTransferTest.java @@ -13,6 +13,8 @@ public class ConstrainedTransferTest implements TransferTestData { private static final TransferConstraint NO_CONSTRAINS = TransferConstraint.create().build(); private static final TransferConstraint GUARANTIED = TransferConstraint.create().guaranteed().build(); + private final ConstrainedTransfer TX_STATION_TO_A = new ConstrainedTransfer(null, STATION_POINT, STOP_POINT_A, NO_CONSTRAINS); + private final ConstrainedTransfer TX_T11_STATION = new ConstrainedTransfer(null, TRIP_POINT_11, STATION_POINT, NO_CONSTRAINS); private final ConstrainedTransfer TX_A_TO_B = new ConstrainedTransfer(null, STOP_POINT_A, STOP_POINT_B, NO_CONSTRAINS); private final ConstrainedTransfer TX_A_TO_R22 = new ConstrainedTransfer(null, STOP_POINT_A, ROUTE_POINT_22, NO_CONSTRAINS); private final ConstrainedTransfer TX_A_TO_T23 = new ConstrainedTransfer(null, STOP_POINT_A, TRIP_POINT_23, NO_CONSTRAINS); @@ -36,13 +38,15 @@ public void setup() { @Test public void getSpecificityRanking() { - assertEquals(0, TX_A_TO_B.getSpecificityRanking()); - assertEquals(1, TX_R11_TO_B.getSpecificityRanking()); - assertEquals(1, TX_A_TO_R22.getSpecificityRanking()); - assertEquals(2, TX_R11_TO_R22.getSpecificityRanking()); - assertEquals(2, TX_A_TO_T23.getSpecificityRanking()); - assertEquals(3, TX_T11_TO_R22.getSpecificityRanking()); - assertEquals(4, TX_T11_TO_T22.getSpecificityRanking()); + assertEquals(10, TX_STATION_TO_A.getSpecificityRanking()); + assertEquals(21, TX_A_TO_B.getSpecificityRanking()); + assertEquals(31, TX_A_TO_R22.getSpecificityRanking()); + assertEquals(32, TX_R11_TO_B.getSpecificityRanking()); + assertEquals(33, TX_T11_STATION.getSpecificityRanking()); + assertEquals(41, TX_A_TO_T23.getSpecificityRanking()); + assertEquals(42, TX_R11_TO_R22.getSpecificityRanking()); + assertEquals(53, TX_T11_TO_R22.getSpecificityRanking()); + assertEquals(63, TX_T11_TO_T22.getSpecificityRanking()); } @Test diff --git a/src/test/java/org/opentripplanner/model/transfer/TransferConstraintTest.java b/src/test/java/org/opentripplanner/model/transfer/TransferConstraintTest.java index 14a421b82fb..c51bce15d62 100644 --- a/src/test/java/org/opentripplanner/model/transfer/TransferConstraintTest.java +++ b/src/test/java/org/opentripplanner/model/transfer/TransferConstraintTest.java @@ -48,6 +48,14 @@ public void isFacilitated() { assertFalse(NOT_ALLOWED.isFacilitated()); } + @Test + public void useInRaptorRouting() { + assertTrue(GUARANTIED.useInRaptorRouting()); + assertTrue(STAY_SEATED.useInRaptorRouting()); + assertFalse(NO_CONSTRAINS.useInRaptorRouting()); + assertTrue(NOT_ALLOWED.useInRaptorRouting()); + } + @Test public void isNotAllowed() { assertTrue(NOT_ALLOWED.isNotAllowed()); diff --git a/src/test/java/org/opentripplanner/model/transfer/TransferPointMapTest.java b/src/test/java/org/opentripplanner/model/transfer/TransferPointMapTest.java new file mode 100644 index 00000000000..842fb9b0141 --- /dev/null +++ b/src/test/java/org/opentripplanner/model/transfer/TransferPointMapTest.java @@ -0,0 +1,48 @@ +package org.opentripplanner.model.transfer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class TransferPointMapTest implements TransferTestData { + + final TransferPointMap subject = new TransferPointMap<>(); + + @BeforeEach + void setup() { + STOP_A.setParentStation(STATION); + } + + @Test + void addAndGetEmptyMap() { + assertEquals(List.of(), subject.get(TRIP_1, STOP_A, STOP_POSITION_1)); + } + + @Test + void addAndGet() { + subject.put(TRIP_POINT_11, "A"); + subject.put(TRIP_POINT_23, "B"); + subject.put(ROUTE_POINT_11, "C"); + subject.put(ROUTE_POINT_22, "D"); + subject.put(STOP_POINT_A, "E"); + subject.put(STOP_POINT_B, "F"); + subject.put(STATION_POINT, "G"); + + assertEquals(List.of("A", "C", "E", "G"), subject.get(TRIP_1, STOP_A, STOP_POSITION_1)); + assertEquals(List.of("D", "F"), subject.get(TRIP_2, STOP_B, STOP_POSITION_2)); + } + + @Test + void computeIfAbsent() { + assertEquals("A", subject.computeIfAbsent(TRIP_POINT_11, () -> "A")); + assertEquals("B", subject.computeIfAbsent(ROUTE_POINT_11, () -> "B")); + assertEquals("C", subject.computeIfAbsent(STOP_POINT_B, () -> "C")); + assertEquals("D", subject.computeIfAbsent(STATION_POINT, () -> "D")); + assertEquals("B", subject.computeIfAbsent(ROUTE_POINT_11, () -> "E")); + + assertEquals(List.of("A", "B", "D"), subject.get(TRIP_1, STOP_A, STOP_POSITION_1)); + assertEquals(List.of("C"), subject.get(TRIP_2, STOP_B, STOP_POSITION_2)); + } +} \ No newline at end of file diff --git a/src/test/java/org/opentripplanner/model/transfer/TransferPointTest.java b/src/test/java/org/opentripplanner/model/transfer/TransferPointTest.java index 8a5b4787c6d..3c8db6d6474 100644 --- a/src/test/java/org/opentripplanner/model/transfer/TransferPointTest.java +++ b/src/test/java/org/opentripplanner/model/transfer/TransferPointTest.java @@ -1,86 +1,66 @@ package org.opentripplanner.model.transfer; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.Test; +import java.util.List; +import org.junit.jupiter.api.Test; public class TransferPointTest implements TransferTestData { - public static final int STOP_POSITION_1 = 1; - public static final int STOP_POSITION_2 = 2; - - private final TransferPoint otherPoint = new RouteTransferPoint(ROUTE_2, TRIP_2, STOP_POSITION_2); - + @Test + public void getStation() { + assertEquals(STATION, STATION_POINT.asStationTransferPoint().getStation()); + } @Test public void getStop() { - assertEquals(STOP_A, STOP_POINT_A.getStop()); - assertNull(TRIP_POINT_11.getStop()); - assertNull(ROUTE_POINT_11.getStop()); + assertEquals(STOP_A, STOP_POINT_A.asStopTransferPoint().getStop()); } @Test public void getTrip() { - assertNull(STOP_POINT_A.getTrip()); - assertEquals(TRIP_1, TRIP_POINT_11.getTrip()); - assertEquals(TRIP_1, ROUTE_POINT_11.getTrip()); + assertEquals(TRIP_1, TRIP_POINT_11.asTripTransferPoint().getTrip()); } @Test public void getStopPosition() { - assertEquals(TransferPoint.NOT_AVAILABLE, STOP_POINT_A.getStopPosition()); - assertEquals(STOP_POSITION_1, TRIP_POINT_11.getStopPosition()); - assertEquals(STOP_POSITION_1, ROUTE_POINT_11.getStopPosition()); + assertEquals(STOP_POSITION_1, TRIP_POINT_11.asTripTransferPoint().getStopPositionInPattern()); + assertEquals(STOP_POSITION_1, ROUTE_POINT_11.asRouteTransferPoint().getStopPositionInPattern()); } @Test public void getSpecificityRanking() { - assertEquals(0, STOP_POINT_A.getSpecificityRanking()); - assertEquals(1, ROUTE_POINT_11.getSpecificityRanking()); - assertEquals(2, TRIP_POINT_11.getSpecificityRanking()); + assertEquals(0, STATION_POINT.getSpecificityRanking()); + assertEquals(1, STOP_POINT_A.getSpecificityRanking()); + assertEquals(2, ROUTE_POINT_11.getSpecificityRanking()); + assertEquals(3, TRIP_POINT_11.getSpecificityRanking()); + } + + @Test + public void isStationTransferPoint() { + List.of(STATION_POINT, STOP_POINT_A, ROUTE_POINT_11, TRIP_POINT_11).forEach( p -> { + assertEquals(p == STATION_POINT, p.isStationTransferPoint()); + assertEquals(p == STOP_POINT_A, p.isStopTransferPoint()); + assertEquals(p == ROUTE_POINT_11, p.isRouteTransferPoint()); + assertEquals(p == TRIP_POINT_11, p.isTripTransferPoint()); + }); } @Test public void applyToAllTrips() { + assertTrue(STATION_POINT.applyToAllTrips()); assertTrue(STOP_POINT_A.applyToAllTrips()); assertTrue(ROUTE_POINT_11.applyToAllTrips()); assertFalse(TRIP_POINT_11.applyToAllTrips()); } - @Test - public void equalsAndHashCode() { - // A STOP_POINT_A should never match a route or trip point - assertNotEquals(STOP_POINT_A, ROUTE_POINT_11); - assertNotEquals(STOP_POINT_A, TRIP_POINT_11); - assertNotEquals(ROUTE_POINT_11, STOP_POINT_A); - assertNotEquals(TRIP_POINT_11, STOP_POINT_A); - - assertNotEquals(STOP_POINT_A.hashCode(), ROUTE_POINT_11.hashCode()); - assertNotEquals(STOP_POINT_A.hashCode(), TRIP_POINT_11.hashCode()); - assertNotEquals(ROUTE_POINT_11.hashCode(), STOP_POINT_A.hashCode()); - assertNotEquals(TRIP_POINT_11.hashCode(), STOP_POINT_A.hashCode()); - - // If the trip and stopPosition is the same then trip and route point should match - assertEquals(TRIP_POINT_11, ROUTE_POINT_11); - assertEquals(ROUTE_POINT_11, TRIP_POINT_11); - - assertEquals(TRIP_POINT_11.hashCode(), ROUTE_POINT_11.hashCode()); - assertEquals(ROUTE_POINT_11.hashCode(), TRIP_POINT_11.hashCode()); - - assertNotEquals(TRIP_POINT_11, otherPoint); - assertNotEquals(ROUTE_POINT_11, otherPoint); - assertNotEquals(TRIP_POINT_11.hashCode(), otherPoint.hashCode()); - assertNotEquals(ROUTE_POINT_11.hashCode(), otherPoint.hashCode()); - } - @Test public void testToString() { + assertEquals("", STATION_POINT.toString()); assertEquals("", STOP_POINT_A.toString()); - assertEquals("", ROUTE_POINT_11.toString()); - assertEquals("", TRIP_POINT_11.toString()); + assertEquals("", ROUTE_POINT_11.toString()); + assertEquals("", TRIP_POINT_23.toString()); } } \ No newline at end of file diff --git a/src/test/java/org/opentripplanner/model/transfer/TransferServiceTest.java b/src/test/java/org/opentripplanner/model/transfer/TransferServiceTest.java index 9fd1ba7ffc8..d66acff5e25 100644 --- a/src/test/java/org/opentripplanner/model/transfer/TransferServiceTest.java +++ b/src/test/java/org/opentripplanner/model/transfer/TransferServiceTest.java @@ -1,43 +1,54 @@ package org.opentripplanner.model.transfer; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opentripplanner.model.Stop; public class TransferServiceTest implements TransferTestData { private final TransferService subject = new TransferService(); + @BeforeEach + public void setup() { + STOP_A.setParentStation(STATION); + } @Test - public void addOneTransferForEachCombinationOfFromToTypesAndRetriveEachOfThem() { + public void findTransfer() { // Given: - var A = transfer(STOP_POINT_A, STOP_POINT_B); - var B = transfer(STOP_POINT_A, TRIP_POINT_23); - var C = transfer(ROUTE_POINT_11, STOP_POINT_B); - var D = transfer(TRIP_POINT_11, ROUTE_POINT_22); - var E = transfer(TRIP_POINT_11, TRIP_POINT_23); + var ANY_STOP = Stop.stopForTest("ANY", 67.0, 11.0); + var A = transfer(STATION_POINT, ROUTE_POINT_11); + var B = transfer(STOP_POINT_A, STOP_POINT_B); + var C = transfer(STOP_POINT_A, TRIP_POINT_23); + var D = transfer(ROUTE_POINT_11, STOP_POINT_B); + var E = transfer(TRIP_POINT_11, ROUTE_POINT_22); + var F = transfer(TRIP_POINT_11, TRIP_POINT_23); // When: all transfers is added to service - subject.addAll(List.of(A, B, C, D, E)); + subject.addAll(List.of(A, B, C, D, E, F)); - /* THEN */ + // Then: // Find the most specific transfer, Trip and stop position match - stops is ignored - assertEquals(D, subject.findTransfer(STOP_A, STOP_B, TRIP_1, TRIP_2, 1, 2)); + assertEquals(E, subject.findTransfer(STOP_A, STOP_B, TRIP_1, TRIP_2, 1, 2)); // Find the another specific transfer with the stop position changed - assertEquals(E, subject.findTransfer(STOP_A, STOP_B, TRIP_1, TRIP_2, 1, 3)); + assertEquals(F, subject.findTransfer(STOP_A, STOP_B, TRIP_1, TRIP_2, 1, 3)); // Find the specific transfer: TRIP -> STOP when stop position do not match TO point - assertEquals(C, subject.findTransfer(STOP_A, STOP_B, TRIP_1, TRIP_2, 1, 7)); + assertEquals(D, subject.findTransfer(STOP_A, STOP_B, TRIP_1, TRIP_2, 1, 7)); // Find the specific transfer: STOP -> TRIP when stop position do not match FROM point - assertEquals(B, subject.findTransfer(STOP_A, STOP_B, TRIP_1, TRIP_2, 7, 3)); + assertEquals(C, subject.findTransfer(STOP_A, STOP_B, TRIP_1, TRIP_2, 7, 3)); // Stop position fall back to STOP -> STOP when stop position do not match - assertEquals(A, subject.findTransfer(STOP_A, STOP_B, TRIP_1, TRIP_2, 7, 7)); + assertEquals(B, subject.findTransfer(STOP_A, STOP_B, TRIP_1, TRIP_2, 7, 7)); + + // + assertEquals(A, subject.findTransfer(STOP_A, ANY_STOP, TRIP_2, TRIP_1, 7, 1)); } @Test @@ -51,6 +62,19 @@ public void addSameTransferTwiceRetrieveFirstAdded() { assertEquals(A, subject.findTransfer(STOP_A, STOP_B, TRIP_1, TRIP_2, 1, 2)); } + @Test + public void listAll() { + // Given: + var A = transfer(STATION_POINT, ROUTE_POINT_11); + var B = transfer(STOP_POINT_A, STOP_POINT_B); + var C = transfer(STOP_POINT_A, TRIP_POINT_23); + + // When: all transfers is added to service + subject.addAll(List.of(A, B, C)); + + // Then + assertEquals(List.of(A, B, C), subject.listAll()); + } ConstrainedTransfer transfer(TransferPoint from, TransferPoint to) { var c = TransferConstraint.create().build(); diff --git a/src/test/java/org/opentripplanner/model/transfer/TransferTestData.java b/src/test/java/org/opentripplanner/model/transfer/TransferTestData.java index 04f4427e350..32544291978 100644 --- a/src/test/java/org/opentripplanner/model/transfer/TransferTestData.java +++ b/src/test/java/org/opentripplanner/model/transfer/TransferTestData.java @@ -2,12 +2,25 @@ import org.opentripplanner.model.FeedScopedId; import org.opentripplanner.model.Route; +import org.opentripplanner.model.Station; import org.opentripplanner.model.Stop; +import org.opentripplanner.model.StopTransferPriority; import org.opentripplanner.model.Trip; public interface TransferTestData { String FEED_ID = "F"; + Station STATION = new Station( + createId(1), + "Central Station", + null, null, null, null, null, + StopTransferPriority.ALLOWED + ); + + int STOP_POSITION_1 = 1; + int STOP_POSITION_2 = 2; + int STOP_POSITION_3 = 3; + Stop STOP_A = Stop.stopForTest("A", 60.0, 11.0); Stop STOP_B = Stop.stopForTest("B", 60.0, 11.0); @@ -17,15 +30,16 @@ public interface TransferTestData { Trip TRIP_1 = createTrip(1, ROUTE_1); Trip TRIP_2 = createTrip(2, ROUTE_2); + TransferPoint STATION_POINT = new StationTransferPoint(STATION); + TransferPoint STOP_POINT_A = new StopTransferPoint(STOP_A); TransferPoint STOP_POINT_B = new StopTransferPoint(STOP_B); - TransferPoint ROUTE_POINT_11 = new RouteTransferPoint(ROUTE_1, TRIP_1, 1); - TransferPoint ROUTE_POINT_22 = new RouteTransferPoint(ROUTE_2, TRIP_2, 2); - - TransferPoint TRIP_POINT_11 = new TripTransferPoint(TRIP_1, 1); - TransferPoint TRIP_POINT_23 = new TripTransferPoint(TRIP_2, 3); + TransferPoint ROUTE_POINT_11 = new RouteTransferPoint(ROUTE_1, STOP_POSITION_1); + TransferPoint ROUTE_POINT_22 = new RouteTransferPoint(ROUTE_2, STOP_POSITION_2); + TransferPoint TRIP_POINT_11 = new TripTransferPoint(TRIP_1, STOP_POSITION_1); + TransferPoint TRIP_POINT_23 = new TripTransferPoint(TRIP_2, STOP_POSITION_3); private static Trip createTrip(int id, Route route) { Trip t = new Trip(createId(id)); @@ -34,11 +48,10 @@ private static Trip createTrip(int id, Route route) { } private static Route createRoute(int id, String name) { - Route route = new Route(createId(id)); - route.setShortName(name); - return route; + Route r = new Route(createId(id)); + r.setShortName(name); + return r; } - private static FeedScopedId createId(int id) { return new FeedScopedId(FEED_ID, String.valueOf(id)); } diff --git a/src/test/java/org/opentripplanner/netex/mapping/StopTransferPriorityMapperTest.java b/src/test/java/org/opentripplanner/netex/mapping/StopTransferPriorityMapperTest.java index 0f91b91ea9b..d2345598b91 100644 --- a/src/test/java/org/opentripplanner/netex/mapping/StopTransferPriorityMapperTest.java +++ b/src/test/java/org/opentripplanner/netex/mapping/StopTransferPriorityMapperTest.java @@ -13,23 +13,23 @@ public class StopTransferPriorityMapperTest { @Test public void mapToDomain() { - assertNull(TransferPriorityMapper.mapToDomain(null)); + assertNull(StopTransferPriorityMapper.mapToDomain(null)); assertEquals( StopTransferPriority.DISCOURAGED, - TransferPriorityMapper.mapToDomain(InterchangeWeightingEnumeration.NO_INTERCHANGE) + StopTransferPriorityMapper.mapToDomain(InterchangeWeightingEnumeration.NO_INTERCHANGE) ); assertEquals( StopTransferPriority.ALLOWED, - TransferPriorityMapper.mapToDomain(InterchangeWeightingEnumeration.INTERCHANGE_ALLOWED) + StopTransferPriorityMapper.mapToDomain(InterchangeWeightingEnumeration.INTERCHANGE_ALLOWED) ); assertEquals( StopTransferPriority.PREFERRED, - TransferPriorityMapper.mapToDomain(InterchangeWeightingEnumeration.PREFERRED_INTERCHANGE) + StopTransferPriorityMapper.mapToDomain(InterchangeWeightingEnumeration.PREFERRED_INTERCHANGE) ); assertEquals( StopTransferPriority.RECOMMENDED, - TransferPriorityMapper.mapToDomain(InterchangeWeightingEnumeration.RECOMMENDED_INTERCHANGE) + StopTransferPriorityMapper.mapToDomain(InterchangeWeightingEnumeration.RECOMMENDED_INTERCHANGE) ); } } \ No newline at end of file diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptor/transit/request/ConstrainedBoardingSearchTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/ConstrainedBoardingSearchTest.java similarity index 58% rename from src/test/java/org/opentripplanner/routing/algorithm/raptor/transit/request/ConstrainedBoardingSearchTest.java rename to src/test/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/ConstrainedBoardingSearchTest.java index 91654ad57f4..cfa15a13e62 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptor/transit/request/ConstrainedBoardingSearchTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptor/transit/constrainedtransfer/ConstrainedBoardingSearchTest.java @@ -1,4 +1,4 @@ -package org.opentripplanner.routing.algorithm.raptor.transit.request; +package org.opentripplanner.routing.algorithm.raptor.transit.constrainedtransfer; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -6,27 +6,33 @@ import static org.opentripplanner.model.transfer.TransferConstraint.REGULAR_TRANSFER; import gnu.trove.map.hash.TIntObjectHashMap; +import java.util.Collection; import java.util.List; +import java.util.function.Function; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.opentripplanner.model.Stop; import org.opentripplanner.model.TransitMode; import org.opentripplanner.model.transfer.ConstrainedTransfer; import org.opentripplanner.model.transfer.StopTransferPoint; import org.opentripplanner.model.transfer.TransferConstraint; +import org.opentripplanner.model.transfer.TransferPoint; import org.opentripplanner.model.transfer.TripTransferPoint; +import org.opentripplanner.routing.algorithm.raptor.transit.StopIndexForRaptor; +import org.opentripplanner.routing.algorithm.raptor.transit.TransitTuningParameters; +import org.opentripplanner.routing.algorithm.raptor.transit.TripPatternWithRaptorStopIndexes; +import org.opentripplanner.routing.algorithm.raptor.transit.request.TestRouteData; +import org.opentripplanner.routing.algorithm.raptor.transit.request.TestTransitCaseData; public class ConstrainedBoardingSearchTest implements TestTransitCaseData { - - private static final boolean FORWARD = true; - private static final boolean REVERSE = false; private static final int TRIP_1_INDEX = 0; private static final int TRIP_2_INDEX = 1; - private TestRouteData route1; private TestRouteData route2; + private TripPatternWithRaptorStopIndexes pattern1; + private TripPatternWithRaptorStopIndexes pattern2; + private StopIndexForRaptor stopIndex; /** * Create transit data with 2 routes with a trip each. @@ -48,10 +54,10 @@ public class ConstrainedBoardingSearchTest implements TestTransitCaseData { * the next trip 'R2-2'. * *
  • - * The transfer at stop C is allow regular transfers between trip R1-2 and R2-1. + * The transfer at stop C allow regular transfers between trip R1-2 and R2-1. *
  • *
  • - * R1-1 is the fallback int the reverse search in the same way as R2-2 is the fallback + * R1-1 is the fallback in the reverse search in the same way as R2-2 is the fallback * int the forward search. *
  • * @@ -73,21 +79,35 @@ void setup() { "10:15 10:30 10:40", "10:20 10:35 10:45" ); + + this.pattern1 = route1.getRaptorTripPattern(); + this.pattern2 = route2.getRaptorTripPattern(); + this.stopIndex = new StopIndexForRaptor( + List.of(STOP_A, STOP_B, STOP_C, STOP_D), + TransitTuningParameters.FOR_TEST + ); } @Test void transferExist() { - var transfers = new TIntObjectHashMap>(); - int targetStopPos = 3; - transfers.put(targetStopPos, List.of()); + int fromStopPos = route1.stopPosition(STOP_C); + int toStopPos = route2.stopPosition(STOP_C); + + ConstrainedTransfer txAllowed = new ConstrainedTransfer( + id("ALLOWED"), + new StopTransferPoint(STOP_C), + new StopTransferPoint(STOP_C), + TransferConstraint.create().guaranteed().build() + ); + generateTransfersForPatterns(List.of(txAllowed)); // Forward - var subject = new ConstrainedBoardingSearch(true, transfers); - assertTrue(subject.transferExist(targetStopPos)); + var subject = route2.getRaptorTripPattern().constrainedTransferForwardSearch(); + assertTrue(subject.transferExist(toStopPos)); // Reverse - subject = new ConstrainedBoardingSearch(false, transfers); - assertTrue(subject.transferExist(targetStopPos)); + subject = route1.getRaptorTripPattern().constrainedTransferReverseSearch(); + assertTrue(subject.transferExist(fromStopPos)); } @Test @@ -96,8 +116,8 @@ void findGuaranteedTransferWithZeroConnectionTime() { int sourceStopPos = route1.stopPosition(STOP_B); int targetStopPos = route2.stopPosition(STOP_B); - TransferConstraint constraint = TransferConstraint.create().guaranteed().build(); - ConstrainedTransfer txGuaranteed = new ConstrainedTransfer( + var constraint = TransferConstraint.create().guaranteed().build(); + var txGuaranteed = new ConstrainedTransfer( id("Guaranteed"), new TripTransferPoint(route1.lastTrip().trip(), sourceStopPos), new TripTransferPoint(route2.firstTrip().trip(), targetStopPos), @@ -106,75 +126,66 @@ void findGuaranteedTransferWithZeroConnectionTime() { List constrainedTransfers = List.of(txGuaranteed); transfers.put(targetStopPos, constrainedTransfers); - testTransferSearch(STOP_B, constrainedTransfers, TRIP_1_INDEX, constraint); + testTransferSearch(STOP_B, constrainedTransfers, TRIP_1_INDEX, TRIP_2_INDEX, constraint); } @Test - void findNextTransferWhenFirstTripIsNoAllowed() { - var transfers = new TIntObjectHashMap>(); + void findNextTransferWhenFirstTransferIsNotAllowed() { int sourceStopPos = route1.stopPosition(STOP_C); int targetStopPos = route2.stopPosition(STOP_C); - TransferConstraint constraint = TransferConstraint.create().notAllowed().build(); - ConstrainedTransfer txGuaranteed = new ConstrainedTransfer( + var txNotAllowed = new ConstrainedTransfer( id("NOT-ALLOWED"), new TripTransferPoint(route1.lastTrip().trip(), sourceStopPos), new TripTransferPoint(route2.firstTrip().trip(), targetStopPos), - constraint + TransferConstraint.create().notAllowed().build() ); - List constrainedTransfers = List.of(txGuaranteed); - transfers.put(targetStopPos, constrainedTransfers); - testTransferSearch(STOP_C, constrainedTransfers, TRIP_2_INDEX, REGULAR_TRANSFER); + testTransferSearch(STOP_C, List.of(txNotAllowed), TRIP_2_INDEX, TRIP_1_INDEX, REGULAR_TRANSFER); } @Test - @Disabled("This test fail, so we will try to fix the problem in the nex commit") - void blockTransferWhenNoAllowedMatchesAllTripsInRoute() { + void blockTransferWhenNotAllowedApplyToAllTrips() { var transfers = new TIntObjectHashMap>(); int targetStopPos = route2.stopPosition(STOP_C); TransferConstraint constraint = TransferConstraint.create().notAllowed().build(); - ConstrainedTransfer txGuaranteed = new ConstrainedTransfer( + ConstrainedTransfer transfer = new ConstrainedTransfer( id("NOT-ALLOWED"), new StopTransferPoint(STOP_C), new StopTransferPoint(STOP_C), constraint ); - List constrainedTransfers = List.of(txGuaranteed); + List constrainedTransfers = List.of(transfer); transfers.put(targetStopPos, constrainedTransfers); - testTransferSearch(STOP_C, constrainedTransfers, TRIP_1_INDEX, constraint); + testTransferSearch(STOP_C, constrainedTransfers, TRIP_1_INDEX, TRIP_2_INDEX, constraint); } void testTransferSearch( Stop transferStop, List constraints, - int expTripIndex, + int expTripIndexFwdSearch, + int expTripIndexRevSearch, TransferConstraint expConstraint ) { - testTransferSearchForward(transferStop, constraints, expTripIndex, expConstraint); - // Swap expected trip index for reverse search - int revExpTripIndex = expTripIndex == TRIP_1_INDEX ? TRIP_2_INDEX : TRIP_1_INDEX; - testTransferSearchReverse(transferStop, constraints, revExpTripIndex, expConstraint); + //testTransferSearchForward(transferStop, constraints, expTripIndexFwdSearch, expConstraint); + testTransferSearchReverse(transferStop, constraints, expTripIndexRevSearch, expConstraint); } void testTransferSearchForward( Stop transferStop, - List constraints, + List txList, int expectedTripIndex, TransferConstraint expectedConstraint ) { + generateTransfersForPatterns(txList); + var subject = pattern2.constrainedTransferForwardSearch(); + int targetStopPos = route2.stopPosition(transferStop); - var transfers = new TIntObjectHashMap>(); int stopIndex = stopIndex(transferStop); int sourceArrivalTime = route1.lastTrip().getStopTime(transferStop).getArrivalTime(); - transfers.put(targetStopPos, constraints); - - // Forward - var subject = new ConstrainedBoardingSearch(FORWARD, transfers); - // Check that transfer exist assertTrue(subject.transferExist(targetStopPos)); @@ -192,23 +203,19 @@ void testTransferSearchForward( assertEquals(expectedTripIndex, boarding.getTripIndex()); } - void testTransferSearchReverse( Stop transferStop, - List constraints, + List txList, int expectedTripIndex, TransferConstraint expectedConstraint ) { + generateTransfersForPatterns(txList); + var subject = pattern1.constrainedTransferReverseSearch(); int targetStopPos = route1.stopPosition(transferStop); - var transfers = new TIntObjectHashMap>(); + int stopIndex = stopIndex(transferStop); int sourceArrivalTime = route2.firstTrip().getStopTime(transferStop).getDepartureTime(); - transfers.put(targetStopPos, constraints); - - // Forward - var subject = new ConstrainedBoardingSearch(REVERSE, transfers); - // Check that transfer exist assertTrue(subject.transferExist(targetStopPos)); @@ -225,4 +232,36 @@ void testTransferSearchReverse( assertEquals(targetStopPos, boarding.getStopPositionInPattern()); assertEquals(expectedTripIndex, boarding.getTripIndex()); } + + TransferForPatternByStopPos createTxAtStopPos( + Collection transfers, + Function getSource + ) { + var c = new TransferForPatternByStopPos(); + for (ConstrainedTransfer tx : transfers) { + var source = getSource.apply(tx); + var target = source == tx.getFrom() ? tx.getTo() : tx.getFrom(); + var fromTrip = TransferPoint.getTrip(source); + int fromStopPos = TransferPoint.getStopPositionInPattern(source); + int toStopPos = TransferPoint.getStopPositionInPattern(source); + var toTrip = TransferPoint.getTrip(target); + + //var sourcePoint = TransferForPattern. + + /* + c.add(toStopPos, new TransferForPattern( + sourcePoint, + toTrip, + tx.getSpecificityRanking(), + tx.getTransferConstraint()) + ); + */ + } + return c; + } + + private void generateTransfersForPatterns(Collection txList) { + new TransferIndexGenerator(txList, List.of(pattern1, pattern2), stopIndex) + .generateTransfers(); + } } diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptor/transit/mappers/DateMapperTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptor/transit/mappers/DateMapperTest.java index 9e70cad4790..f42dd1e3246 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptor/transit/mappers/DateMapperTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptor/transit/mappers/DateMapperTest.java @@ -1,17 +1,15 @@ package org.opentripplanner.routing.algorithm.raptor.transit.mappers; -import org.junit.Assert; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.opentripplanner.routing.algorithm.raptor.transit.mappers.DateMapper.asStartOfService; import java.time.Instant; import java.time.LocalDate; import java.time.LocalTime; import java.time.ZoneId; import java.time.ZonedDateTime; - -import static org.junit.Assert.assertEquals; -import static org.opentripplanner.routing.algorithm.raptor.transit.mappers.DateMapper.asStartOfService; +import org.junit.jupiter.api.Test; public class DateMapperTest { private static final ZoneId ZONE_ID = ZoneId.of("Europe/Paris"); diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptor/transit/mappers/StopIndexForRaptorTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptor/transit/mappers/StopIndexForRaptorTest.java index c7aa83c41ce..3df449eca0a 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptor/transit/mappers/StopIndexForRaptorTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptor/transit/mappers/StopIndexForRaptorTest.java @@ -1,10 +1,10 @@ package org.opentripplanner.routing.algorithm.raptor.transit.mappers; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Arrays; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.opentripplanner.model.FeedScopedId; import org.opentripplanner.model.Station; import org.opentripplanner.model.Stop; diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptor/transit/request/RaptorRoutingRequestTransitDataCreatorTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptor/transit/request/RaptorRoutingRequestTransitDataCreatorTest.java index 3e7aa1c26ce..ae896989dce 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptor/transit/request/RaptorRoutingRequestTransitDataCreatorTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptor/transit/request/RaptorRoutingRequestTransitDataCreatorTest.java @@ -1,7 +1,15 @@ package org.opentripplanner.routing.algorithm.raptor.transit.request; -import org.junit.Before; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.opentripplanner.model.FeedScopedId; import org.opentripplanner.model.Route; import org.opentripplanner.model.StopPattern; @@ -15,15 +23,6 @@ import org.opentripplanner.routing.trippattern.Deduplicator; import org.opentripplanner.routing.trippattern.TripTimes; -import java.time.LocalDate; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import static org.junit.Assert.assertEquals; - public class RaptorRoutingRequestTransitDataCreatorTest { public static final FeedScopedId TP_ID_1 = new FeedScopedId("F", "1"); @@ -36,7 +35,7 @@ public class RaptorRoutingRequestTransitDataCreatorTest { new StopPattern(List.of()) ); - @Before + @BeforeEach public void setup() { TP.getRoute().setMode(TransitMode.BUS); } diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptor/transit/request/RoutingRequestTransitDataProviderFilterTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptor/transit/request/RoutingRequestTransitDataProviderFilterTest.java index ad7a7f2f383..aac0fa65683 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptor/transit/request/RoutingRequestTransitDataProviderFilterTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptor/transit/request/RoutingRequestTransitDataProviderFilterTest.java @@ -1,21 +1,29 @@ package org.opentripplanner.routing.algorithm.raptor.transit.request; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.LocalDate; +import java.util.List; +import java.util.Set; +import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import org.opentripplanner.model.*; +import org.opentripplanner.model.BikeAccess; +import org.opentripplanner.model.FeedScopedId; +import org.opentripplanner.model.Route; +import org.opentripplanner.model.Stop; +import org.opentripplanner.model.StopPattern; +import org.opentripplanner.model.StopTime; +import org.opentripplanner.model.TransitMode; +import org.opentripplanner.model.Trip; +import org.opentripplanner.model.TripAlteration; +import org.opentripplanner.model.TripPattern; import org.opentripplanner.routing.algorithm.raptor.transit.TripPatternForDate; import org.opentripplanner.routing.algorithm.raptor.transit.TripPatternWithRaptorStopIndexes; import org.opentripplanner.routing.trippattern.Deduplicator; import org.opentripplanner.routing.trippattern.TripTimes; -import java.time.LocalDate; -import java.util.List; -import java.util.Set; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - public class RoutingRequestTransitDataProviderFilterTest { private static final FeedScopedId TEST_ROUTE_ID = new FeedScopedId("TEST", "ROUTE"); @@ -134,7 +142,7 @@ private TripPatternForDate createTestTripPatternForDate() { TripPattern pattern = new TripPattern(null, route, stopPattern); TripPatternWithRaptorStopIndexes tripPattern = new TripPatternWithRaptorStopIndexes( - new int[0], pattern + pattern, new int[0] ); TripTimes tripTimes = Mockito.mock(TripTimes.class); diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptor/transit/request/TestRouteData.java b/src/test/java/org/opentripplanner/routing/algorithm/raptor/transit/request/TestRouteData.java index f14753d6fe0..1a3bd953fe0 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptor/transit/request/TestRouteData.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptor/transit/request/TestRouteData.java @@ -31,6 +31,7 @@ public class TestRouteData implements TestTransitCaseData { private final Map tripTimesByTrip = new HashMap<>(); private final Map tripSchedulesByTrip = new HashMap<>(); private final RaptorTimeTable timetable; + private final TripPatternWithRaptorStopIndexes raptorTripPattern; private Trip currentTrip; public TestRouteData(String route, TransitMode mode, List stops, String ... times) { @@ -47,15 +48,19 @@ public TestRouteData(String route, TransitMode mode, List stops, String .. .map(tripTimesByTrip::get) .collect(Collectors.toList()); - var tripPattern = new TripPatternWithRaptorStopIndexes( - stopIndexes(stopTimesFistTrip), - new TripPattern(id("TP1"), this.route, new StopPattern(stopTimesFistTrip)) + raptorTripPattern = new TripPatternWithRaptorStopIndexes( + new TripPattern(id("TP:"+route), this.route, new StopPattern(stopTimesFistTrip)), + stopIndexes(stopTimesFistTrip) ); + tripTimes.forEach(t -> raptorTripPattern.getPattern().add(t)); + var listOfTripPatternForDates = List.of( - new TripPatternForDate(tripPattern, tripTimes, DATE) + new TripPatternForDate(raptorTripPattern, tripTimes, DATE) ); - var patternForDates = new TripPatternForDates(tripPattern, listOfTripPatternForDates, List.of(OFFSET)); + var patternForDates = new TripPatternForDates( + raptorTripPattern, listOfTripPatternForDates, List.of(OFFSET) + ); for (Trip trip : trips) { var tripSchedule = new TripScheduleWithOffset( patternForDates, DATE, tripTimesByTrip.get(trip), OFFSET @@ -75,32 +80,35 @@ private Trip parseTripInfo(String route, String tripTimes, List stops, Ded return trip; } - Trip trip() { + public Trip trip() { return currentTrip; } - TestRouteData firstTrip() { + public TestRouteData firstTrip() { this.currentTrip = trips.get(0); return this; } - TestRouteData lastTrip() { + public TestRouteData lastTrip() { this.currentTrip = trips.get(trips.size()-1); return this; } - StopTime getStopTime(Stop stop) { + public StopTime getStopTime(Stop stop) { return stopTimesByTrip.get(currentTrip).get(stopPosition(stop)); } - RaptorTimeTable getTimetable() { + public RaptorTimeTable getTimetable() { return timetable; } - TripSchedule getTripSchedule() { + public TripSchedule getTripSchedule() { return tripSchedulesByTrip.get(currentTrip); } + public TripPatternWithRaptorStopIndexes getRaptorTripPattern() { + return raptorTripPattern; + } private List getStopTimes() { return stopTimesByTrip.get(currentTrip); @@ -126,7 +134,7 @@ int[] stopIndexes(Collection times) { return times.stream().mapToInt(it -> stopIndex(it.getStop())).toArray(); } - int stopPosition(StopLocation stop) { + public int stopPosition(StopLocation stop) { List times = firstTrip().getStopTimes(); for (int i=0; i