8 votos

¿Existe una función inversa de st_buffer que devuelva una estimación de la anchura?

Cuando se suministra una geometría g1 y un ancho w a la función estándar OGC ST_Buffer , devuelve una nueva geometría g2 ,

  g2 = ST_Buffer(g1,w)

Esta pregunta es sobre la inversa: una función en la que se pueden suministrar dos geometrías relacionadas - g1 y g2 satisface ST_Within(g1,g2) - y devuelve el parámetro de anchura,

  w = Unbuffer(g1,g2)

Ahora la interpretación de w es la "anchura media". Así que, dónde está esta función Unbuffer ¿se define? ¿Existen referencias para ello? ¿Existe una biblioteca estándar para ello?


Complemento después de la recompensa.

Ejemplos de aplicaciones (estimación de): ancho de la calle , ancho de la acera La anchura del río, la anchura del canal, la semblanza del polígono interno, la anchura de la zona de amortiguación de la vegetación ribereña, etc.

Ejemplo de propuestas de fuentes creíbles: Pavelsky y Smith 2008 ; ArcMap script Procedimiento . Son no soluciones aquí porque son (muy) consumidoras de CPU, "especializadas en ríos", y no puede ser implementado por las normas OGC . Calculan la media de ancho del río por cada una de las muchas líneas perpendiculares (de una línea central); por tanto, son algoritmos de "fuerza bruta".

Ejemplo de propuesta sin fuentes creíbles y/u oficiales: solucionador de problemas aquí . En este caso, se puede obtener la recompensa de encontrar fuentes que hagan creíble la solución publicada.

2voto

sashkello Puntos 325

No soy un experto en Postgis, pero creo que esto podría funcionar:

ST_Distance(ST_Boundary(g2),g1)

El ST_Boundary transformará su polígono en una cadena de líneas, Si g2 fue el resultado de un buffer de distancia X de g1, entonces todos los vértices de la cadena de líneas resultante deben estar a la misma distancia X de g1, y ST_Distancia (distancia mínima) le dará el resultado deseado.

NOTA: Esto no funcionará con los buffers generados a partir de ST_Buffer con join=benvel o encap=flat.

Resolver el problema de la benvela y el piso. Aquí mis dos centavos, usted podría transformar el g1 linestring o polígono a los n segmentos de línea (Ls) que lo componen con esto responder , calculan para cada uno de ellos la distancia de sus puntos medios a g2 con:

ST_Distance(ST_Boundary(g2),line_interpolate_point(Ls,0.5)) 

Y, por último, calcular el Modo de todas esas distancias.

Suponiendo que tu geometría g1 no sea demasiado compleja, el modo te dará la anchura exacta del buffer original, mientras que la media dará siempre una aproximación.

0voto

Iznogood Puntos 143

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;

0voto

kwutchak Puntos 232

Puede intentar obtener los vértices del polígono (resultado del buffer) , utilizando los vértices como entrada para polígonos de voronoi En el caso de los polígonos, se dibuja la línea media entre los vértices, y luego se intenta extraer la línea media, borrando las líneas que tocan el contorno (resultado del buffer).

ojalá pueda ayudar

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X