/* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.opentripplanner.scripting.api; import java.io.IOException; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.opentripplanner.analyst.batch.Individual; import org.opentripplanner.analyst.batch.Population; import com.csvreader.CsvReader; /** * A population is a collection of individuals. * * Example of use (python script loading a CSV file): * <pre> * spt = ... * pop = otp.loadCSVPopulation('pop.csv', 'lat', 'lon') * result = spt.eval(pop) * for r in result: * print r.getTime() * </pre> * * @author laurent */ public class OtpsPopulation implements Iterable<OtpsIndividual> { private List<OtpsIndividual> individuals; private Map<String, Integer> dataIndex; protected OtpsPopulation() { individuals = new ArrayList<>(); dataIndex = null; } protected OtpsPopulation(Population population) { individuals = new ArrayList<>(); for (Individual ind : population) { addIndividual(ind.lat, ind.lon); } } /** * Set data header keys. Only useful for population you create yourself programmatically. For * CSV population this will be set automatically according to the CSV headers. * * @param headers The data header, in order. Later used as a key to retrieve individual data * value. */ public void setHeaders(String[] headers) { dataIndex = new HashMap<>(); for (int i = 0; i < headers.length; i++) { dataIndex.put(headers[i], i); } } protected int getDataIndex(String dataName) { if (dataIndex == null) return -1; Integer ret = dataIndex.get(dataName); if (ret == null) return -1; return ret; } /** * Add a new data-less individual to the collection. * * @param lat Latitude of the individual location * @param lon Longitude of the individual location */ public void addIndividual(double lat, double lon) { this.addIndividual(lat, lon, null); } /** * Add a new individual with some data attached to it. * * @param lat Latitude of the individual location * @param lon Longitude of the individual location * @param data An array of data to store in the individual */ public void addIndividual(double lat, double lon, String[] data) { OtpsIndividual individual = new OtpsIndividual(lat, lon, data, this); individuals.add(individual); } @Override public Iterator<OtpsIndividual> iterator() { // This seems to work. What the use for Guava ForwardingIterator then? return individuals.iterator(); } // TODO Specify the CRS to use // TODO Use a Map<String, Object> for optional parameters? protected static OtpsPopulation loadFromCSV(String filename, String latColName, String lonColName) throws IOException { OtpsPopulation ret = new OtpsPopulation(); CsvReader reader = new CsvReader(filename, ',', Charset.forName("UTF8")); reader.readHeaders(); // Read headers List<String> dataHeaders = new ArrayList<>(reader.getHeaderCount()); for (String header : reader.getHeaders()) { if (header.equals(latColName) || header.equals(lonColName)) continue; dataHeaders.add(header); } ret.setHeaders(dataHeaders.toArray(new String[dataHeaders.size()])); // Read records while (reader.readRecord()) { double lat = Double.parseDouble(reader.get(latColName)); double lon = Double.parseDouble(reader.get(lonColName)); List<String> data = new ArrayList<String>(); for (String header : dataHeaders) { data.add(reader.get(header)); } OtpsIndividual individual = new OtpsIndividual(lat, lon, data.toArray(new String[data .size()]), ret); ret.individuals.add(individual); } reader.close(); return ret; } }