All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opentripplanner.profile.IsochroneGenerator Maven / Gradle / Ivy

There is a newer version: 2.6.0
Show newest version
package org.opentripplanner.profile;

import com.vividsolutions.jts.geom.Coordinate;
import org.apache.commons.math3.util.FastMath;
import org.opentripplanner.analyst.PointSet;
import org.opentripplanner.analyst.core.IsochroneData;
import org.opentripplanner.analyst.request.SampleGridRenderer;
import org.opentripplanner.common.geometry.AccumulativeGridSampler;
import org.opentripplanner.common.geometry.DelaunayIsolineBuilder;
import org.opentripplanner.common.geometry.SparseMatrixZSampleGrid;
import org.opentripplanner.common.geometry.SphericalDistanceLibrary;
import org.opentripplanner.common.geometry.ZSampleGrid;
import org.opentripplanner.analyst.request.SampleGridRenderer.WTWD;

import java.util.ArrayList;
import java.util.List;

import static org.apache.commons.math3.util.FastMath.toRadians;

/**
 * This class contains a couple of utility methods for making Isochrones when all you have is a PointSet and times to
 * reach that PointSet. Eventually we may want to just define a standard grid PointSet for each study area in such a way
 * that all streets within each grid cell contribute to its travel time value, then use that grid directly when making
 * isolines. This would allow us to drop the separate grid accumulation step.
 *
 * The reason why we need this functionality is that we are now producing travel time stats directly for pointsets as targets
 * of a repeated RAPTOR search, without saving accompanying travel time values for the street vertices along the way.
 */
public abstract class IsochroneGenerator {

    public static final double GRID_SIZE_METERS = 300;

    // Off-road max distance MUST be APPROX EQUALS to the grid precision
    // TODO: Loosen this restriction (by adding more closing samples).
    // Change the 0.8 magic factor here with caution.
    public static final double WALK_DISTANCE_GRID_SIZE_RATIO = 0.8;

    /**
     * Make a ZSampleGrid from a PointSet and a parallel array of travel times for that PointSet.
     * Those times could come from a ResultSetWithTimes or directly from a PropagatedTimesStore, which has one
     * such array for each of min, avg, and max travel time over the departure time window it represents.
     * If your PointSet is dense enough (e.g. every block in a city) then this should yield a decent surface and
     * isochrones.
     * FIXME code duplication, this is ripped off from TimeSurface and should probably replace the version there as it is more generic.
     * @param walkSpeed the walk speed in meters per second
     * @return a grid suitable for making isochrones, based on an arbitrary PointSet and times to reach all those points.
     */
    public static ZSampleGrid makeGrid (PointSet pointSet, int[] times, double walkSpeed) {
        final double D0 = WALK_DISTANCE_GRID_SIZE_RATIO * GRID_SIZE_METERS; // offroad walk distance roughly grid size
        final double V0 = walkSpeed; // off-road walk speed in m/sec
        Coordinate coordinateOrigin = pointSet.getCoordinate(0); // Use the first feature as the center of the projection
        final double cosLat = FastMath.cos(toRadians(coordinateOrigin.y));
        double dY = Math.toDegrees(GRID_SIZE_METERS / SphericalDistanceLibrary.RADIUS_OF_EARTH_IN_M);
        double dX = dY / cosLat;
        ZSampleGrid grid = new SparseMatrixZSampleGrid(16, times.length, dX, dY, coordinateOrigin);
        AccumulativeGridSampler.AccumulativeMetric metric =
                new SampleGridRenderer.WTWDAccumulativeMetric(cosLat, D0, V0, GRID_SIZE_METERS);
        AccumulativeGridSampler sampler = new AccumulativeGridSampler<>(grid, metric);

        // Iterate over every point in this PointSet, adding it to the ZSampleGrid
        for (int p = 0; p < times.length; p++) {
            int time = times[p];
            WTWD z = new WTWD();
            z.w = 1.0;
            z.d = 0.0;
            z.wTime = time;
            z.wBoardings = 0; // unused
            z.wWalkDist = 0; // unused
            sampler.addSamplingPoint(pointSet.getCoordinate(p), z, V0);
        }
        sampler.close();

        return grid;
    }


    /**
     * Make isochrones from a grid. This more general function should probably be reused by SurfaceResource.
     * FIXME code duplication: function ripped off from SurfaceResource
     * @param spacingMinutes the number of minutes between isochrones
     * @return a list of evenly-spaced isochrones
     */
    public static List getIsochronesAccumulative(ZSampleGrid grid,
                                                          int spacingMinutes, int cutoffMinutes, int nMax) {

        DelaunayIsolineBuilder isolineBuilder = new DelaunayIsolineBuilder<>(
                grid.delaunayTriangulate(), new WTWD.IsolineMetric());

        List isochrones = new ArrayList<>();
        for (int minutes = spacingMinutes, n = 0; minutes <= cutoffMinutes && n < nMax; minutes += spacingMinutes, n++) {
            int seconds = minutes * 60;
            SampleGridRenderer.WTWD z0 = new SampleGridRenderer.WTWD();
            z0.w = 1.0;
            z0.wTime = seconds;
            z0.d = GRID_SIZE_METERS;
            IsochroneData isochrone = new IsochroneData(seconds, isolineBuilder.computeIsoline(z0));
            isochrones.add(isochrone);
            if (n + 1 >= nMax) {
                break;
            }
        }

        return isochrones;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy