Después de preguntar, discutir, pagar la recompensa, y publicar otras variantes de la misma pregunta... He decidido construir mi solución. No es tan genérica como quiero, pero estoy trabajando... Hay 3 partes, que corresponden a los 3 tipos alternativos de g1
entradas: punto, línea o polígono.
Solución para el buffer de puntos
Desde mi pregunta , más o menos la anchura de la playa de un pequeño polígono de la isla.
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;
Solución para el búfer de línea
Desde Pregunta de @celenius , más o menos la anchura media de una calle.
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;
Solución para el buffer poligonal
Desde mi primera pregunta sobre los topes sobre la estimación de la anchura media de una acera...
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;
Ponerlo todo junto
(... TRABAJANDO...)
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;
Sobrecargas:
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;
Prueba
-- 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;