use of org.opentripplanner.analyst.request.SampleGridRenderer.WTWD in project OpenTripPlanner by opentripplanner.
the class TNPropagatedTimesStore method makeIsochroneForVertices.
/**
* This bypasses a bunch of TimeSurface conversion/copy steps we were going though and makes the isochrones directly.
* This assumes that the target indexes in this router/propagatedTimesStore are vertex indexes, not pointset indexes.
* Called three times on min/avg/max to create the three elements of a ResultEnvelope.
*/
private ResultSet makeIsochroneForVertices(int[] times) {
final int spacing = 5;
final int nMax = 24;
final int cutoffMinutes = 120;
final int offroadDistanceMeters = 250;
SparseMatrixZSampleGrid<WTWD> grid = makeSampleGridForVertices(times, offroadDistanceMeters);
long t0 = System.currentTimeMillis();
DelaunayIsolineBuilder<WTWD> isolineBuilder = new DelaunayIsolineBuilder<>(grid.delaunayTriangulate(), new WTWD.IsolineMetric());
List<IsochroneData> isoData = new ArrayList<IsochroneData>();
for (int minutes = spacing, n = 0; minutes <= cutoffMinutes && n < nMax; minutes += spacing, n++) {
int seconds = minutes * 60;
WTWD z0 = new WTWD();
z0.w = 1.0;
z0.wTime = seconds;
z0.d = offroadDistanceMeters;
IsochroneData isochrone = new IsochroneData(seconds, isolineBuilder.computeIsoline(z0));
isoData.add(isochrone);
}
long t1 = System.currentTimeMillis();
ResultSet resultSet = new ResultSet();
resultSet.isochrones = new IsochroneData[isoData.size()];
isoData.toArray(resultSet.isochrones);
LOG.debug("Computed {} isochrones in {} msec", isoData.size(), (int) (t1 - t0));
return resultSet;
}
use of org.opentripplanner.analyst.request.SampleGridRenderer.WTWD in project OpenTripPlanner by opentripplanner.
the class TimeGridWs method getTimeGridPng.
@GET
@Produces({ "image/png" })
public Response getTimeGridPng(@QueryParam("base64") @DefaultValue("false") boolean base64) throws Exception {
/* Fetch the Router for this request using server and routerId fields from superclass. */
Router router = otpServer.getRouter(routerId);
if (precisionMeters < 10)
throw new IllegalArgumentException("Too small precisionMeters: " + precisionMeters);
if (offRoadDistanceMeters < 10)
throw new IllegalArgumentException("Too small offRoadDistanceMeters: " + offRoadDistanceMeters);
// Build the request
RoutingRequest sptRequest = buildRequest();
SampleGridRequest tgRequest = new SampleGridRequest();
tgRequest.maxTimeSec = maxTimeSec;
tgRequest.precisionMeters = precisionMeters;
tgRequest.offRoadDistanceMeters = offRoadDistanceMeters;
if (coordinateOrigin != null)
tgRequest.coordinateOrigin = new GenericLocation(null, coordinateOrigin).getCoordinate();
// Get a sample grid
ZSampleGrid<WTWD> sampleGrid = router.sampleGridRenderer.getSampleGrid(tgRequest, sptRequest);
int cols = sampleGrid.getXMax() - sampleGrid.getXMin() + 1;
int rows = sampleGrid.getYMax() - sampleGrid.getYMin() + 1;
// Hard-coded to RGBA
int channels = 4;
// We force to 8 bits channel depth, some clients won't support more than 8
// (namely, HTML5 canvas...)
ImageInfo imgInfo = new ImageInfo(cols, rows, 8, true, false, false);
/**
* TODO: PNGJ allow for progressive (ie line-by-line) writing. Thus we could theorically
* prevent having to allocate a bit pixel array in the first place, but we would need a
* line-by-line iterator on the sample grid, which is currently not the case.
*/
int[][] rgba = new int[rows][cols * channels];
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PngWriter pw = new PngWriter(baos, imgInfo);
pw.getMetadata().setText(PngChunkTextVar.KEY_Software, "OTPA");
pw.getMetadata().setText(PngChunkTextVar.KEY_Creation_Time, new Date().toString());
pw.getMetadata().setText(PngChunkTextVar.KEY_Description, "Sample grid bitmap");
String gridCornerStr = String.format(Locale.US, "%.8f,%.8f", sampleGrid.getCenter().y + sampleGrid.getYMin() * sampleGrid.getCellSize().y, sampleGrid.getCenter().x + sampleGrid.getXMin() * sampleGrid.getCellSize().x);
String gridCellSzStr = String.format(Locale.US, "%.12f,%.12f", sampleGrid.getCellSize().y, sampleGrid.getCellSize().x);
String offRoadDistStr = String.format(Locale.US, "%d", offRoadDistanceMeters);
PngChunkTEXT gridCornerChunk = new PngChunkTEXT(imgInfo);
gridCornerChunk.setKeyVal(OTPA_GRID_CORNER, gridCornerStr);
pw.getChunksList().queue(gridCornerChunk);
PngChunkTEXT gridCellSzChunk = new PngChunkTEXT(imgInfo);
gridCellSzChunk.setKeyVal(OTPA_GRID_CELL_SIZE, gridCellSzStr);
pw.getChunksList().queue(gridCellSzChunk);
PngChunkTEXT offRoadDistChunk = new PngChunkTEXT(imgInfo);
offRoadDistChunk.setKeyVal(OTPA_OFFROAD_DIST, offRoadDistStr);
pw.getChunksList().queue(offRoadDistChunk);
double unit;
switch(zDataType) {
case TIME:
// 1:1sec, max 18h
unit = 1.0;
break;
case BOARDINGS:
// 1:0.001 boarding, max 65.5
unit = 1000.0;
break;
case WALK_DISTANCE:
// 1:0.1m, max 6.55km
unit = 10.0;
break;
default:
throw new IllegalArgumentException("Unsupported Z DataType.");
}
for (ZSamplePoint<WTWD> p : sampleGrid) {
WTWD z = p.getZ();
int row = p.getY() - sampleGrid.getYMin();
int col = p.getX() - sampleGrid.getXMin();
double zz;
switch(zDataType) {
case TIME:
zz = z.wTime / z.w;
break;
case BOARDINGS:
zz = z.wBoardings / z.w;
break;
case WALK_DISTANCE:
zz = z.wWalkDist / z.w;
break;
default:
throw new IllegalArgumentException("Unsupported Z DataType.");
}
int iz;
if (Double.isInfinite(zz)) {
iz = 65535;
} else {
iz = ImageLineHelper.clampTo_0_65535((int) Math.round(zz * unit));
if (iz == 65535)
// Clamp
iz = 65534;
}
// d is expressed as a percentage of grid size, max 255%.
// Sometimes d will be bigger than 2.55 x grid size,
// but this should not be too much important as we are off-bounds.
int id = ImageLineHelper.clampTo_0_255((int) Math.round(z.d / precisionMeters * 100));
int offset = col * channels;
// z low 8 bits
rgba[row][offset + 0] = (iz & 0xFF);
// z high 8 bits
rgba[row][offset + 1] = (iz >> 8);
// d
rgba[row][offset + 2] = id;
/*
* Keep the alpha channel at 255, otherwise the RGB channel will be downsampled on some
* rendering clients (namely, JS canvas).
*/
rgba[row][offset + 3] = 255;
}
for (int row = 0; row < rgba.length; row++) {
ImageLineInt iline = new ImageLineInt(imgInfo, rgba[row]);
pw.writeRow(iline, row);
}
pw.end();
// Disallow caching on client side
CacheControl cc = new CacheControl();
cc.setNoCache(true);
// Also put the meta-data in the HTML header (easier to read from JS)
byte[] data = baos.toByteArray();
if (base64) {
data = Base64.encodeBase64(data);
}
return Response.ok().cacheControl(cc).entity(data).header(OTPA_GRID_CORNER, gridCornerStr).header(OTPA_GRID_CELL_SIZE, gridCellSzStr).header(OTPA_OFFROAD_DIST, offRoadDistStr).build();
}
use of org.opentripplanner.analyst.request.SampleGridRenderer.WTWD in project OpenTripPlanner by opentripplanner.
the class SurfaceResource method getIsochronesAccumulative.
/**
* Use Laurent's accumulative grid sampler. Cutoffs in minutes.
* The grid and Delaunay triangulation are cached, so subsequent requests are very fast.
*
* @param spacing the number of minutes between isochrones
* @return a list of evenly-spaced isochrones up to the timesurface's cutoff point
*/
public static List<IsochroneData> getIsochronesAccumulative(TimeSurface surf, int spacing, int nMax) {
long t0 = System.currentTimeMillis();
if (surf.sampleGrid == null) {
// The sample grid was not built from the SPT; make a minimal one including only time from the vertices in this timesurface
surf.makeSampleGridWithoutSPT();
}
DelaunayIsolineBuilder<WTWD> isolineBuilder = new DelaunayIsolineBuilder<WTWD>(surf.sampleGrid.delaunayTriangulate(), new WTWD.IsolineMetric());
List<IsochroneData> isochrones = new ArrayList<IsochroneData>();
for (int minutes = spacing, n = 0; minutes <= surf.cutoffMinutes && n < nMax; minutes += spacing, n++) {
int seconds = minutes * 60;
WTWD z0 = new WTWD();
z0.w = 1.0;
z0.wTime = seconds;
// meters. TODO set dynamically / properly, make sure it matches grid cell size?
z0.d = 300;
IsochroneData isochrone = new IsochroneData(seconds, isolineBuilder.computeIsoline(z0));
isochrones.add(isochrone);
}
long t1 = System.currentTimeMillis();
LOG.debug("Computed {} isochrones in {} msec", isochrones.size(), (int) (t1 - t0));
return isochrones;
}
use of org.opentripplanner.analyst.request.SampleGridRenderer.WTWD in project OpenTripPlanner by opentripplanner.
the class TimeSurface method makeSampleGridWithoutSPT.
/**
* Create the SampleGrid from whatever values are already in the TimeSurface, rather than looking at the SPT.
* This is not really ideal since it includes only intersection nodes, and no points along the road segments.
*/
public void makeSampleGridWithoutSPT() {
long t0 = System.currentTimeMillis();
// Todo: set dynamically and make sure this matches isoline builder params
final double gridSizeMeters = 300;
// Off-road max distance MUST be APPROX EQUALS to the grid precision
// TODO: Loosen this restriction (by adding more closing sample).
// Change the 0.8 magic factor here with caution.
// offroad walk distance roughly grid size
final double D0 = 0.8 * gridSizeMeters;
// off-road walk speed in m/sec
final double V0 = 1.00;
Coordinate coordinateOrigin = new Coordinate();
final double cosLat = FastMath.cos(toRadians(coordinateOrigin.y));
double dY = Math.toDegrees(gridSizeMeters / SphericalDistanceLibrary.RADIUS_OF_EARTH_IN_M);
double dX = dY / cosLat;
sampleGrid = new SparseMatrixZSampleGrid<WTWD>(16, this.times.size(), dX, dY, coordinateOrigin);
AccumulativeGridSampler.AccumulativeMetric<WTWD> metric = new SampleGridRenderer.WTWDAccumulativeMetric(cosLat, D0, V0, gridSizeMeters);
AccumulativeGridSampler<WTWD> sampler = new AccumulativeGridSampler<WTWD>(sampleGrid, metric);
// TODO propagation along street geometries could happen at this stage, rather than when the SPT is still available.
for (TObjectIntIterator<Vertex> iter = times.iterator(); iter.hasNext(); ) {
iter.advance();
Vertex vertex = iter.key();
int time = iter.value();
WTWD z = new WTWD();
z.w = 1.0;
z.d = 0.0;
z.wTime = time;
// unused
z.wBoardings = 0;
// unused
z.wWalkDist = 0;
sampler.addSamplingPoint(vertex.getCoordinate(), z, V0);
}
sampler.close();
long t1 = System.currentTimeMillis();
LOG.info("Made scalar SampleGrid from TimeSurface in {} msec.", (int) (t1 - t0));
}
use of org.opentripplanner.analyst.request.SampleGridRenderer.WTWD in project OpenTripPlanner by opentripplanner.
the class PropagatedTimesStore method makeIsochroneForVertices.
/**
* This bypasses a bunch of TimeSurface conversion/copy steps we were going though and makes the isochrones directly.
* This assumes that the target indexes in this router/propagatedTimesStore are vertex indexes, not pointset indexes.
* Called three times on min/avg/max to create the three elements of a ResultEnvelope.
*/
private ResultSet makeIsochroneForVertices(int[] times) {
final int spacing = 5;
final int nMax = 24;
final int cutoffMinutes = 120;
final double gridSize = IsochroneGenerator.GRID_SIZE_METERS;
final double offroadDistanceMeters = gridSize * IsochroneGenerator.WALK_DISTANCE_GRID_SIZE_RATIO;
SparseMatrixZSampleGrid<WTWD> grid = makeSampleGridForVertices(times, gridSize);
long t0 = System.currentTimeMillis();
DelaunayIsolineBuilder<WTWD> isolineBuilder = new DelaunayIsolineBuilder<>(grid.delaunayTriangulate(), new WTWD.IsolineMetric());
List<IsochroneData> isoData = new ArrayList<IsochroneData>();
for (int minutes = spacing, n = 0; minutes <= cutoffMinutes && n < nMax; minutes += spacing, n++) {
int seconds = minutes * 60;
WTWD z0 = new WTWD();
z0.w = 1.0;
z0.wTime = seconds;
z0.d = offroadDistanceMeters;
IsochroneData isochrone = new IsochroneData(seconds, isolineBuilder.computeIsoline(z0));
isoData.add(isochrone);
}
long t1 = System.currentTimeMillis();
ResultSet resultSet = new ResultSet();
resultSet.isochrones = new IsochroneData[isoData.size()];
isoData.toArray(resultSet.isochrones);
LOG.debug("Computed {} isochrones in {} msec", isoData.size(), (int) (t1 - t0));
return resultSet;
}
Aggregations