/** * 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.bundle.tasks.transfer_pattern; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.zip.GZIPInputStream; import org.onebusaway.csv_entities.CSVLibrary; import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.transit_data_federation.services.AgencyAndIdLibrary; import org.onebusaway.transit_data_federation.services.transit_graph.StopEntry; import org.onebusaway.transit_data_federation.services.transit_graph.TransitGraphDao; import org.onebusaway.transit_data_federation.services.tripplanner.CompactedTransferPattern; import org.onebusaway.transit_data_federation.services.tripplanner.TransferPattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class CompactedTransferPatternFactory { private static Logger _log = LoggerFactory.getLogger(CompactedTransferPatternFactory.class); private final List<CompactedTransferPatternFactoryListener> _listeners = new ArrayList<CompactedTransferPatternFactoryListener>(); private final Map<StopEntry, TransferPattern> _patternsByOriginStop = new HashMap<StopEntry, TransferPattern>(); private final TransitGraphDao _dao; private long _lines = 0; private List<StopEntry> _allStops; private Map<StopEntry, Short> _indices = new HashMap<StopEntry, Short>(); private Set<StopEntry> _hubStops = Collections.emptySet(); private Set<String> _pruneFromParent = new HashSet<String>(); private Map<String, Integer> _depths = new HashMap<String, Integer>(); public CompactedTransferPatternFactory(TransitGraphDao dao) { _dao = dao; _allStops = dao.getAllStops(); if (_allStops.size() > Short.MAX_VALUE) throw new IllegalStateException("more than " + Short.MAX_VALUE + " stops means our indexing trick will no long work"); for (short in = (short) 0; in < _allStops.size(); in++) _indices.put(_allStops.get(in), in); } public void addListener(CompactedTransferPatternFactoryListener listener) { _listeners.add(listener); } public void setHubStops(Set<StopEntry> hubStops) { _hubStops = hubStops; } public Map<StopEntry, TransferPattern> getPatternsByOriginStop() { return _patternsByOriginStop; } public void clear() { _patternsByOriginStop.clear(); } public void readPatternsFromFile(File path) throws IOException { BufferedReader reader = openFile(path); String line = null; List<Record> records = new ArrayList<Record>(); StopEntry originStop = null; boolean isOriginHubStop = false; while ((line = reader.readLine()) != null) { if (_lines % 1000000 == 0) _log.info("lines=" + _lines); if (line.length() == 0) continue; List<String> tokens = new CSVLibrary().parse(line); AgencyAndId stopId = AgencyAndIdLibrary.convertFromString(tokens.get(1)); StopEntry stop = _dao.getStopEntryForId(stopId, true); short stopIndex = _indices.get(stop); String key = tokens.get(0); ERecordType type = getRecordTypeForValue(tokens.get(2)); int depth = 0; if (tokens.size() == 3) { compact(originStop, records); originStop = stop; isOriginHubStop = _hubStops.contains(originStop); _pruneFromParent.clear(); _depths.clear(); _depths.put(key, 0); } String parentKey = null; if (tokens.size() == 4) { parentKey = tokens.get(3); if (_pruneFromParent.contains(parentKey)) { _pruneFromParent.add(key); continue; } if( ! _depths.containsKey(parentKey)) System.out.println("here"); depth = _depths.get(parentKey) + 1; _depths.put(key, depth); } if (!isOriginHubStop && _hubStops.contains(stop) && depth > 0 && (depth % 2) == 0) { _pruneFromParent.add(key); type = ERecordType.HUB; } Record record = new Record(key, stopIndex, parentKey, type); records.add(record); _lines++; } if (!records.isEmpty()) compact(originStop, records); reader.close(); } private ERecordType getRecordTypeForValue(String v) { if (v.equals("0")) return ERecordType.NODE; if (v.equals("1")) return ERecordType.EXIT_ALLOWED; if (v.equals("2")) return ERecordType.HUB; throw new IllegalStateException("uknown record type"); } public long getLines() { return _lines; } /**** * Private Methods ****/ private BufferedReader openFile(File path) throws IOException { InputStream in = new FileInputStream(path); if (path.getName().endsWith(".gz")) in = new GZIPInputStream(in); return new BufferedReader(new InputStreamReader(in)); } private void compact(StopEntry originStop, List<Record> records) { if (records.isEmpty()) return; Collections.sort(records); Map<String, Integer> offsets = new HashMap<String, Integer>(); for (Record record : records) offsets.put(record.key, offsets.size()); short[] stopIndexArray = new short[records.size()]; int[] parentIndicesArray = new int[records.size()]; int exitAllowedOffset = records.size(); int hubOffset = records.size();; for (int i = 0; i < records.size(); i++) { Record record = records.get(i); stopIndexArray[i] = record.stopIndex; int offset = -1; if (record.parentKey != null) offset = offsets.get(record.parentKey); parentIndicesArray[i] = offset; switch (record.type) { case NODE: exitAllowedOffset = i + 1; hubOffset = i + 1; break; case EXIT_ALLOWED: hubOffset = i + 1; break; } } if (exitAllowedOffset == -1 || hubOffset == -1) throw new IllegalStateException(); CompactedTransferPattern pattern = new CompactedTransferPattern( stopIndexArray, parentIndicesArray, exitAllowedOffset, hubOffset); pattern.setAllStops(_allStops); TransferPattern existing = _patternsByOriginStop.put(originStop, pattern); if (existing != null) _log.warn("overriding pattern for stop " + originStop.getId()); for (CompactedTransferPatternFactoryListener listener : _listeners) listener.patternProcessed(this, originStop, pattern); records.clear(); } private enum ERecordType { NODE, EXIT_ALLOWED, HUB } private static class Record implements Comparable<Record> { private final String key; private final short stopIndex; private final String parentKey; private final ERecordType type; public Record(String key, short stopIndex, String parentKey, ERecordType type) { this.key = key; this.stopIndex = stopIndex; this.parentKey = parentKey; this.type = type; } @Override public int compareTo(Record o) { int c = this.type.compareTo(o.type); if (c != 0) return c; return (this.stopIndex - o.stopIndex); } } }