com.conveyal.gtfs.stats.StopStats Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gtfs-lib Show documentation
Show all versions of gtfs-lib Show documentation
A library to load and index GTFS feeds of arbitrary size using disk-backed storage
package com.conveyal.gtfs.stats;
import com.conveyal.gtfs.GTFSFeed;
import com.conveyal.gtfs.model.Route;
import com.conveyal.gtfs.model.Service;
import com.conveyal.gtfs.model.StopTime;
import com.conveyal.gtfs.model.Trip;
import com.conveyal.gtfs.stats.model.TransferPerformanceSummary;
import gnu.trove.list.TIntList;
import gnu.trove.list.array.TIntArrayList;
import org.mapdb.Fun;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
/**
* Created by landon on 9/2/16.
*/
public class StopStats {
private GTFSFeed feed = null;
private FeedStats stats = null;
private RouteStats routeStats = null;
public StopStats (GTFSFeed f, FeedStats fs) {
feed = f;
stats = fs;
routeStats = stats.route;
}
public List getRoutes (String stop_id) {
return feed.patterns.values().stream()
.filter(p -> p.orderedStops.contains(stop_id))
.distinct()
.map(p -> feed.routes.get(p.route_id))
.collect(Collectors.toList());
}
/**
* Gets count of routes that visit a stop.
* @param stop_id
* @return count of routes for stop
*/
public int getRouteCount (String stop_id) {
return getRoutes(stop_id).size();
}
/**
* Gets count of trips that visit a stop for a specified date of service.
* @param stop_id
* @param date
* @return count of trips for date
*/
public int getTripCountForDate (String stop_id, LocalDate date) {
return (int) getTripsForDate(stop_id, date).size();
}
/**
* Get list of trips that visit a stop for a specified date of service.
* @param stop_id
* @param date
* @return list of trips for date
*/
public List getTripsForDate (String stop_id, LocalDate date) {
List tripIds = stats.getTripsForDate(date).stream()
.map(trip -> trip.trip_id)
.collect(Collectors.toList());
return feed.getDistinctTripsForStop(stop_id).stream()
.filter(t -> tripIds.contains(t.trip_id)) // filter by trip_id list for date
.collect(Collectors.toList());
}
/**
* Get the average headway, in seconds, for all trips at a stop over a time window.
* @param stop_id
* @param date
* @param from
* @param to
* @return avg. headway (in seconds)
*/
public int getAverageHeadwayForStop (String stop_id, LocalDate date, LocalTime from, LocalTime to) {
List tripsForStop = getTripsForDate(stop_id, date);
return getStopHeadwayForTrips(stop_id, tripsForStop, from, to);
}
/** Get the route headway for a given service date at a stop over a time window, in seconds */
public Map getRouteHeadwaysForStop (String stop_id, LocalDate date, LocalTime from, LocalTime to) {
Map routeHeadwayMap = new HashMap<>();
List routes = feed.patterns.values().stream()
.filter(p -> p.orderedStops.contains(stop_id))
.map(p -> feed.routes.get(p.route_id))
.collect(Collectors.toList());
for (Route route : routes) {
routeHeadwayMap.put(route.route_id, getHeadwayForStopByRoute(stop_id, route.route_id, date, from, to));
}
return routeHeadwayMap;
}
/**
* Get the average headway, in seconds, for a set of trips at a stop over a time window.
* @param stop_id
* @param trips
* @param from
* @param to
* @return avg. headway (in seconds)
*/
//TODO: add direction_id?
//TODO: specified stop vs. first stop
public int getStopHeadwayForTrips (String stop_id, List trips, LocalTime from, LocalTime to) {
TIntList timesAtStop = new TIntArrayList();
Set tripIds = trips.stream()
.map(t -> t.trip_id)
.collect(Collectors.toSet());
List stopTimes = feed.getStopTimesForStop(stop_id).stream()
.filter(st -> tripIds.contains(st.trip_id))
.collect(Collectors.toList());
for (StopTime st : stopTimes) {
// these trips are actually running on the next day, skip them
if (st.departure_time > 86399 || st.departure_time < 0) continue;
LocalTime timeAtStop = LocalTime.ofSecondOfDay(st.departure_time);
if (timeAtStop.isAfter(to) || timeAtStop.isBefore(from)) {
continue;
}
timesAtStop.add(st.departure_time);
}
timesAtStop.sort();
// convert to deltas
TIntList deltas = new TIntArrayList();
for (int i = 0; i < timesAtStop.size() - 1; i++) {
int delta = timesAtStop.get(i + 1) - timesAtStop.get(i);
if (delta > 60) deltas.add(delta);
}
if (deltas.isEmpty()) return -1;
return deltas.sum() / deltas.size();
}
/**
* Get the average headway, in seconds, for a route at a stop over a time window.
* @param stop_id
* @param route_id
* @param date
* @param from
* @param to
* @return avg. headway (in seconds)
*/
public int getHeadwayForStopByRoute (String stop_id, String route_id, LocalDate date, LocalTime from, LocalTime to) {
List tripsForStop = feed.getDistinctTripsForStop(stop_id).stream()
.filter(trip -> feed.trips.get(trip.trip_id).route_id.equals(route_id))
.filter(trip -> feed.services.get(trip.service_id).activeOn(date))
.collect(Collectors.toList());
return getStopHeadwayForTrips(stop_id, tripsForStop, from, to);
}
/**
* Returns a list of transfer performance summaries (max, min, and median wait times between routes)
* for each route pair at a stop for the specified date of service.
* @param stop_id
* @param date
* @return
*/
public List getTransferPerformance (String stop_id, LocalDate date) {
List stopTimes = feed.getStopTimesForStop(stop_id);
Map> routeStopTimeMap = new HashMap<>();
List transferPerformanceMap = new ArrayList<>();
// TODO: do we need to handle interpolated stop times???
// first stream stopTimes for stop into route -> list of stopTimes map
stopTimes.stream()
.forEach(st -> {
Trip trip = feed.trips.get(st.trip_id);
Service service = feed.services.get(trip.service_id);
// only add to map if trip is active on given date
if (service != null && service.activeOn(date)) {
Route route = feed.routes.get(trip.route_id);
List times = new ArrayList<>();
if (routeStopTimeMap.containsKey(route.route_id)) {
times.addAll(routeStopTimeMap.get(route.route_id));
}
times.add(st);
routeStopTimeMap.put(route.route_id, times);
}
});
Map, TIntList> waitTimesByRoute = new HashMap<>();
Map, Set>> missedTransfers = new HashMap<>();
// iterate over every entry in route -> list of stopTimes map
for (Map.Entry> entry : routeStopTimeMap.entrySet()) {
final int MISSED_TRANSFER_THRESHOLD = 60 * 10;
List currentTimes = entry.getValue();
String currentRoute = entry.getKey();
for (StopTime currentTime : currentTimes) {
if (currentTime.arrival_time > 0) {
// cycle through all other routes that stop here.
for (Map.Entry> entry2 : routeStopTimeMap.entrySet()) {
List compareTimes = entry2.getValue();
String compareRoute = entry2.getKey();
Fun.Tuple2 routeKey = new Fun.Tuple2(currentRoute, compareRoute);
if (compareRoute.equals(currentRoute)) {
continue;
}
if (!waitTimesByRoute.containsKey(routeKey)) {
waitTimesByRoute.put(routeKey, new TIntArrayList());
}
int shortestWait = Integer.MAX_VALUE;
// compare current time against departure times for route
for (StopTime compareTime : compareTimes) {
if (compareTime.departure_time > 0) {
int waitTime = compareTime.departure_time - currentTime.arrival_time;
// if non-negative and shortest, save for later
if (waitTime >= 0 && waitTime < shortestWait){
shortestWait = waitTime;
}
// otherwise, check for missed near-transfer opportunities
else {
if (waitTime < 0 && waitTime * -1 <= MISSED_TRANSFER_THRESHOLD) {
Fun.Tuple2 missedTransfer = new Fun.Tuple2(compareTime, currentTime);
// missedTransfer.add(currentTime);
// missedTransfer.add(compareTime);
if (!missedTransfers.containsKey(routeKey)) {
missedTransfers.put(routeKey, new HashSet<>());
}
missedTransfers.get(routeKey).add(missedTransfer);
}
}
}
}
// add shortestWait for currentTime to map
if (shortestWait < Integer.MAX_VALUE)
waitTimesByRoute.get(routeKey).add(shortestWait);
}
}
}
}
for (Map.Entry, TIntList> entry : waitTimesByRoute.entrySet()) {
Fun.Tuple2 routeKey = entry.getKey();
TIntList waitTimes = entry.getValue();
if (waitTimes.isEmpty()) {
continue;
}
int min = waitTimes.min();
int max = waitTimes.max();
waitTimes.sort();
int median = waitTimes.get(waitTimes.size() / 2);
TransferPerformanceSummary routeTransferPerformance = new TransferPerformanceSummary(routeKey.a, routeKey.b, min, max, median, missedTransfers.get(routeKey));
transferPerformanceMap.add(routeTransferPerformance);
}
return transferPerformanceMap;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy