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
  • Loading branch information
t2gran committed Dec 18, 2021
1 parent d6e3ece commit ca028ac
Show file tree
Hide file tree
Showing 54 changed files with 1,802 additions and 1,167 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
/**
* A very simple CSV builder to create CSV reports.
* <p>
* 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;
Expand Down Expand Up @@ -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();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -41,32 +47,39 @@ public static String export(List<ConstrainedTransfer> 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);
Expand All @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -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();
}
Expand All @@ -90,6 +94,27 @@ private static ConstrainedTransfer transfer(DataFetchingEnvironment environment)
return environment.getSource();
}

private static TransferPoint transferPoint(
DataFetchingEnvironment environment,
Function<ConstrainedTransfer, TransferPoint> fromTo
) {
return fromTo.apply(transfer(environment));
}

private static Trip transferTrip(
DataFetchingEnvironment environment,
Function<ConstrainedTransfer, TransferPoint> fromTo
) {
return TransferPoint.getTrip(transferPoint(environment, fromTo));
}

private static Route transferRoute(
DataFetchingEnvironment environment,
Function<ConstrainedTransfer, TransferPoint> fromTo
) {
return TransferPoint.getRoute(transferPoint(environment, fromTo));
}

private static TransferConstraint constraint(DataFetchingEnvironment environment) {
return transfer(environment).getTransferConstraint();
}
Expand Down
Loading

0 comments on commit ca028ac

Please sign in to comment.