/**
* Copyright (C) 2011 Brian Ferris <bdferris@onebusaway.org>
* Copyright (C) 2011 Google, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onebusaway.transit_data_federation.bundle.tasks.transit_graph;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.TimeZone;
import org.onebusaway.container.ConfigurationParameter;
import org.onebusaway.gtfs.model.Agency;
import org.onebusaway.gtfs.model.Route;
import org.onebusaway.gtfs.model.StopTime;
import org.onebusaway.gtfs.model.Trip;
import org.onebusaway.gtfs.model.calendar.LocalizedServiceId;
import org.onebusaway.gtfs.services.GtfsRelationalDao;
import org.onebusaway.transit_data_federation.bundle.services.UniqueService;
import org.onebusaway.transit_data_federation.bundle.tasks.ShapePointHelper;
import org.onebusaway.transit_data_federation.impl.transit_graph.RouteEntryImpl;
import org.onebusaway.transit_data_federation.impl.transit_graph.StopTimeEntryImpl;
import org.onebusaway.transit_data_federation.impl.transit_graph.TransitGraphImpl;
import org.onebusaway.transit_data_federation.impl.transit_graph.TripEntryImpl;
import org.onebusaway.transit_data_federation.model.ShapePoints;
import org.onebusaway.transit_data_federation.services.transit_graph.StopTimeEntry;
import org.onebusaway.transit_data_federation.services.transit_graph.TripEntry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class TripEntriesFactory {
private Logger _log = LoggerFactory.getLogger(TripEntriesFactory.class);
private UniqueService _uniqueService;
private GtfsRelationalDao _gtfsDao;
private StopTimeEntriesFactory _stopTimeEntriesFactory;
private ShapePointHelper _shapePointsHelper;
private boolean _throwExceptionOnInvalidStopToShapeMappingException = true;
@Autowired
public void setUniqueService(UniqueService uniqueService) {
_uniqueService = uniqueService;
}
@Autowired
public void setShapePointHelper(ShapePointHelper shapePointsHelper) {
_shapePointsHelper = shapePointsHelper;
}
@Autowired
public void setGtfsDao(GtfsRelationalDao gtfsDao) {
_gtfsDao = gtfsDao;
}
@Autowired
public void setStopTimeEntriesFactory(
StopTimeEntriesFactory stopTimeEntriesFactory) {
_stopTimeEntriesFactory = stopTimeEntriesFactory;
}
/**
* By default, we throw an exception when an invalid stop-to-shape mapping is
* found for a GTFS feed. Override that behavior by setting this parameter to
* false.
*
* @param throwExceptionOnInvalidStopToShapeMappingException when true, an
* exception is thrown on invalid stop-to-shape mappings
*/
@ConfigurationParameter
public void setThrowExceptionOnInvalidStopToShapeMappingException(
boolean throwExceptionOnInvalidStopToShapeMappingException) {
_throwExceptionOnInvalidStopToShapeMappingException = throwExceptionOnInvalidStopToShapeMappingException;
}
public void processTrips(TransitGraphImpl graph) {
Collection<Route> routes = _gtfsDao.getAllRoutes();
int routeIndex = 0;
for (Route route : routes) {
_log.info("route processed: " + routeIndex + "/" + routes.size());
routeIndex++;
List<Trip> tripsForRoute = _gtfsDao.getTripsForRoute(route);
_log.info("trips to process: " + tripsForRoute.size());
int tripIndex = 0;
RouteEntryImpl routeEntry = graph.getRouteForId(route.getId());
ArrayList<TripEntry> tripEntries = new ArrayList<TripEntry>();
for (Trip trip : tripsForRoute) {
tripIndex++;
if (tripIndex % 500 == 0)
_log.info("trips processed: " + tripIndex + "/"
+ tripsForRoute.size());
TripEntryImpl tripEntry = processTrip(graph, trip);
if (tripEntry != null) {
tripEntry.setRoute(routeEntry);
tripEntries.add(tripEntry);
}
}
tripEntries.trimToSize();
routeEntry.setTrips(tripEntries);
}
if (_stopTimeEntriesFactory.getInvalidStopToShapeMappingExceptionCount() > 0
&& _throwExceptionOnInvalidStopToShapeMappingException) {
throw new IllegalStateException(
"Multiple instances of InvalidStopToShapeMappingException thrown: count="
+ _stopTimeEntriesFactory.getInvalidStopToShapeMappingExceptionCount()
+ ". For more information on errors of this kind, see:\n"
+ " https://github.com/OneBusAway/onebusaway-application-modules/wiki/Stop-to-Shape-Matching");
}
graph.refreshTripMapping();
}
private TripEntryImpl processTrip(TransitGraphImpl graph, Trip trip) {
List<StopTime> stopTimes = _gtfsDao.getStopTimesForTrip(trip);
// A trip without stop times is a trip we don't care about
if (stopTimes.isEmpty())
return null;
ShapePoints shapePoints = null;
if (trip.getShapeId() != null)
shapePoints = _shapePointsHelper.getShapePointsForShapeId(trip.getShapeId());
Agency agency = trip.getRoute().getAgency();
TimeZone tz = TimeZone.getTimeZone(agency.getTimezone());
LocalizedServiceId lsid = new LocalizedServiceId(trip.getServiceId(), tz);
TripEntryImpl tripEntry = new TripEntryImpl();
tripEntry.setId(trip.getId());
tripEntry.setDirectionId(unique(trip.getDirectionId()));
tripEntry.setServiceId(unique(lsid));
// Only set the shape id for a trip if there are actually shape points to
// back it up
if (!(shapePoints == null || shapePoints.isEmpty()))
tripEntry.setShapeId(unique(trip.getShapeId()));
List<StopTimeEntryImpl> stopTimesForTrip = _stopTimeEntriesFactory.processStopTimes(
graph, stopTimes, tripEntry, shapePoints);
double tripDistance = getTripDistance(stopTimesForTrip, shapePoints);
tripEntry.setTotalTripDistance(tripDistance);
tripEntry.setStopTimes(cast(stopTimesForTrip));
graph.putTripEntry(tripEntry);
return tripEntry;
}
private List<StopTimeEntry> cast(List<StopTimeEntryImpl> stopTimesForTrip) {
List<StopTimeEntry> stopTimes = new ArrayList<StopTimeEntry>(
stopTimesForTrip.size());
for (StopTimeEntryImpl stopTime : stopTimesForTrip)
stopTimes.add(stopTime);
return stopTimes;
}
private double getTripDistance(List<StopTimeEntryImpl> stopTimes,
ShapePoints shapePoints) {
StopTimeEntryImpl lastStopTime = stopTimes.get(stopTimes.size() - 1);
if (shapePoints != null) {
double[] distances = shapePoints.getDistTraveled();
double distance = distances[shapePoints.getSize() - 1];
return Math.max(lastStopTime.getShapeDistTraveled(), distance);
}
return lastStopTime.getShapeDistTraveled();
}
private <T> T unique(T value) {
return _uniqueService.unique(value);
}
}