/** * 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.blocks; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.onebusaway.collections.FactoryMap; import org.onebusaway.gtfs.model.calendar.ServiceInterval; import org.onebusaway.transit_data_federation.impl.transit_graph.FrequencyBlockStopTimeEntryImpl; import org.onebusaway.transit_data_federation.services.blocks.BlockIndexService; import org.onebusaway.transit_data_federation.services.blocks.BlockStopTimeIndex; import org.onebusaway.transit_data_federation.services.blocks.FrequencyBlockStopTimeIndex; import org.onebusaway.transit_data_federation.services.transit_graph.BlockConfigurationEntry; import org.onebusaway.transit_data_federation.services.transit_graph.BlockEntry; 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.FrequencyBlockStopTimeEntry; import org.onebusaway.transit_data_federation.services.transit_graph.FrequencyEntry; import org.onebusaway.transit_data_federation.services.transit_graph.StopEntry; import org.onebusaway.transit_data_federation.services.transit_graph.StopTimeEntry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Construct {@link BlockStopTimeIndex} indices from all {@link BlockEntry} in * the transit graph. The indices created by this factory are grouped by * serviceIds, such that all {@link BlockStopTimeEntry} stop entries with the * same active service ids are grouped together. * * @author bdferris * @see BlockStopTimeIndex * @see BlockIndexService */ public class BlockStopTimeIndicesFactory { private static Logger _log = LoggerFactory.getLogger(BlockStopTimeIndicesFactory.class); private static final BlockStopTimeComparator _blockStopTimeLooseComparator = new BlockStopTimeComparator(); private static final BlockStopTimeStrictComparator _blockStopTimeStrictComparator = new BlockStopTimeStrictComparator(); private static final FrequencyBlockStopTimeComparator _frequencyBlockStopTimeLooseComparator = new FrequencyBlockStopTimeComparator(); private static final FrequencyBlockStopTimeStrictComparator _frequencyBlockStopTimeStrictComparator = new FrequencyBlockStopTimeStrictComparator(); private boolean _verbose = false; public void setVerbose(boolean verbose) { _verbose = verbose; } /**** * ****/ public List<BlockStopTimeIndex> createIndices(Iterable<BlockEntry> blocks) { Map<BlockStopTimeKey, List<BlockStopTimeEntry>> stopTimesByKey = groupBlockStopTimes( blocks, false); return createIndicesFromGroups(stopTimesByKey); } public List<FrequencyBlockStopTimeIndex> createFrequencyIndices( Iterable<BlockEntry> blocks) { Map<BlockStopTimeKey, List<BlockStopTimeEntry>> stopTimesByKey = groupBlockStopTimes( blocks, true); return createFrequencyIndicesFromGroups(stopTimesByKey); } /**** * ****/ private Map<BlockStopTimeKey, List<BlockStopTimeEntry>> groupBlockStopTimes( Iterable<BlockEntry> blocks, boolean frequencyBased) { Map<BlockStopTimeKey, List<BlockStopTimeEntry>> stopTimesByKey = new FactoryMap<BlockStopTimeKey, List<BlockStopTimeEntry>>( new ArrayList<BlockStopTimeEntry>()); if (_verbose) _log.info("grouping block stop times by key"); int stopTimeCount = 0; for (BlockEntry block : blocks) { List<BlockConfigurationEntry> configurations = block.getConfigurations(); if (configurations.isEmpty()) { _log.warn("block has no active configurations: " + block.getId()); continue; } if (BlockLibrary.isFrequencyBased(block) != frequencyBased) continue; for (BlockConfigurationEntry blockConfiguration : configurations) { List<BlockStopTimeEntry> blockStopTimes = blockConfiguration.getStopTimes(); for (BlockStopTimeEntry blockStopTime : blockStopTimes) { BlockStopTimeKey key = getBlockStopTimeAsKey(blockStopTime); List<BlockStopTimeEntry> stopTimesForKey = stopTimesByKey.get(key); stopTimesForKey.add(blockStopTime); stopTimeCount++; } } } if (_verbose) _log.info("groups found: " + stopTimesByKey.size() + " out of stopTimes: " + stopTimeCount); return stopTimesByKey; } private BlockStopTimeKey getBlockStopTimeAsKey( BlockStopTimeEntry blockStopTime) { BlockTripEntry blockTrip = blockStopTime.getTrip(); BlockConfigurationEntry blockConfig = blockTrip.getBlockConfiguration(); StopTimeEntry stopTime = blockStopTime.getStopTime(); StopEntry stop = stopTime.getStop(); return new BlockStopTimeKey(blockConfig.getServiceIds(), stop.getId()); } /**** * ****/ private List<BlockStopTimeIndex> createIndicesFromGroups( Map<BlockStopTimeKey, List<BlockStopTimeEntry>> stopTimesByKey) { List<BlockStopTimeIndex> allIndices = new ArrayList<BlockStopTimeIndex>(); int count = 0; for (List<BlockStopTimeEntry> stopTimes : stopTimesByKey.values()) { if (_verbose && count % 1000 == 0) _log.info("groups processed: " + count + "/" + stopTimesByKey.size()); count++; List<List<BlockStopTimeEntry>> groupedStopTimes = BlockLibrary.createStrictlyOrderedGroups( stopTimes, _blockStopTimeLooseComparator, _blockStopTimeStrictComparator); for (List<BlockStopTimeEntry> group : groupedStopTimes) { BlockStopTimeIndex index = createBlockStopTimeIndexForGroup(group); allIndices.add(index); } } return allIndices; } private BlockStopTimeIndex createBlockStopTimeIndexForGroup( List<BlockStopTimeEntry> group) { int n = group.size(); List<BlockConfigurationEntry> blockConfigs = new ArrayList<BlockConfigurationEntry>( n); int[] stopIndices = new int[n]; ServiceInterval interval = null; for (int i = 0; i < n; i++) { BlockStopTimeEntry blockStopTime = group.get(i); StopTimeEntry stopTime = blockStopTime.getStopTime(); blockConfigs.add(blockStopTime.getTrip().getBlockConfiguration()); stopIndices[i] = blockStopTime.getBlockSequence(); interval = ServiceInterval.extend(interval, stopTime.getArrivalTime(), stopTime.getDepartureTime()); } return new BlockStopTimeIndex(blockConfigs, stopIndices, interval); } /**** * ****/ private List<FrequencyBlockStopTimeIndex> createFrequencyIndicesFromGroups( Map<BlockStopTimeKey, List<BlockStopTimeEntry>> stopTimesByKey) { List<FrequencyBlockStopTimeIndex> allIndices = new ArrayList<FrequencyBlockStopTimeIndex>(); int count = 0; for (List<BlockStopTimeEntry> stopTimes : stopTimesByKey.values()) { if (_verbose && count % 100 == 0) _log.info("groups processed: " + count + "/" + stopTimesByKey.size()); count++; List<FrequencyBlockStopTimeEntry> frequencyStopTimes = getStopTimesAsFrequencyStopTimes(stopTimes); List<List<FrequencyBlockStopTimeEntry>> groupedStopTimes = BlockLibrary.createStrictlyOrderedGroups( frequencyStopTimes, _frequencyBlockStopTimeLooseComparator, _frequencyBlockStopTimeStrictComparator); for (List<FrequencyBlockStopTimeEntry> group : groupedStopTimes) { FrequencyBlockStopTimeIndex index = createFrequencyBlockStopTimeIndexForGroup(group); allIndices.add(index); } } return allIndices; } private List<FrequencyBlockStopTimeEntry> getStopTimesAsFrequencyStopTimes( List<BlockStopTimeEntry> stopTimes) { List<FrequencyBlockStopTimeEntry> frequencyStopTimes = new ArrayList<FrequencyBlockStopTimeEntry>(); for (BlockStopTimeEntry blockStopTime : stopTimes) { BlockTripEntry trip = blockStopTime.getTrip(); BlockConfigurationEntry blockConfig = trip.getBlockConfiguration(); for (FrequencyEntry frequency : blockConfig.getFrequencies()) { FrequencyBlockStopTimeEntry frequencyStopTime = new FrequencyBlockStopTimeEntryImpl( blockStopTime, frequency); frequencyStopTimes.add(frequencyStopTime); } } return frequencyStopTimes; } private FrequencyBlockStopTimeIndex createFrequencyBlockStopTimeIndexForGroup( List<FrequencyBlockStopTimeEntry> group) { int n = group.size(); List<FrequencyEntry> frequencies = new ArrayList<FrequencyEntry>(n); List<BlockConfigurationEntry> blockConfigs = new ArrayList<BlockConfigurationEntry>( n); int[] stopIndices = new int[n]; ServiceInterval interval = null; for (int i = 0; i < n; i++) { FrequencyBlockStopTimeEntry frequencyBlockStopTime = group.get(i); FrequencyEntry frequency = frequencyBlockStopTime.getFrequency(); frequencies.add(frequency); BlockStopTimeEntry blockStopTime = frequencyBlockStopTime.getStopTime(); blockConfigs.add(blockStopTime.getTrip().getBlockConfiguration()); stopIndices[i] = blockStopTime.getBlockSequence(); interval = ServiceInterval.extend(interval, frequency.getStartTime(), frequency.getStartTime()); interval = ServiceInterval.extend(interval, frequency.getEndTime(), frequency.getEndTime()); } return new FrequencyBlockStopTimeIndex(frequencies, blockConfigs, stopIndices, interval); } }