
org.opentripplanner.analyst.ResultSet Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of otp Show documentation
Show all versions of otp Show documentation
The OpenTripPlanner multimodal journey planning system
package org.opentripplanner.analyst;
import com.beust.jcommander.internal.Maps;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.geotools.feature.FeatureCollection;
import org.geotools.geojson.feature.FeatureJSON;
import org.opentripplanner.analyst.core.IsochroneData;
import org.opentripplanner.api.resource.LIsochrone;
import org.opentripplanner.api.resource.SurfaceResource;
import org.opentripplanner.common.geometry.ZSampleGrid;
import org.opentripplanner.profile.IsochroneGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* This holds the results of a one-to-many search from a single origin point to a whole set of destination points.
* This may be either a profile search (capturing variation over a time window of several hours) or a "classic" search
* at a single departure time. Those results are expressed as histograms: bins representing how many destinations you
* can reach after M minutes of travel. For large point sets (large numbers of origins and destinations), this is
* significantly more compact than a full origin-destination travel time matrix. It makes the total size of the results
* linear in the number of origins, rather than quadratic.
*
* Optionally, this can also carry travel times to every point in the target pointset and/or a series vector
* isochrones around the origin point.
*/
public class ResultSet implements Serializable{
private static final long serialVersionUID = -6723127825189535112L;
private static final Logger LOG = LoggerFactory.getLogger(ResultSet.class);
/** An identifier consisting of the ids for the pointset and time surface that were combined. */
public String id;
/** One histogram for each category of destination points in the target pointset. */
public Map histograms = Maps.newHashMap();
// FIXME aren't the histogram.counts identical for all these histograms?
/** Times to reach every feature, may be null */
public int[] times;
/** Isochrone geometries around the origin, may be null. */
public IsochroneData[] isochrones;
public ResultSet() {
}
/** Build a new ResultSet by evaluating the given TimeSurface at all the given sample points, not including times. */
public ResultSet(SampleSet samples, TimeSurface surface){
this(samples, surface, false, false);
}
/** Build a new ResultSet by evaluating the given TimeSurface at all the given sample points, optionally including times. */
public ResultSet(SampleSet samples, TimeSurface surface, boolean includeTimes, boolean includeIsochrones){
id = samples.pset.id + "_" + surface.id;
PointSet targets = samples.pset;
// Evaluate the surface at all points in the pointset
int[] times = samples.eval(surface);
buildHistograms(times, targets);
if (includeTimes)
this.times = times;
if (includeIsochrones)
buildIsochrones(surface);
}
private void buildIsochrones(TimeSurface surface) {
List id = SurfaceResource.getIsochronesAccumulative(surface, 5, 24);
this.isochrones = new IsochroneData[id.size()];
id.toArray(this.isochrones);
}
private void buildIsochrones(int[] times, PointSet targets) {
ZSampleGrid zs = IsochroneGenerator.makeGrid(targets, times, 1.3);
List id = IsochroneGenerator.getIsochronesAccumulative(zs, 5, 120, 24);
this.isochrones = new IsochroneData[id.size()];
id.toArray(this.isochrones);
}
/**
* Build a new ResultSet that contains only isochrones, built by accumulating the times at all street vertices
* into a regular grid without an intermediate pointSet.
*/
public ResultSet (TimeSurface surface) {
buildIsochrones(surface);
}
/** Build a new ResultSet directly from times at point features, optionally including histograms or interpolating isochrones */
public ResultSet(int[] times, PointSet targets, boolean includeTimes, boolean includeHistograms, boolean includeIsochrones) {
if (includeTimes)
this.times = times;
if (includeHistograms)
buildHistograms(times, targets);
if (includeIsochrones)
buildIsochrones(times, targets);
}
/**
* Given an array of travel times to reach each point in the supplied pointset, make a histogram of
* travel times to reach each separate category of points (i.e. "property") within the pointset.
* Each new histogram object will be stored as a part of this result set keyed on its property/category.
*/
protected void buildHistograms(int[] times, PointSet targets) {
this.histograms = Histogram.buildAll(times, targets);
}
/**
* Sum the values of specified categories at all time limits within the
* bounds of the search. If no categories are specified, sum all categories.
*/
public long sum (String... categories) {
return sum((Integer) null, categories);
}
/**
* Sum the values of the specified categories up to the time limit specified
* (in seconds). If no categories are specified, sum all categories.
*/
public long sum(Integer timeLimit, String... categories) {
if (categories.length == 0)
categories = histograms.keySet().toArray(new String[histograms.keySet().size()]);
long value = 0l;
int maxMinutes;
if(timeLimit != null)
maxMinutes = timeLimit / 60;
else
maxMinutes = Integer.MAX_VALUE;
for(String k : categories) {
int minute = 0;
for(int v : histograms.get(k).sums) {
if(minute < maxMinutes)
value += v;
minute++;
}
}
return value;
}
/**
* Serialize this ResultSet to the given output stream as a JSON document, when the pointset is not available.
* TODO: explain why and when that would happen
*/
public void writeJson(OutputStream output) {
writeJson(output, null);
}
/**
* Serialize this ResultSet to the given output stream as a JSON document.
* properties: a list of the names of all the pointSet properties for which we have histograms.
* data: for each property, a histogram of arrival times.
*/
public void writeJson(OutputStream output, PointSet ps) {
try {
JsonFactory jsonFactory = new JsonFactory();
JsonGenerator jgen = jsonFactory.createGenerator(output);
jgen.setCodec(new ObjectMapper());
jgen.writeStartObject(); {
if(ps == null) {
jgen.writeObjectFieldStart("properties"); {
if (id != null)
jgen.writeStringField("id", id);
}
jgen.writeEndObject();
}
else {
ps.writeJsonProperties(jgen);
}
jgen.writeObjectFieldStart("data"); {
for(String propertyId : histograms.keySet()) {
jgen.writeObjectFieldStart(propertyId); {
histograms.get(propertyId).writeJson(jgen);
}
jgen.writeEndObject();
}
}
jgen.writeEndObject();
if (times != null) {
jgen.writeArrayFieldStart("times");
for (int t : times) jgen.writeNumber(t);
jgen.writeEndArray();
}
}
jgen.writeEndObject();
jgen.close();
} catch (IOException ioex) {
LOG.info("IOException, connection may have been closed while streaming JSON.");
}
}
/** Write the isochrones as GeoJSON */
public void writeIsochrones(JsonGenerator jgen) throws IOException {
if (this.isochrones == null)
return;
FeatureJSON fj = new FeatureJSON();
FeatureCollection fc = LIsochrone.makeContourFeatures(Arrays.asList(isochrones));
StringWriter sw = new StringWriter();
fj.writeFeatureCollection(fc, sw);
// TODO cludge
String json = sw.toString();
jgen.writeRaw(json.substring(1, json.length() - 1));
}
/** A set of result sets from profile routing: min, avg, max */;
public static class RangeSet implements Serializable {
public static final long serialVersionUID = 1L;
public ResultSet min;
public ResultSet avg;
public ResultSet max;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy