Skip to content

Commit

Permalink
feat: Support for GTFS Trip, Route, Stop & Station Transfers
Browse files Browse the repository at this point in the history
- The Transfer report is improved
- OTP serialization version id incremented
  • Loading branch information
t2gran committed Jan 11, 2022
1 parent 594f8f4 commit 1390922
Show file tree
Hide file tree
Showing 30 changed files with 1,271 additions and 385 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
</scm>

<properties>
<otp.serialization.version.id>15</otp.serialization.version.id>
<otp.serialization.version.id>16</otp.serialization.version.id>
<!-- Lib versions - keep list sorted on property name -->
<geotools.version>25.2</geotools.version>
<jackson.version>2.12.5</jackson.version>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;

import java.net.URISyntaxException;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
import static org.opentripplanner.util.time.DurationUtils.durationToStr;

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.StopLocation;
import org.opentripplanner.model.Trip;
import org.opentripplanner.model.TripPattern;
import org.opentripplanner.model.WgsCoordinate;
import org.opentripplanner.model.transfer.ConstrainedTransfer;
import org.opentripplanner.model.transfer.RouteTransferPoint;
import org.opentripplanner.model.transfer.RouteStationTransferPoint;
import org.opentripplanner.model.transfer.RouteStopTransferPoint;
import org.opentripplanner.model.transfer.StationTransferPoint;
import org.opentripplanner.model.transfer.StopTransferPoint;
import org.opentripplanner.model.transfer.TransferPoint;
Expand All @@ -26,6 +27,8 @@
* to read and find special test-cases when needed.
*/
public class TransfersReport {
private static final boolean BOARD = true;
private static final boolean ALIGHT = false;
private static final int NOT_SET = -1;


Expand Down Expand Up @@ -54,13 +57,16 @@ String export() {
);

transfers.forEach(t -> {
var from = pointInfo(t.getFrom(), true);
var to = pointInfo(t.getTo(), false);
var from = pointInfo(t.getFrom(), ALIGHT);
var to = pointInfo(t.getTo(), BOARD);
var dist = (from.coordinate == null || to.coordinate == null)
? ""
: String.format(
"%.0fm",
SphericalDistanceLibrary.fastDistance(from.coordinate, to.coordinate)
SphericalDistanceLibrary.fastDistance(
from.coordinate.asJtsCoordinate(),
to.coordinate.asJtsCoordinate()
)
);
var duration = (from.time == NOT_SET || to.time == NOT_SET)
? "" : durationToStr(to.time - from.time);
Expand All @@ -72,13 +78,13 @@ String export() {
buf.addText(from.entityId);
buf.addText(from.route);
buf.addText(from.trip);
buf.addText(from.loc);
buf.addText(from.location());
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.addText(to.location());
buf.addNumber(to.specificity);
buf.addTime(from.time, NOT_SET);
buf.addTime(to.time, NOT_SET);
Expand All @@ -93,10 +99,7 @@ String export() {
return buf.toString();
}

private TxPoint pointInfo(
TransferPoint p,
boolean arrival
) {
private TxPoint pointInfo(TransferPoint p, boolean boarding) {
var r = new TxPoint();

if(p instanceof TripTransferPoint) {
Expand All @@ -109,33 +112,44 @@ private TxPoint pointInfo(
r.entityId = trip.getId().getId();
r.route = route.getName() + " " + route.getMode() + " " + route.getLongName();
r.trip = trip.getTripHeadsign();
addLocation(r, ptn, tp.getStopPositionInPattern(), trip, arrival);
var stop = ptn.getStop(tp.getStopPositionInPattern());
addLocation(r, ptn, stop, trip, boarding);
}
else if(p instanceof RouteTransferPoint) {
var rp = (RouteTransferPoint)p;
else if(p instanceof RouteStopTransferPoint) {
var rp = (RouteStopTransferPoint)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);
addLocation(r, ptn, rp.getStop(), null, boarding);
}
else if(p instanceof RouteStationTransferPoint) {
var rp = (RouteStationTransferPoint)p;
var route = rp.getRoute();
r.operator = route.getOperator().getId().getId();
r.type = "Route";
r.entityId = route.getId().getId();
r.route = route.getName() + " " + route.getMode() + " " + route.getLongName();
r.loc += rp.getStation().getName();
r.coordinate = rp.getStation().getCoordinate();
}
else if(p instanceof StopTransferPoint) {
var sp = (StopTransferPoint)p;
StopLocation stop = sp.getStop();
r.type = "Stop";
r.entityId = stop.getId().getId();
r.loc = stop.getName();
r.coordinate = stop.getCoordinate().asJtsCoordinate();
r.coordinate = stop.getCoordinate();
}
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.coordinate = station.getCoordinate();
}

r.specificity = p.getSpecificityRanking();
Expand All @@ -146,23 +160,29 @@ else if(p instanceof StationTransferPoint) {
private static void addLocation(
TxPoint r,
TripPattern pattern,
int stopPosition,
StopLocation stop,
Trip trip,
boolean arrival
boolean boarding
) {
if(pattern == null || stopPosition >= pattern.numberOfStops()) {
r.loc += "[Stop position not found: " + stopPosition + "]";
if(pattern == null) {
r.loc += stop.getName() + " [Pattern no found]";
return;
}
int stopPosition = pattern.findStopPosition(stop);
r.coordinate = stop.getCoordinate();

if(stopPosition<0) {
r.loc += "[Stop not found in pattern: " + stop.getName() + "]";
return;
}
var stop = pattern.getStops().get(stopPosition);
r.loc += stop.getName() + " [" + stopPosition + "]" + " " + stop.getCoordinate();
r.coordinate = stop.getCoordinate().asJtsCoordinate();
r.loc += stop.getName() + " [" + stopPosition + "]";

if(trip != null) {
var tt = pattern.getScheduledTimetable().getTripTimes(trip);
r.time = arrival
? tt.getScheduledArrivalTime(stopPosition)
: tt.getScheduledDepartureTime(stopPosition);
r.time = boarding
? tt.getScheduledDepartureTime(stopPosition)
: tt.getScheduledArrivalTime(stopPosition)
;
}
}

Expand All @@ -174,7 +194,11 @@ static class TxPoint {
private String trip = "";
private String route = "";
private Integer specificity = null;
private Coordinate coordinate = null;
private WgsCoordinate coordinate = null;
private int time = NOT_SET;

String location() {
return coordinate == null ? loc : loc + " " + coordinate;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.val;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.opentripplanner.common.model.T2;
Expand Down Expand Up @@ -42,7 +41,7 @@ public Collection<T2<String, Object>> map(TransitStopVertex input) {

String patterns = JSONArray.toJSONString(patternsForStop.stream().map(tripPattern -> {
int stopPos = tripPattern.findStopPosition(stop);
val headsign = stopPos < 0 ? "Not Available" :
var headsign = stopPos < 0 ? "Not Available" :
tripPattern.getScheduledTimetable().getTripTimes().get(0).getHeadsign(stopPos);
return new JSONObject(Map.of(
"headsign", headsign,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
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.RouteStationTransferPoint;
import org.opentripplanner.model.transfer.RouteStopTransferPoint;
import org.opentripplanner.model.transfer.StationTransferPoint;
import org.opentripplanner.model.transfer.StopTransferPoint;
import org.opentripplanner.model.transfer.TransferConstraint;
Expand Down Expand Up @@ -184,11 +185,8 @@ private TransferPoint mapTransferPoint(
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 for route: " + route); }

int stopPositionInPattern = stopPosition(route, stop, station, boardTrip);
return new RouteTransferPoint(route, stopPositionInPattern);
if(stop != null) { return new RouteStopTransferPoint(route, stop); }
else if(station != null) { return new RouteStationTransferPoint(route, station); }
}
else if(stop != null) {
return new StopTransferPoint(stop);
Expand Down
29 changes: 29 additions & 0 deletions src/main/java/org/opentripplanner/model/StopPattern.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.function.Predicate;

/**
* This class represents what is called a JourneyPattern in Transmodel: the sequence of stops at
Expand Down Expand Up @@ -77,6 +78,22 @@ int findStopPosition(StopLocation stop) {
return -1;
}

int findBoardingPosition(StopLocation stop) {
return findStopPosition(0, stops.length-1, (s) -> s == stop, stop);
}

int findAlightPosition(StopLocation stop) {
return findStopPosition(1, stops.length, (s) -> s == stop, stop);
}

int findBoardingPosition(Station station) {
return findStopPosition(0, stops.length-1, station::includes, station);
}

int findAlightPosition(Station station) {
return findStopPosition(1, stops.length, station::includes, station);
}

public boolean equals(Object other) {
if (other instanceof StopPattern) {
StopPattern that = (StopPattern) other;
Expand Down Expand Up @@ -180,4 +197,16 @@ private static PickDrop computePickDrop(StopLocation stop, PickDrop pickDrop) {
if(stop instanceof FlexStopLocation) { return PickDrop.NONE; }
else { return pickDrop; }
}

private int findStopPosition(
final int start,
final int end,
final Predicate<StopLocation> match,
final Object entity
) {
for (int i=start; i<end; ++i) {
if(match.test(stops[i])) { return i; }
}
throw new IllegalArgumentException("Stop/Station not found: " + entity);
}
}
16 changes: 16 additions & 0 deletions src/main/java/org/opentripplanner/model/TripPattern.java
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,22 @@ public int findStopPosition(StopLocation stop) {
return stopPattern.findStopPosition(stop);
}

public int findBoardingStopPositionInPattern(Station station) {
return stopPattern.findBoardingPosition(station);
}

public int findAlightStopPositionInPattern(Station station) {
return stopPattern.findAlightPosition(station);
}

public int findBoardingStopPositionInPattern(StopLocation stop) {
return stopPattern.findBoardingPosition(stop);
}

public int findAlightStopPositionInPattern(StopLocation stop) {
return stopPattern.findAlightPosition(stop);
}

/** Returns whether passengers can alight at a given stop */
public boolean canAlight(int stopIndex) {
return stopPattern.canAlight(stopIndex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,27 @@

import java.io.Serializable;
import org.opentripplanner.model.Route;
import org.opentripplanner.model.Station;
import org.opentripplanner.model.base.ValueObjectToStringBuilder;

public final class RouteTransferPoint implements TransferPoint, Serializable {
public final class RouteStationTransferPoint implements TransferPoint, Serializable {

private static final long serialVersionUID = 1L;

private final Route route;
private final int stopPositionInPattern;
private final Station station;

public RouteTransferPoint(Route route, int stopPositionInPattern) {
public RouteStationTransferPoint(Route route, Station station) {
this.route = route;
this.stopPositionInPattern = stopPositionInPattern;
this.station = station;
}

public Route getRoute() {
return route;
}

public int getStopPositionInPattern() {
return stopPositionInPattern;
public Station getStation() {
return station;
}

@Override
Expand All @@ -33,15 +34,15 @@ public boolean applyToAllTrips() {
public int getSpecificityRanking() { return 2; }

@Override
public boolean isRouteTransferPoint() { return true; }
public boolean isRouteStationTransferPoint() { return true; }

@Override
public String toString() {
return ValueObjectToStringBuilder.of()
.addText("<Route ")
.addObj(route.getId())
.addText(" @stopPos:")
.addNum(stopPositionInPattern)
.addText(", station ")
.addObj(station.getId())
.addText(">")
.toString();
}
Expand Down
Loading

0 comments on commit 1390922

Please sign in to comment.