Identificando cruzamentos de estradas usando o PostGIS

17

Estou tentando identificar onde as estradas se cruzam e fazer um ponto nesse cruzamento, com o número de estradas que formam o cruzamento listado.

insira a descrição da imagem aqui

Fiquei me perguntando se havia alguma maneira de usar ST_NumPoints para conseguir isso, mas não consigo entender o que deveria estar fazendo. O que fiz foi criar uma tabela de pontos em que as linhas se cruzam usando o seguinte código:

CREATE TABLE test_points as
SELECT      
    ST_Intersection(a.geom, b.geom),
    a.gid
FROM
    roads as a,
    roads as b
WHERE
    ST_Touches(a.geom, b.geom);

Se eu executar isso em uma amostra de estradas, recebo a seguinte grade de pontos (as estradas são mostradas para ilustração):

insira a descrição da imagem aqui

Se eu inspecionar um dos pontos, vejo que há muitos pontos empilhados um sobre o outro:

insira a descrição da imagem aqui

O GID aqui é o ID da estrada, mas não entendo por que existem muitos pontos. Eu posso entender 4 pontos sendo contados para um cruzamento de estrada central, mas há 12 pontos listados aqui. Existe uma maneira melhor de realizar esse cálculo no PostGIS?

djq
fonte

Respostas:

21

Se você agrupar, você deve obter apenas pontos únicos.

CREATE TABLE test_points as
SELECT      
    ST_Intersection(a.geom, b.geom),
    Count(Distinct a.gid)
FROM
    roads as a,
    roads as b
WHERE
    ST_Touches(a.geom, b.geom)
    AND a.gid != b.gid
GROUP BY
    ST_Intersection(a.geom, b.geom)
;
underdark
fonte
Apenas um aviso, agrupando por geometria, resulta no agrupamento pela bbox da geometria, não pela própria geometria. Isso não importa quando se lida com pontos. Bem, quase. Bboxes tem menos precisão do que o próprio ponto que, em teoria, pode levar ao agrupamento de dois pontos que não são idênticos.
Nicklas Avén
Obrigado @ NicklasAvén. Qual a precisão da comparação da Bbox? Eu esperaria que fosse suficiente para este caso de uso.
underdark
1
Obrigado @underdark. Você sabe como posso contar o número de linhas que se cruzam? Eu tentei algumas combinações de COUNT()como COUNT(ST_Touches(..))e, COUNT(ST_Intersection(..))mas isso não parece funcionar como todos os valores 12.
DJQ
@underdark, sim, é absolutamente suficiente, é por isso que escrevi "em teoria". A caixa está em float4 e as coordenadas do ponto estão em dupla precisão. Portanto, a caixa terá a mesma aparência para ST_Point (1.000001,1.0) e ST_Point (1.000002,1.0) (pelo menos no meu sistema, eu apenas tentei. Ele agrupa os pontos juntos). Essa diferença entre a caixa e a geometria real tem sido um problema durante algum tempo na lista de desenvolvedores.
Nicklas Avén
Consulte @AlexOs modificação sugerida gis.stackexchange.com/a/151277/3195
Martin F
6

Isso é um pouco mais complicado do que você imagina. Isso ocorre porque não há uma boa maneira de analisar relações para mais do que pares. Você não pode colocar três linhas em uma função e perguntar se todas elas se cruzam.

Porém, pelo menos uma abordagem poderia ser encontrar primeiro os cruzamentos e depois verificar quantas estradas estão tocando em cada cruzamento (tudo isso pode ser feito na mesma consulta).

Se suas estradas se conectam perfeitamente umas às outras e não há estradas passando por uma passagem, você pode fazer algo assim (não testado):
editado com a cláusula de grupo esquecida (ainda não testada):

SELECT distinct_crosspoints.geom as crossing, array_agg(roads.gid), count(*) FROM
  (SELECT DISTINCT (geom) geom FROM 
    (SELECT ST_Intersection(a.geom, b.geom) geom 
     FROM roads a, roads b 
     WHERE ST_Intersects(a.geom, b.geom)
    ) all_crosspoints
   ) distinct_crosspoints
   ,roads 
 WHERE ST_Intersects(distinct_crosspoints.geom, roads.geom)
 GROUP BY distinct_crosspoints.geom;

Se as estradas não estiverem conectadas adequadamente e / ou algumas passarem por uma passagem, é mais complicado.

HTH

Nicklas

Nicklas Avén
fonte
Olá @Nicklas, não consigo fazer isso funcionar. As duas cláusulas internas funcionam bem; devo substituir o distinct_crosspoints ,roadsnome da minha tabela ( roads_test)? Eu tentei isso, mas depois recebi um erro sobre geomser ambíguo.
DJQ
1
@celenius, desculpe por ter esquecido a cláusula de grupo. Também vejo que você não precisa colocar distintas em um nível extra. Você pode simplesmente colocá-lo no cruzamento diretamente. Observe que Distinct tem o mesmo comportamento que o grupo, de acordo com a discussão em resposta de underdarks.
Nicklas Avén
Adicionei o distinct_crosspoints.geom à resposta de Nicklas para obter a consulta em execução. Funciona agora para mim.
23413 Frank
1
 CREATE TABLE test_points as
    SELECT      
        ST_Intersection(a.geom, b.geom),
        Count(Distinct a.gid)
    FROM
        roads as a,
        roads as b
    WHERE
        ST_Touches(a.geom, b.geom)
        AND a.gid < b.gid   /* !!! Changed "!=" for "<"  */
    GROUP BY
        ST_Intersection(a.geom, b.geom)
    ;

Se a linha A (id 1) cruza a linha B (id 2), é um ponto de interseção que precisamos. Mas a linha B também cruza a linha A no mesmo ponto. Mas não precisamos desse ponto duas vezes. É por isso que estou usando, em a.gid < b.gid vez dea.gid != b.gid

Alex Os
fonte