/**
* 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.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.onebusaway.collections.FactoryMap;
import org.onebusaway.collections.MappingLibrary;
import org.onebusaway.gtfs.model.AgencyAndId;
import org.onebusaway.gtfs.model.Frequency;
import org.onebusaway.gtfs.services.GtfsRelationalDao;
import org.onebusaway.transit_data_federation.impl.blocks.FrequencyComparator;
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.FrequencyEntryImpl;
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.services.transit_graph.BlockConfigurationEntry;
import org.onebusaway.transit_data_federation.services.transit_graph.BlockTripEntry;
import org.onebusaway.transit_data_federation.services.transit_graph.FrequencyEntry;
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 FrequencyEntriesFactory {
private Logger _log = LoggerFactory.getLogger(FrequencyEntriesFactory.class);
private GtfsRelationalDao _gtfsDao;
@Autowired
public void setGtfsDao(GtfsRelationalDao gtfsDao) {
_gtfsDao = gtfsDao;
}
public void processFrequencies(TransitGraphImpl graph) {
Map<AgencyAndId, List<FrequencyEntry>> frequenciesByTripId = new HashMap<AgencyAndId, List<FrequencyEntry>>();
Map<FrequencyRouteLabelKey, List<FrequencyEntry>> frequencyLabelsByKey = new FactoryMap<FrequencyRouteLabelKey, List<FrequencyEntry>>(
new ArrayList<FrequencyEntry>());
Collection<Frequency> allFrequencies = _gtfsDao.getAllFrequencies();
int frequencyIndex = 0;
for (Frequency frequency : allFrequencies) {
if (frequencyIndex % 100 == 0)
_log.info("frequencies: " + (frequencyIndex++) + "/"
+ allFrequencies.size());
frequencyIndex++;
processRawFrequency(graph, frequency, frequenciesByTripId,
frequencyLabelsByKey);
}
FrequencyComparator comparator = new FrequencyComparator();
for (List<FrequencyEntry> list : frequenciesByTripId.values()) {
Collections.sort(list, comparator);
}
applyFrequencyLabelsToTrips(graph, frequencyLabelsByKey);
int blockIndex = 0;
Map<AgencyAndId, List<TripEntryImpl>> tripsByBlockId = MappingLibrary.mapToValueList(
graph.getTrips(), "block.id");
for (Map.Entry<AgencyAndId, List<TripEntryImpl>> entry : tripsByBlockId.entrySet()) {
if (blockIndex % 10 == 0)
_log.info("block: " + blockIndex + "/" + tripsByBlockId.size());
blockIndex++;
AgencyAndId blockId = entry.getKey();
List<TripEntryImpl> tripsInBlock = entry.getValue();
Map<AgencyAndId, List<FrequencyEntry>> frequenciesAlongBlockByTripId = new HashMap<AgencyAndId, List<FrequencyEntry>>();
Map<AgencyAndId, FrequencyEntry> frequencyLabelsAlongBlockByTripId = new HashMap<AgencyAndId, FrequencyEntry>();
for (TripEntryImpl trip : tripsInBlock) {
List<FrequencyEntry> frequencies = frequenciesByTripId.get(trip.getId());
if (frequencies != null) {
frequenciesAlongBlockByTripId.put(trip.getId(), frequencies);
}
if (trip.getFrequencyLabel() != null) {
frequencyLabelsAlongBlockByTripId.put(trip.getId(),
trip.getFrequencyLabel());
}
}
checkForInvalidFrequencyConfigurations(blockId, tripsInBlock,
frequenciesAlongBlockByTripId, frequencyLabelsAlongBlockByTripId);
if (!frequenciesAlongBlockByTripId.isEmpty()) {
applyFrequenciesToBlockTrips(tripsInBlock,
frequenciesAlongBlockByTripId);
} else if (!frequencyLabelsAlongBlockByTripId.isEmpty()) {
} else {
}
}
}
private void processRawFrequency(TransitGraphImpl graph, Frequency frequency,
Map<AgencyAndId, List<FrequencyEntry>> frequenciesByTripId,
Map<FrequencyRouteLabelKey, List<FrequencyEntry>> frequencyLabelsByKey) {
AgencyAndId tripId = frequency.getTrip().getId();
TripEntry trip = graph.getTripEntryForId(tripId);
FrequencyEntryImpl entry = new FrequencyEntryImpl(frequency.getStartTime(),
frequency.getEndTime(), frequency.getHeadwaySecs());
FrequencyRouteLabelKey key = getFrequencyLabelKeyForTrip(frequency, trip);
if (key != null) {
frequencyLabelsByKey.get(key).add(entry);
} else {
List<FrequencyEntry> frequencies = frequenciesByTripId.get(tripId);
if (frequencies == null) {
frequencies = new ArrayList<FrequencyEntry>();
frequenciesByTripId.put(tripId, frequencies);
}
frequencies.add(entry);
}
}
private void checkForInvalidFrequencyConfigurations(AgencyAndId blockId,
List<TripEntryImpl> tripsInBlock,
Map<AgencyAndId, List<FrequencyEntry>> frequenciesAlongBlockByTripId,
Map<AgencyAndId, FrequencyEntry> frequencyLabelsAlongBlockByTripId) {
if (!frequenciesAlongBlockByTripId.isEmpty()
&& !frequencyLabelsAlongBlockByTripId.isEmpty()) {
throw new IllegalStateException(
"A block of trips cannot have trips with both normal Frequency entries and label Frequency entries: blockId="
+ blockId);
}
if (!frequenciesAlongBlockByTripId.isEmpty()
&& frequenciesAlongBlockByTripId.size() < tripsInBlock.size()) {
throw new IllegalStateException(
"can't have mixture of trips with and without frequencies: blockId="
+ blockId);
}
}
private void applyFrequenciesToBlockTrips(List<TripEntryImpl> tripsInBlock,
Map<AgencyAndId, List<FrequencyEntry>> frequenciesAlongBlockByTripId) {
BlockEntryImpl blockEntry = tripsInBlock.get(0).getBlock();
List<BlockConfigurationEntry> configurations = blockEntry.getConfigurations();
for (int i = 0; i < configurations.size(); i++) {
BlockConfigurationEntryImpl blockConfig = (BlockConfigurationEntryImpl) configurations.get(i);
List<FrequencyEntry> frequencies = computeBlockFrequencies(blockEntry,
blockConfig.getTrips(), frequenciesAlongBlockByTripId);
blockConfig.setFrequencies(frequencies);
}
}
private void applyFrequencyLabelsToTrips(TransitGraphImpl graph,
Map<FrequencyRouteLabelKey, List<FrequencyEntry>> frequencyLabelsByKey) {
for (TripEntry trip : graph.getAllTrips()) {
List<FrequencyEntry> applicableFrequencyLabels = getApplicableFrequencyLabelsForTrip(
frequencyLabelsByKey, trip);
if (applicableFrequencyLabels.isEmpty()) {
continue;
} else if (applicableFrequencyLabels.size() > 1) {
throw new IllegalStateException(
"multiple applicable frequency labels for tripId=" + trip.getId()
+ " frequencies=" + applicableFrequencyLabels);
}
FrequencyEntry frequencyLabel = applicableFrequencyLabels.get(0);
((TripEntryImpl) trip).setFrequencyLabel(frequencyLabel);
}
}
private List<FrequencyEntry> getApplicableFrequencyLabelsForTrip(
Map<FrequencyRouteLabelKey, List<FrequencyEntry>> frequencyLabelsByKey,
TripEntry trip) {
AgencyAndId routeId = trip.getRoute().getId();
AgencyAndId serviceId = trip.getServiceId().getId();
String directionId = trip.getDirectionId();
List<AgencyAndId> stopIds = MappingLibrary.map(trip.getStopTimes(),
"stop.id");
FrequencyRouteLabelKey keyA = new FrequencyRouteLabelKey(routeId, serviceId);
FrequencyDirectionLabelKey keyB = new FrequencyDirectionLabelKey(routeId,
serviceId, directionId);
FrequencyStopsLabelKey keyC = new FrequencyStopsLabelKey(routeId,
serviceId, stopIds);
List<FrequencyEntry> applicableLabels = new ArrayList<FrequencyEntry>();
FrequencyRouteLabelKey[] keys = {keyA, keyB, keyC};
for (FrequencyRouteLabelKey key : keys) {
List<FrequencyEntry> labels = frequencyLabelsByKey.get(key);
if (labels == null) {
continue;
}
int startTime = trip.getStopTimes().get(0).getDepartureTime();
for (FrequencyEntry label : labels) {
if (label.getStartTime() <= startTime && startTime < label.getEndTime()) {
applicableLabels.add(label);
}
}
}
return applicableLabels;
}
private FrequencyRouteLabelKey getFrequencyLabelKeyForTrip(
Frequency frequency, TripEntry trip) {
AgencyAndId routeId = trip.getRoute().getId();
AgencyAndId serviceId = trip.getServiceId().getId();
switch (frequency.getLabelOnly()) {
// Route
case 1: {
return new FrequencyRouteLabelKey(routeId, serviceId);
}
// Direction
case 2: {
AgencyAndId routeId1 = trip.getRoute().getId();
AgencyAndId serviceId1 = trip.getServiceId().getId();
return new FrequencyDirectionLabelKey(routeId1, serviceId1,
trip.getDirectionId());
}
// Stops
case 3: {
List<AgencyAndId> stopIds = MappingLibrary.map(trip.getStopTimes(),
"stop.id");
return new FrequencyStopsLabelKey(routeId, serviceId, stopIds);
}
}
return null;
}
private List<FrequencyEntry> computeBlockFrequencies(BlockEntryImpl block,
List<BlockTripEntry> trips,
Map<AgencyAndId, List<FrequencyEntry>> frequenciesAlongBlock) {
List<FrequencyEntry> frequencies = null;
for (BlockTripEntry blockTrip : trips) {
TripEntry trip = blockTrip.getTrip();
List<FrequencyEntry> potentialFrequencies = frequenciesAlongBlock.get(trip.getId());
if (frequencies == null) {
frequencies = potentialFrequencies;
} else {
if (!frequencies.equals(potentialFrequencies)) {
throw new IllegalStateException(
"frequency-based trips in same block don't have same frequencies: blockId="
+ block.getId());
}
}
}
return frequencies;
}
}