use of org.opentripplanner.graph_builder.annotation.RepeatedStops in project OpenTripPlanner by opentripplanner.
the class GTFSPatternHopFactory method run.
/**
* Generate the edges. Assumes that there are already vertices in the graph for the stops.
*/
public void run(Graph graph) {
if (fareServiceFactory == null) {
fareServiceFactory = new DefaultFareServiceFactory();
}
fareServiceFactory.processGtfs(_dao);
// TODO: Why are we loading stops? The Javadoc above says this method assumes stops are aleady loaded.
loadStops(graph);
loadPathways(graph);
loadFeedInfo(graph);
loadAgencies(graph);
// TODO: Why is there cached "data", and why are we clearing it? Due to a general lack of comments, I have no idea.
// Perhaps it is to allow name collisions with previously loaded feeds.
clearCachedData();
/* Assign 0-based numeric codes to all GTFS service IDs. */
for (AgencyAndId serviceId : _dao.getAllServiceIds()) {
// TODO: FIX Service code collision for multiple feeds.
graph.serviceCodes.put(serviceId, graph.serviceCodes.size());
}
LOG.debug("building hops from trips");
Collection<Trip> trips = _dao.getAllTrips();
int tripCount = 0;
/* First, record which trips are used by one or more frequency entries.
* These trips will be ignored for the purposes of non-frequency routing, and
* all the frequency entries referencing the same trip can be added at once to the same
* Timetable/TripPattern.
*/
ListMultimap<Trip, Frequency> frequenciesForTrip = ArrayListMultimap.create();
for (Frequency freq : _dao.getAllFrequencies()) {
frequenciesForTrip.put(freq.getTrip(), freq);
}
/* Then loop over all trips, handling each one as a frequency-based or scheduled trip. */
int freqCount = 0;
int nonFreqCount = 0;
/* The hops don't actually exist when we build their geometries, but we have to build their geometries
* below, before we throw away the modified stopTimes, saving only the tripTimes (which don't have enough
* information to build a geometry). So we keep them here.
*
* A trip pattern actually does not have a single geometry, but one per hop, so we store an array.
* FIXME _why_ doesn't it have a single geometry?
*/
Map<TripPattern, LineString[]> geometriesByTripPattern = Maps.newHashMap();
TRIP: for (Trip trip : trips) {
if (++tripCount % 100000 == 0) {
LOG.debug("loading trips {}/{}", tripCount, trips.size());
}
// TODO: move to a validator module
if (!_calendarService.getServiceIds().contains(trip.getServiceId())) {
LOG.warn(graph.addBuilderAnnotation(new TripUndefinedService(trip)));
// Invalid trip, skip it, it will break later
continue TRIP;
}
/* Fetch the stop times for this trip. Copy the list since it's immutable. */
List<StopTime> stopTimes = new ArrayList<StopTime>(_dao.getStopTimesForTrip(trip));
/* GTFS stop times frequently contain duplicate, missing, or incorrect entries. Repair them. */
TIntList removedStopSequences = removeRepeatedStops(stopTimes);
if (!removedStopSequences.isEmpty()) {
LOG.warn(graph.addBuilderAnnotation(new RepeatedStops(trip, removedStopSequences)));
}
filterStopTimes(stopTimes, graph);
interpolateStopTimes(stopTimes);
/* If after filtering this trip does not contain at least 2 stoptimes, it does not serve any purpose. */
if (stopTimes.size() < 2) {
LOG.warn(graph.addBuilderAnnotation(new TripDegenerate(trip)));
continue TRIP;
}
/* Try to get the direction id for the trip, set to -1 if not found */
int directionId;
try {
directionId = Integer.parseInt(trip.getDirectionId());
} catch (NumberFormatException e) {
LOG.debug("Trip {} does not have direction id, defaults to -1");
directionId = -1;
}
/* Get the existing TripPattern for this filtered StopPattern, or create one. */
StopPattern stopPattern = new StopPattern(stopTimes);
TripPattern tripPattern = findOrCreateTripPattern(stopPattern, trip.getRoute(), directionId);
/* Create a TripTimes object for this list of stoptimes, which form one trip. */
TripTimes tripTimes = new TripTimes(trip, stopTimes, graph.deduplicator);
/* If this trip is referenced by one or more lines in frequencies.txt, wrap it in a FrequencyEntry. */
List<Frequency> frequencies = frequenciesForTrip.get(trip);
if (frequencies != null && !(frequencies.isEmpty())) {
for (Frequency freq : frequencies) {
tripPattern.add(new FrequencyEntry(freq, tripTimes));
freqCount++;
}
// TODO replace: createGeometry(graph, trip, stopTimes, hops);
} else /* This trip was not frequency-based. Add the TripTimes directly to the TripPattern's scheduled timetable. */
{
tripPattern.add(tripTimes);
nonFreqCount++;
}
// there would be a trip pattern with no geometry yet because it failed some of these tests
if (!geometriesByTripPattern.containsKey(tripPattern) && trip.getShapeId() != null && trip.getShapeId().getId() != null && !trip.getShapeId().getId().equals("")) {
// save the geometry to later be applied to the hops
geometriesByTripPattern.put(tripPattern, createGeometry(graph, trip, stopTimes));
}
}
// end foreach TRIP
LOG.info("Added {} frequency-based and {} single-trip timetable entries.", freqCount, nonFreqCount);
graph.hasFrequencyService = graph.hasFrequencyService || freqCount > 0;
graph.hasScheduledService = graph.hasScheduledService || nonFreqCount > 0;
/* Generate unique human-readable names for all the TableTripPatterns. */
TripPattern.generateUniqueNames(tripPatterns.values());
/* Generate unique short IDs for all the TableTripPatterns. */
TripPattern.generateUniqueIds(tripPatterns.values());
/* Loop over all new TripPatterns, creating edges, setting the service codes and geometries, etc. */
for (TripPattern tripPattern : tripPatterns.values()) {
tripPattern.makePatternVerticesAndEdges(graph, context.stationStopNodes);
// Add the geometries to the hop edges.
LineString[] geom = geometriesByTripPattern.get(tripPattern);
if (geom != null) {
for (int i = 0; i < tripPattern.hopEdges.length; i++) {
tripPattern.hopEdges[i].setGeometry(geom[i]);
}
// Make a geometry for the whole TripPattern from all its constituent hops.
// This happens only if geometry is found in geometriesByTripPattern,
// because that means that geometry was created from shapes instead "as crow flies"
tripPattern.makeGeometry();
}
// TODO this could be more elegant
tripPattern.setServiceCodes(graph.serviceCodes);
/* Iterate over all stops in this pattern recording mode information. */
TraverseMode mode = GtfsLibrary.getTraverseMode(tripPattern.route);
for (TransitStop tstop : tripPattern.stopVertices) {
tstop.addMode(mode);
if (mode == TraverseMode.SUBWAY) {
tstop.setStreetToStopTime(subwayAccessTime);
}
graph.addTransitMode(mode);
}
}
/* Identify interlined trips and create the necessary edges. */
interline(tripPatterns.values(), graph);
/* Interpret the transfers explicitly defined in transfers.txt. */
loadTransfers(graph);
/* Store parent stops in graph, even if not linked. These are needed for clustering*/
for (TransitStationStop stop : context.stationStopNodes.values()) {
if (stop instanceof TransitStation) {
TransitStation parentStopVertex = (TransitStation) stop;
graph.parentStopById.put(parentStopVertex.getStopId(), parentStopVertex.getStop());
}
}
// it is already done at deserialization, but standalone mode allows using graphs without serializing them.
for (TripPattern tableTripPattern : tripPatterns.values()) {
tableTripPattern.scheduledTimetable.finish();
}
// eh?
clearCachedData();
graph.putService(FareService.class, fareServiceFactory.makeFareService());
graph.putService(OnBoardDepartService.class, new OnBoardDepartServiceImpl());
}
Aggregations