/* 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.analyst.batch;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import lombok.Getter;
import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.csvreader.CsvWriter;
public class BasicPopulation implements Population {
private static final Logger LOG = LoggerFactory.getLogger(BasicPopulation.class);
@Setter
public String sourceFilename;
@Setter @Getter
public List<Individual> individuals = new ArrayList<Individual>();
@Setter @Getter
public List<IndividualFilter> filterChain = null;
private boolean[] skip = null;
public BasicPopulation() { }
public BasicPopulation(Individual... individuals) {
this.individuals = Arrays.asList(individuals);
}
public BasicPopulation(Collection<Individual> individuals) {
this.individuals = new ArrayList<Individual>(individuals);
}
@Override
public void addIndividual(Individual individual) {
this.individuals.add(individual);
}
@Override
public Iterator<Individual> iterator() {
return new PopulationIterator();
}
@Override
public void clearIndividuals(List<Individual> individuals) {
this.individuals.clear();
}
@Override
public void createIndividuals() {
// nothing to do in the basic population case
}
@Override
public int size() {
return this.individuals.size();
}
protected void writeCsv(String outFileName, ResultSet results) {
LOG.debug("Writing population to CSV: {}", outFileName);
try {
CsvWriter writer = new CsvWriter(outFileName, ',', Charset.forName("UTF8"));
writer.writeRecord( new String[] {"label", "lat", "lon", "input", "output"} );
int i = 0;
int j = 0;
// using internal list rather than filtered iterator
for (Individual indiv : this.individuals) {
if ( ! this.skip[i]) {
String[] entries = new String[] {
indiv.label, Double.toString(indiv.lat), Double.toString(indiv.lon),
Double.toString(indiv.input), Double.toString(results.results[j])
};
writer.writeRecord(entries);
j++;
}
i++;
}
writer.close(); // flush writes and close
} catch (Exception e) {
LOG.error("Error while writing to CSV file: {}", e.getMessage());
return;
}
LOG.debug("Done writing population to CSV at {}.", outFileName);
}
@Override
public void writeAppropriateFormat(String outFileName, ResultSet results) {
// as a default, save to CSV. override this method in subclasses when more is known about data structure.
this.writeCsv(outFileName, results);
}
// TODO maybe store skip values in the samples themselves?
/**
* If a filter chain is specified, apply it to the individuals. Must be called after loading
* or generating the individuals. Filtering does not actually remove individuals from the
* population, it merely tags them as rejected. This is important for structured populations
* like rasters, where we may need to write out all individuals including those that were
* skipped.
*/
private void applyFilterChain() {
this.skip = new boolean[individuals.size()]; // initialized to false
if (filterChain == null) // no filter chain, do not reject any individuals
return;
for (IndividualFilter filter : filterChain) {
LOG.info("applying filter {}", filter);
int rejected = 0;
int i = 0;
for (Individual individual : this.individuals) {
boolean skipThis = ! filter.filter(individual);
if (skipThis)
rejected += 1;
skip[i++] |= skipThis;
}
LOG.info("accepted {} rejected {}", skip.length - rejected, rejected);
}
int rejected = 0;
for (boolean s : skip)
if (s)
rejected += 1;
LOG.info("TOTALS: accepted {} rejected {}", skip.length - rejected, rejected);
}
@Override
public void setup() {
// call the subclass-specific file loading method
this.createIndividuals();
// call the shared filter chain method
this.applyFilterChain();
}
class PopulationIterator implements Iterator<Individual> {
int i = 0;
int n = individuals.size();
Iterator<Individual> iter = individuals.iterator();
public boolean hasNext() {
while (i < n && skip[i]) {
//LOG.debug("in iter, i = {}", i);
if (! iter.hasNext())
return false;
i += 1;
iter.next();
}
//LOG.debug("done skipping at {}", i);
return iter.hasNext();
}
public Individual next() {
if (this.hasNext()) {
Individual ret = iter.next();
i += 1;
return ret;
} else {
throw new NoSuchElementException();
}
}
public void remove() {
throw new UnsupportedOperationException();
}
}
}