/**
* Copyright (C) 2011 Brian Ferris <bdferris@onebusaway.org>
*
* 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.impl.realtime.history;
import java.util.Arrays;
import org.onebusaway.collections.Range;
import org.onebusaway.gtfs.model.AgencyAndId;
import org.onebusaway.realtime.api.VehicleLocationRecord;
import org.onebusaway.transit_data_federation.services.blocks.BlockInstance;
import org.onebusaway.transit_data_federation.services.blocks.ScheduledBlockLocation;
import org.onebusaway.transit_data_federation.services.realtime.ArrivalAndDepartureInstance;
import org.onebusaway.transit_data_federation.services.realtime.RealTimeHistoryService;
import org.onebusaway.transit_data_federation.services.realtime.ScheduleDeviationHistogram;
import org.onebusaway.transit_data_federation.services.realtime.ScheduleDeviationSamples;
import org.onebusaway.transit_data_federation.services.transit_graph.BlockStopTimeEntry;
import org.onebusaway.transit_data_federation.services.transit_graph.BlockTripEntry;
import org.onebusaway.transit_data_federation.services.transit_graph.StopTimeEntry;
import org.onebusaway.transit_data_federation.services.transit_graph.TripEntry;
import org.onebusaway.utility.InterpolationLibrary;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import cern.colt.list.DoubleArrayList;
import cern.jet.random.Normal;
import cern.jet.random.engine.RandomEngine;
import cern.jet.stat.Descriptive;
@Component
public class RealTimeHistoryServiceImpl implements RealTimeHistoryService {
private ScheduleDeviationHistoryDaoImpl _scheduleDeviationHistoryDao;
private int _predictionLookahead = 20 * 60;
private Normal _schedDevScaleParam = new Normal(0, 5 * 60,
RandomEngine.makeDefault());
@Autowired
public void setScheduleDeviationHistoryDao(
ScheduleDeviationHistoryDaoImpl scheduleDeviationHistoryDao) {
_scheduleDeviationHistoryDao = scheduleDeviationHistoryDao;
}
public void setPredictionLookahead(int predictionLookahead) {
_predictionLookahead = predictionLookahead;
}
@Override
public ScheduleDeviationHistogram getScheduleDeviationHistogramForArrivalAndDepartureInstance(
ArrivalAndDepartureInstance instance, int stepSizeInSeconds) {
BlockTripEntry blockTrip = instance.getBlockTrip();
TripEntry trip = blockTrip.getTrip();
AgencyAndId tripId = trip.getId();
ScheduleDeviationHistory history = _scheduleDeviationHistoryDao.getScheduleDeviationHistoryForTripId(tripId);
if( history == null)
return null;
BlockStopTimeEntry blockStopTime = instance.getBlockStopTime();
StopTimeEntry stopTime = blockStopTime.getStopTime();
double[] values = getScheduleDeviationsForScheduleTime(history,
stopTime.getDepartureTime());
return createHistogramFromValues(values, stepSizeInSeconds);
}
@Override
public ScheduleDeviationSamples sampleScheduleDeviationsForVehicle(
BlockInstance instance, VehicleLocationRecord record,
ScheduledBlockLocation scheduledBlockLocation) {
if( true )
return null;
BlockTripEntry blockTrip = scheduledBlockLocation.getActiveTrip();
TripEntry trip = blockTrip.getTrip();
ScheduleDeviationHistory history = _scheduleDeviationHistoryDao.getScheduleDeviationHistoryForTripId(trip.getId());
if (history == null)
return null;
ScheduleDeviationHistory resampledHistory = resampleHistory(history,
scheduledBlockLocation.getScheduledTime(),
record.getScheduleDeviation());
DoubleArrayList scheduleTimes = new DoubleArrayList();
DoubleArrayList mus = new DoubleArrayList();
DoubleArrayList sigmas = new DoubleArrayList();
for (int t = 0; t <= _predictionLookahead; t += 5 * 60) {
int scheduleTime = scheduledBlockLocation.getScheduledTime() + t;
double[] deviations = getScheduleDeviationsForScheduleTime(
resampledHistory, scheduleTime);
deviations = noNans(deviations);
DoubleArrayList values = new DoubleArrayList(deviations);
double mu = Descriptive.mean(values);
double var = Descriptive.sampleVariance(values, mu);
double sigma = Descriptive.sampleStandardDeviation(values.size(), var);
scheduleTimes.add(scheduleTime);
mus.add(mu);
sigmas.add(sigma);
}
scheduleTimes.trimToSize();
mus.trimToSize();
sigmas.trimToSize();
return new ScheduleDeviationSamples(scheduleTimes.elements(),
mus.elements(), sigmas.elements());
}
/****
* Private
****/
/**
* @param history
* @param distanceAlongBlock
* @return
*/
private double[] getScheduleDeviationsForScheduleTime(
ScheduleDeviationHistory history, int scheduleTime) {
double[] scheduleTimes = history.getScheduleTimes();
double[][] scheduleDeviations = history.getScheduleDeviations();
int index = Arrays.binarySearch(scheduleTimes, scheduleTime);
if (index >= 0)
return getColumn(scheduleDeviations, index);
index = -(index + 1);
if (index == scheduleTimes.length)
return getColumn(scheduleDeviations, index - 1);
if (index == 0)
return getColumn(scheduleDeviations, 0);
int numSamples = history.getNumberOfSamples();
double[] values = new double[numSamples];
for (int i = 0; i < numSamples; i++) {
double fromKey = scheduleTimes[index - 1];
double toKey = scheduleTimes[index];
double fromValue = scheduleDeviations[i][index - 1];
double toValue = scheduleDeviations[i][index];
values[i] = InterpolationLibrary.interpolatePair(fromKey, fromValue,
toKey, toValue, scheduleTime);
}
return values;
}
private double[] getColumn(double[][] values, int index) {
double[] v = new double[values.length];
for (int i = 0; i < values.length; i++)
v[i] = values[i][index];
return v;
}
private double[] noNans(double[] values) {
DoubleArrayList vs = new DoubleArrayList();
for (double v : values) {
if (!Double.isNaN(v))
vs.add(v);
}
vs.trimToSize();
return vs.elements();
}
private ScheduleDeviationHistogram createHistogramFromValues(double[] values,
int stepSizeInSeconds) {
values = noNans(values);
if (values.length == 0)
return new ScheduleDeviationHistogram(new int[0], new int[0]);
Range r = new Range();
for (double v : values)
r.addValue(v);
if (r.getRange() == 0)
return new ScheduleDeviationHistogram(new int[] {(int) values[0]},
new int[] {values.length});
int halfStep = stepSizeInSeconds / 2;
int from = (int) (Math.floor((r.getMin()-halfStep) / stepSizeInSeconds) * stepSizeInSeconds) + halfStep;
int to = (int) (Math.ceil((r.getMax()+halfStep) / stepSizeInSeconds) * stepSizeInSeconds) - halfStep;
int columns = (to - from) / stepSizeInSeconds;
int[] scheduleDeviations = new int[columns];
int[] counts = new int[columns];
for (int i = 0; i < columns; i++)
scheduleDeviations[i] = from + stepSizeInSeconds * i + halfStep;
for (double value : values) {
int index = (int) ((value - from) / stepSizeInSeconds);
counts[index]++;
}
return new ScheduleDeviationHistogram(scheduleDeviations, counts);
}
private ScheduleDeviationHistory resampleHistory(
ScheduleDeviationHistory history, int scheduleTime, double activeDeviation) {
double[] deviations = getScheduleDeviationsForScheduleTime(history,
scheduleTime);
CDFMap<Integer> cdf = new CDFMap<Integer>();
for (int i = 0; i < deviations.length; i++) {
double deviation = deviations[i];
if (Double.isNaN(deviation))
continue;
double p = _schedDevScaleParam.apply(activeDeviation - deviation);
cdf.put(p, i);
}
int numSamples = deviations.length;
double[] scheduleTimes = history.getScheduleTimes();
double[][] scheduleDeviations = history.getScheduleDeviations();
double[][] sampledScheduleDeviations = new double[numSamples][];
for (int i = 0; i < numSamples; i++) {
int index = cdf.sample();
sampledScheduleDeviations[i] = scheduleDeviations[index];
}
return new ScheduleDeviationHistory(history.getTripId(), scheduleTimes,
sampledScheduleDeviations);
}
}