Aqui mostro pouca otimização sobre a solução @whuber, e estou colocando em termos de "largura do buffer", porque é útil para integrar a solução de um problema mais geral: Existe uma função inversa st_buffer, que retorna uma estimativa de largura?
CREATE FUNCTION buffer_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;
Para este problema, a questão @celenius sobre largura da rua , sw
a solução é
sw = buffer_width(ST_Length(g1), g2)
onde sw
é a "largura média", g1
a linha central de g2
e a rua g2
é um polígono . Usei apenas a biblioteca padrão OGC, testei com PostGIS e resolvi outras aplicações práticas sérias com a mesma função buffer_width.
DEMONSTRAÇÃO
A2
é a área de g2
, L1
o comprimento da linha central ( g1
) de g2
.
Supondo que possamos gerar g2
por g2=ST_Buffer(g1,w)
, e que g1
seja reto, g2
um retângulo com comprimento L1
e largura 2*w
, e
A2 = L1*(2*w) --> w = 0.5*A2/L1
Não é a mesma fórmula de @whuber, porque aqui w
está metade da g2
largura do retângulo ( ). É um bom estimador, mas, como podemos ver pelos testes (abaixo), não é exato, e a função o utiliza como uma pista para reduzir a g2
área e como um estimador final.
Aqui, não avaliamos buffers com "endcap = square" ou "endcap = round", que precisam da soma A2
de uma área de um buffer de ponto com o mesmo w
.
Referências: em um fórum semelhante de 2005 , W. Huber explica soluções semelhantes e outras.
ENSAIOS E RAZÕES
Para linhas retas, os resultados, como esperado, são exatos. Mas para outras geometrias, os resultados podem ser decepcionantes. A principal razão é que, talvez, todo o modelo seja para retângulos exatos ou para geometrias que possam ser aproximadas a um "retângulo de tira". Aqui está um "kit de teste" para verificar os limites dessa aproximação (veja wfactor
nos resultados acima).
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;
RESULTADOS:
COM RETÂNGULOS (a linha central é uma LINHA RETO):
btype | len | w | wfactor | w_estim | w_near | estim_perc_error
------------------------+-------+------+---------+---------+--------+------------------
endcap=flat | 141.4 | 1.0 | 0.007 | 1 | 1 | 0
endcap=flat join=bevel | 141.4 | 1.0 | 0.007 | 1 | 1 | 0
endcap=flat | 141.4 | 10.0 | 0.071 | 10 | 10 | 0
endcap=flat join=bevel | 141.4 | 10.0 | 0.071 | 10 | 10 | 0
endcap=flat | 141.4 | 20.0 | 0.141 | 20 | 20 | 0
endcap=flat join=bevel | 141.4 | 20.0 | 0.141 | 20 | 20 | 0
endcap=flat | 141.4 | 50.0 | 0.354 | 50 | 50 | 0
endcap=flat join=bevel | 141.4 | 50.0 | 0.354 | 50 | 50 | 0
COM OUTRAS GEOMETRIAS (linha central dobrada):
btype | len | w | wfactor | w_estim | w_near | estim_perc_error
-----------------------+-----+------+---------+---------+--------+------------------
endcap=flat | 465 | 1.0 | 0.002 | 1 | 1 | 0
endcap=flat join=bevel | 465 | 1.0 | 0.002 | 1 | 0.99 | 0
endcap=flat | 465 | 10.0 | 0.022 | 9.98 | 9.55 | -0.2
endcap=flat join=bevel | 465 | 10.0 | 0.022 | 9.88 | 9.35 | -1.2
endcap=flat | 465 | 20.0 | 0.043 | 19.83 | 18.22 | -0.9
endcap=flat join=bevel | 465 | 20.0 | 0.043 | 19.33 | 17.39 | -3.4
endcap=flat | 465 | 50.0 | 0.108 | 46.29 | 40.47 | -7.4
endcap=flat join=bevel | 465 | 50.0 | 0.108 | 41.76 | 36.65 | -16.5
wfactor= w/len
w_near = 0.5*area/len
w_estim is the proposed estimator, the buffer_width function.
Sobre, btype
consulte o guia ST_Buffer , com boas ilustrações e os LINESTRINGs usados aqui.
CONCLUSÕES :
- o estimador de
w_estim
é sempre melhor que w_near
;
- para
g2
geometrias "quase retangulares" , tudo bem, qualquerwfactor
- para outras geometrias (próximo a "faixas retangulares"), use o limite
wfactor=~0.01
para 1% de erro em w_estim
. Até esse fator, use outro estimador.
Cuidado e prevenção
Por que o erro de estimativa ocorre? Quando você usa ST_Buffer(g,w)
, espera, pelo "modelo de faixa retangular", que a nova área adicionada pelo buffer de largura w
seja aproximadamente w*ST_Length(g)
ou w*ST_Perimeter(g)
... Quando não, geralmente por sobreposições (consulte linhas dobradas) ou "estilo", é quando a estimativa da w
falha média . Esta é a principal mensagem dos testes.
Para detectar esse problema em qualquer rei do buffer , verifique o comportamento da geração do buffer:
SELECT btype, w, round(100.0*(a1-len1*2.0*w)/a1)::varchar||'%' AS straight_error,
round(100.0*(a2-len2*2.0*w)/a2)::varchar||'%' AS curve2_error,
round(100.0*(a3-len3*2.0*w)/a3)::varchar||'%' AS curve3_error
FROM (
SELECT
*, st_length(g1) AS len1, ST_Area(ST_Buffer(g1, w, btype)) AS a1,
st_length(g2) AS len2, ST_Area(ST_Buffer(g2, w, btype)) AS a2,
st_length(g3) AS len3, ST_Area(ST_Buffer(g3, w, btype)) AS a3
FROM (
SELECT ST_GeomFromText('LINESTRING(50 50,150 150)') AS g1, -- straight
ST_GeomFromText('LINESTRING(50 50,150 150,150 50)') AS g2,
ST_GeomFromText('LINESTRING(50 50,150 150,150 50,250 250)') AS g3,
unnest(array[1.0,20.0,50.0]) AS w
) AS t,
(SELECT unnest(array['endcap=flat','endcap=flat join=bevel']) AS btype
) AS t2
) as t3;
RESULTADOS:
btype | w | straight_error | curve2_error | curve3_error
------------------------+------+----------------+--------------+--------------
endcap=flat | 1.0 | 0% | -0% | -0%
endcap=flat join=bevel | 1.0 | 0% | -0% | -1%
endcap=flat | 20.0 | 0% | -5% | -10%
endcap=flat join=bevel | 20.0 | 0% | -9% | -15%
endcap=flat | 50.0 | 0% | -14% | -24%
endcap=flat join=bevel | 50.0 | 0% | -26% | -36%