Hi,
I'm in the process of implementing a new vector isochrone polygon
generation tool and here are a description of an early version for
people that would be interested.
The current vector isochrone generator rely on building a convex hull
out of a delaunay triangulation of some vertex points from the
shortest-path tree graph. However, this implementation lack a few
features. I will be trying to address the following points:
a) support disconnected areas (the output of the isochrone should be a
set of polygons). This is important especially for transit isochrones
where the result is a highly fragmented area with small islands around
stations;
b) support holes. This is less important than a) but for eg. rivers or
large unaccessible areas, some polygons can have many holes;
c) no hard limit on the covered area. Real accessible area should be
covering an area nearby roads, with an accessibility function (off-road
walk speed, off-road max distance...);
d) output is "street-accessible area". There can be two definitions of
an isochrone (only considering area accessible on street, or accessible
even if you are still on the transit vehicle), for standard usage I
think the first definition make more sense;
e) be able to build several isochrones per request;
f) have a configurable and constant precision;
g) polygons within each isochrone should not intersect (also holes);
h) isochrones from different z values should not intersect.
Since some points would be rather hard or impossible to solve with the
current isochrone implementation, I made another one based on a rather
different algorithm, which try to cover all this points.
Broadly speaking, it uses the current analyst sample factory to get a
z=f(x,y) time (or whatever) function, and use an iterative grid sampling
with interpolation to determine the set of isochrone rings. It then
build a set of polygons (some with holes) out of it. See attached teaser
isochrone_1.jpg to see the results. The method in details:
1) Configure the isoline builder with a center point and a grid size
(unit) determining the resulting precision. Try were made with a 100m x
100m grid.
2) Seed the covered area by determining the set of 4x4 units "big-tiles"
that covers all the relevant point from the SPT. The covered area at
this stage can be non contiguous. See debug_1.jpg to see the seed points
and the resulting seed tiles set. This size (4x4) determine the
precision in small islands/holes detection.
3) Recursively split each cutting edges from the big tiles (a cutting
edge AB is an edge intersecting the z0 plane where z0 = isochrone time,
that is zA<z0<=zB or zA>=z0>zB), until the edge has length 1 unit.
4) Expand small cutting edges: for each cutting edge, explore the 2
adjacent tiles by looking at 6 other edges. Use the painter algorithm.
See debug_2.jpg to see the resulting small grid (initial edges and
cutting edges in red, isoline in blue).
5) Build polylines by walking around cutting eges. Handle tiles with 4
intersecting edges (saddle point) by an heuristics. Walk by keeping
always Z>z0 the same side. Determine exact position by interpolating
between edge end-point based on the z values. To better handle infinite
z values (which appear quite often), we further divide the edge to get a
better approximation. See grid_1.jpg.
6) Build polygons out of rings. Shells are CW polylines, holes are CCW
polylines. See isochrone_2.jpg for more details to see islands and
holes. Isochrones from different z values should never intersect.
Here is the first commit for those interested in the details:
https://github.com/laurentg/OpenTripPlanner-plannerstack/commit/922421ab41145eb52da71bb6eaebc7adf907eecc
I split the code in three sections (webservice+json conversion, SPT
computation, isoline computation). The isoline builder is in the
geometry package, as it does not rely on any OTP concept (it's a generic
z=f(x,y) isoline builder).
For an intermediate size city on a rather old laptop, the SPT takes
around 300ms to build and the 3 isolines around 500ms (result seen on
isochrone_1.jpg). Time is of course highly dependent on the covered area
and tile size, and a bit on the complexity of the isochrones.
Most of the time is took by the z sampling (by using a fast Z function
we decrease the time to ~20ms -- see linear_zfunc.jpg). The algorithm
try hard to minimize the number of sampling (using a coarse grid,
walking only when necessary, keeping sample values in cache...). With
properly defined and well-behaved z functions, the resulting output
looks quite good even by using a coarse grid (see again linear_zfunc).
The remaining issues are:
1) properly handling infinite z values, which are not always easy to
work with as we rely on linear interpolation.
2) removing the current z sampling method. The current mechanism is not
optimal, as we rely on a spatial index to determine the vertex. This
takes time, and we have all we need at hand in the first place.
I will try to come up with a solution to this two problems, by using
directly z values from the SPT and it's geometry to seed a grid of
sampling points, handling off-road values with a decay function (dz,w) =
f(d) and a proximity "brush" mechanism, adding a w "probability/weight"
factor alongside z. Probably more info soon.
Does anybody would be interested in that implementation? Anyway feel
free if you have any comments.
--Laurent