/**
* 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.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import org.onebusaway.collections.FactoryMap;
import org.onebusaway.geospatial.services.SphericalGeometryLibrary;
import org.onebusaway.gtfs.model.calendar.LocalizedServiceId;
import org.onebusaway.transit_data_federation.bundle.tasks.ShapePointHelper;
import org.onebusaway.transit_data_federation.impl.transit_graph.BlockConfigurationEntryImpl;
import org.onebusaway.transit_data_federation.impl.transit_graph.BlockEntryImpl;
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.BlockConfigurationEntry;
import org.onebusaway.transit_data_federation.services.transit_graph.ServiceIdActivation;
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 BlockConfigurationEntriesFactory {
private static Logger _log = LoggerFactory.getLogger(BlockConfigurationEntriesFactory.class);
private BlockTripComparator _blockTripComparator = new BlockTripComparator();
private BlockConfigurationComparator _blockConfigurationComparator = new BlockConfigurationComparator();
private ServiceIdOverlapCache _serviceIdOverlapCache;
private ShapePointHelper _shapePointHelper;
@Autowired
public void setServiceIdOverlapCache(
ServiceIdOverlapCache serviceIdOverlapCache) {
_serviceIdOverlapCache = serviceIdOverlapCache;
}
@Autowired
public void setShapePointHelper(ShapePointHelper shapePointHelper) {
_shapePointHelper = shapePointHelper;
}
public void processBlockConfigurations(BlockEntryImpl block,
List<TripEntryImpl> tripsInBlock) {
Map<LocalizedServiceId, List<TripEntryImpl>> tripsByServiceId = getTripsByServiceId(
block, tripsInBlock);
List<ServiceIdActivation> combinations = _serviceIdOverlapCache.getOverlappingServiceIdCombinations(tripsByServiceId.keySet());
ArrayList<BlockConfigurationEntry> configurations = new ArrayList<BlockConfigurationEntry>();
for (ServiceIdActivation serviceIds : combinations) {
BlockConfigurationEntryImpl.Builder builder = processTripsForServiceIdConfiguration(
block, tripsByServiceId, serviceIds);
configurations.add(builder.create());
}
Collections.sort(configurations, _blockConfigurationComparator);
configurations.trimToSize();
if (configurations.isEmpty())
_log.warn("no active block configurations found for block: "
+ block.getId());
block.setConfigurations(configurations);
}
/****
* Private Methods
****/
private Map<LocalizedServiceId, List<TripEntryImpl>> getTripsByServiceId(
BlockEntryImpl block, List<TripEntryImpl> tripsInBlock) {
Map<LocalizedServiceId, List<TripEntryImpl>> tripsByServiceId = new FactoryMap<LocalizedServiceId, List<TripEntryImpl>>(
new ArrayList<TripEntryImpl>());
TimeZone tz = null;
for (TripEntryImpl trip : tripsInBlock) {
LocalizedServiceId serviceId = trip.getServiceId();
if (tz == null) {
tz = serviceId.getTimeZone();
} else if (!tz.equals(serviceId.getTimeZone())) {
throw new IllegalStateException(
"trips in block must all have same timezone: block=" + block
+ " trip=" + trip + " execpted=" + tz + " actual="
+ serviceId.getTimeZone());
}
tripsByServiceId.get(serviceId).add(trip);
}
return tripsByServiceId;
}
private BlockConfigurationEntryImpl.Builder processTripsForServiceIdConfiguration(
BlockEntryImpl block,
Map<LocalizedServiceId, List<TripEntryImpl>> tripsByServiceId,
ServiceIdActivation serviceIds) {
ArrayList<TripEntry> trips = new ArrayList<TripEntry>();
for (LocalizedServiceId serviceId : serviceIds.getActiveServiceIds()) {
trips.addAll(tripsByServiceId.get(serviceId));
}
Collections.sort(trips, _blockTripComparator);
trips.trimToSize();
double[] tripGapDistances = computeGapDistancesBetweenTrips(trips);
BlockConfigurationEntryImpl.Builder builder = BlockConfigurationEntryImpl.builder();
builder.setBlock(block);
builder.setServiceIds(serviceIds);
builder.setTrips(trips);
builder.setTripGapDistances(tripGapDistances);
return builder;
}
private double[] computeGapDistancesBetweenTrips(List<TripEntry> trips) {
double[] tripGapDistances = new double[trips.size()];
if (_shapePointHelper == null)
return tripGapDistances;
for (int index = 0; index < trips.size() - 1; index++) {
TripEntry tripA = trips.get(index);
TripEntry tripB = trips.get(index + 1);
double d = 0;
ShapePoints shapeFrom = _shapePointHelper.getShapePointsForShapeId(tripA.getShapeId());
ShapePoints shapeTo = _shapePointHelper.getShapePointsForShapeId(tripB.getShapeId());
if (shapeFrom != null && shapeTo != null && !shapeFrom.isEmpty()
&& !shapeTo.isEmpty()) {
int n = shapeFrom.getSize();
double lat1 = shapeFrom.getLatForIndex(n - 1);
double lon1 = shapeFrom.getLonForIndex(n - 1);
double lat2 = shapeTo.getLatForIndex(0);
double lon2 = shapeTo.getLonForIndex(0);
d = SphericalGeometryLibrary.distance(lat1, lon1, lat2, lon2);
}
tripGapDistances[index] = d;
}
return tripGapDistances;
}
private static class BlockTripComparator implements Comparator<TripEntry> {
public int compare(TripEntry o1, TripEntry o2) {
int t1 = getAverageTime(o1);
int t2 = getAverageTime(o2);
return t1 - t2;
}
private int getAverageTime(TripEntry trip) {
List<StopTimeEntry> stopTimes = trip.getStopTimes();
if (stopTimes == null || stopTimes.isEmpty())
throw new IllegalStateException("no StopTimes defined for trip " + trip);
int departureTimes = 0;
for (StopTimeEntry stopTime : stopTimes) {
departureTimes += stopTime.getDepartureTime();
}
return departureTimes / stopTimes.size();
}
}
private static class BlockConfigurationComparator implements
Comparator<BlockConfigurationEntry> {
@Override
public int compare(BlockConfigurationEntry o1, BlockConfigurationEntry o2) {
return o1.getServiceIds().compareTo(o2.getServiceIds());
}
}
}