The basic approach is:
- Create points on the street centerline (in the code below, I had already generated these)
- Split the exterior ring of the street polygon into 5 segments, so that for the median point, the nearest two segments will be on opposite sides of the street)
- Calculate the distance from each point to each segment, and for each point, calculate the width as the average of the smallest two distances
- Take the median across points, and multiply by 2
WITH frcs AS
(SELECT generate_series(0, 0.9, 0.2) AS frc1,
generate_series(0.2, 1, 0.2) AS frc2),
rings AS -- exterior ring of polygon, split into 5 substrings
(SELECT id, ST_LineSubstring(ST_ExteriorRing(geom), frc1, frc2) AS geom
FROM frcs, street_polygons)
SELECT id, percentile_cont(0.5) within group (order by avgdist) *2 AS width -- median distance * 2
FROM
(SELECT id, AVG(dist) avgdist -- average distance to the two closest segments of the polygon ring
FROM
(SELECT ROW_NUMBER() OVER (PARTITION BY id, ptid ORDER BY dist) AS r, *
FROM
(SELECT id, ptid, ST_Distance(pt.geom, rings.geom) dist
FROM rings
JOIN street_points pt USING (id)) t0) t1
WHERE r<3 -- limit to 2 closest segments
GROUP BY id, ptid) t2
GROUP BY id;
Thanks to @CL for the suggestion to compute the distance from a bunch of points on the medial axis to the street border. Since I already had the centerlines, it was more efficient to calculate the distance from points on the centerlines to the street border, rather than calculate the medial axis.
However, since the centerlines are not exact centerlines, the approach here is to average the distance to the nearest two segments on the street border (which, for the median point, will be on opposite sides of the street)