Thursday 22 October 2015

buffer - Is there a st_buffer inverse function, that returns a width estimation?


When you supply a geometry g1 and a width w to the standard OGC function ST_Buffer, it returns a new geometry g2,


  g2 = ST_Buffer(g1,w)

This question is about the inverse: a function where you can supply two related geometries — g1 and g2 satisfies ST_Within(g1,g2) — and returns the width parameter,


  w = Unbuffer(g1,g2)

Now the interpretation of w is "average width". So, where is this function Unbuffer defined? Are there references for it? Is there a standard library for it?




Complementing after bounty.



Examples of applications (estimation of): street width, sidewalk width, river width, canal width, internal polygon semelhance, riparian vegetation buffer width, etc.


Example of proposals of credible sources: Pavelsky & Smith 2008 ; ArcMap Script Procedure. They are not solutions here because are (very) CPU consumer, "river-specialized", and can not be implemented by OGC standards. They calculate average of river widthes by each of many perpendicular (of a central line) lines; so, they are "brute force" algorithms.


Example of proposal without credible and/or official sources: troubleshooter here. In this case, you can get the bounty finding sources that makes credible the posted solution.



Answer



After ask, discuss, pay bounty, and post other variants of the same question... I decided to build my solution. It is not so generic as I want, but I am working... There are 3 parts, corresponding to the 3 alternative kinds of g1 inputs: point, line or polygon.



From my question, about the width of the beach of a little island polygon.


CREATE FUNCTION pointBuffer_width(g geometry, N integer DEFAULT 8) RETURNS float AS $$
-- Returns the value of W in the approximation "g ~ ST_Buffer(ST_Centroid(g),W,'quad_segs=N')"
-- where N>0 indicates the reference-shape, 1=square, 2=heptagon, ... circle.

--
SELECT sqrt( area/(0.5*n*sin(2*pi()/n)) )
FROM ( SELECT ST_Area($1) as area,
(CASE WHEN $2<=1 THEN 4 ELSE 4*$2 END)::float as n
) as t;
$$ LANGUAGE SQL immutable;


From @celenius question, about the average width of a street.


CREATE FUNCTION lineBuffer_width(

-- rectangular strip mean width estimator
p_len float, -- len of the central line of g
p_geom geometry, -- g
p_btype varchar DEFAULT 'endcap=flat' -- st_buffer() parameter
) RETURNS float AS $f$
DECLARE
w_half float;
w float;
BEGIN
w_half := 0.25*ST_Area(p_geom)/p_len;

w := 0.50*ST_Area( ST_Buffer(p_geom,-w_half,p_btype) )/(p_len-2.0*w_half);
RETURN w_half+w;
END
$f$ LANGUAGE plpgsql IMMUTABLE;


From my first question about buffers, about estimation of the average width of a sidewalk...


CREATE FUNCTION polyBuffer_width(geometry, geometry) RETURNS float AS $$
SELECT 2.0*(ST_Area($2)-ST_Area($1))/(st_perimeter($2)+st_perimeter($1));
$$ LANGUAGE SQL immutable;



(... WORKING ...)


CREATE or replace FUNCTION buffer_width(
-- Mean width estimator. Returns w_estim for approximates gbase~ST_Buffer(gref,w_estim)
gbase geometry, -- polygon that ST_Contains(gbase,gref)
gref geometry DEFAULT NULL, -- point or line or NULL.
len1 float DEFAULT 0.0, -- len gref, 0 if point, NULL if polygon.
N integer DEFAULT 8, -- buffer parameter, quad_segs=N.
bufferType varchar DEFAULT 'endcap=flat' -- st_buffer() parameter

) RETURNS float AS $f$
DECLARE
w_half float;
w float;
aux float;
greftype varchar;
BEGIN
greftype := geometrytype(gbase);
IF greftype LIKE '%LINE%' OR len1>0.0 THEN
IF gref is not NULL THEN

len1 = ST_Length(gref);
END IF;
w_half := 0.25*ST_Area(gbase)/len1;
w := 0.50*ST_Area( ST_Buffer(gbase,-w_half,bufferType) )/(len1-2.0*w_half);
RETURN w_half+w;
ELSEIF greftype LIKE '%POINT%' OR gref IS NULL THEN
aux = (CASE WHEN N<=1 THEN 4 ELSE 4*N END)::float;
RETURN sqrt( ST_Area(gbase)/(0.5*aux*sin(2.0*pi()/aux)) );
ELSEIF greftype LIKE '%POLY%' THEN
RETURN 2.0*(ST_Area(gbase)-ST_Area(gref))/(st_perimeter(gbase)+st_perimeter(gref));

ELSE
RETURN -1.0; -- ERROR FLAG.
END IF;
END
$f$ LANGUAGE plpgsql IMMUTABLE;

Overloads:


CREATE FUNCTION buffer_width(  -- façade for point
gbase geometry,
N integer

) RETURNS float AS $f$
SELECT buffer_width($1,NULL,0.0,$2,'');
$f$ LANGUAGE SQL IMMUTABLE;

CREATE FUNCTION buffer_width( -- façade for line
len1 float,
gbase geometry,
bufferType varchar DEFAULT 'endcap=flat'
) RETURNS float AS $f$
SELECT buffer_width($2,NULL,$1,8,$3);

$f$ LANGUAGE SQL IMMUTABLE;


-- TEST-KIT FOR BUFFER-POINT
SELECT w,
(array['square','octagon','','','','','','~circle'])[shape] as shape,
round(buffer_width(circle8,shape),1) as circle8,
round(buffer_width(octagon,shape),1) as octagon,
round(buffer_width(square,shape),1) as square
FROM (

SELECT
w, shape, -- 1, 2, 3 or 10
ST_Buffer(g, w) as circle8, -- default 8, use 20 for perfect circle
ST_Buffer(g, w, 'quad_segs=2') as octagon,
ST_Buffer(g, w, 'quad_segs=1') as square
FROM ( SELECT ST_GeomFromText('POINT(100 90)') as g,
unnest(array[1.0,150.0]) as w,
unnest(array[1,2,8]) as shape
) as t
) as t2;



-- TEST-KIT FOR BUFFER-LINE
SELECT *, round(100.0*(w_estim-w)/w,1) as estim_perc_error
FROM (
SELECT btype, round(len,1) AS len, w, round(w/len,3) AS wfactor,
round( buffer_width(len, gbase, btype) ,2) as w_estim ,
round( 0.5*ST_Area(gbase)/len ,2) as w_near
FROM (
SELECT

*, st_length(g) AS len, ST_Buffer(g, w, btype) AS gbase
FROM (
-- SELECT ST_GeomFromText('LINESTRING(50 50,150 150)') AS g, -- straight
SELECT ST_GeomFromText('LINESTRING(50 50,150 150,150 50,250 250)') AS g,
unnest(array[1.0,10.0,20.0,50.0]) AS w
) AS t,
(SELECT unnest(array['endcap=flat','endcap=flat join=bevel']) AS btype
) AS t2
) as t3
) as t4;

No comments:

Post a Comment

arcpy - Changing output name when exporting data driven pages to JPG?

Is there a way to save the output JPG, changing the output file name to the page name, instead of page number? I mean changing the script fo...