Como calcular o ângulo no qual duas linhas se cruzam no PostGIS?

19

Eu quero calcular o ângulo entre duas linhas onde elas se cruzam no PostGIS.

O ponto de partida para cálculos de ângulo no PostGIS parece ser ST_Azimuth - mas isso leva pontos como entrada. Meu primeiro pensamento foi pegar os pontos finais das linhas que se cruzam e realizar um cálculo de azimute sobre elas. Isso não é bom o suficiente, porque a maioria dos recursos da linha não é reta e eu estou interessado no ângulo na interseção. Então, o que eu criei é uma operação aninhada que executa as seguintes etapas:

  1. Identifique todas as interseções entre as duas tabelas de recursos de linha.
  2. Crie um buffer muito pequeno ao redor do ponto de interseção
  3. Identifique os pontos em que os recursos da linha cruzam o exterior do buffer (tomando o primeiro ponto se houver mais de um - estou realmente interessado apenas em saber se o ângulo está próximo de 0, 90 ou 180 graus)
  4. Calcule ST_Azimuth para esses dois pontos.

O SQL completo é demorado para postar aqui, mas eu o gist aqui se você estiver interessado. (A propósito, existe uma maneira melhor do que carregar todos os campos que estão descendo as instruções WITH?)

Os resultados não parecem corretos, então estou claramente fazendo algo errado:

exemplo de saída 1 exemplo de saída 2

EDIT Refiz os cálculos no EPSG: 3785 e os resultados são um pouco diferentes, mas ainda não estão corretos:

saída em 3785 # 1 saída em 3785 # 2

Minha pergunta é onde estão as falhas nesse processo. Estou entendendo mal o que ST_Azimuth faz? Existe um problema de CRS? Algo completamente diferente? Ou talvez haja uma maneira muito, muito mais simples de fazer isso?

mvexel
fonte
1
Qual era o CRS original? Os cálculos de ângulo devem ser feitos com uma projeção conforme - não com lat / long não projetado (SRID = 4326).
Mike T
Era EPSG: 4326 coordenadas originalmente, incluí o ST_Translate apenas para ter 100% de certeza de que todo o processamento seria feito no mesmo CRS. Vou tentar uma projeção conforme, obrigado.
Mvexel #
Refiz que os cálculos são EPSG: 3785 e isso faz diferença - vou alterar a pergunta para mostrar os novos resultados - mas o resultado ainda não reflete o ângulo real.
Mvexel

Respostas:

12

Eu tive a epifania. É bastante mundano. Eu estava deixando de fora uma informação essencial para o PostGIS calcular o ângulo certo.

O que eu estava calculando era o ângulo entre apenas os dois pontos que cruzavam o exterior do pequeno buffer. Para calcular o ângulo da interseção, preciso calcular os dois ângulos entre os dois pontos no exterior do buffer e o ponto de interseção dos dois recursos da linha e subtraí-los.

Atualizei o SQL completo , mas aqui está o ponto mais destacado:

SELECT
    ...
    abs
    (
        round
        (
            degrees
            (
            ST_Azimuth
            (
                points.point2,
                points.intersection
            )
            -
            ST_Azimuth
            (
                points.point1,
                points.intersection
            )
        )::decimal % 180.0
        ,2
    )
)
AS angle
...
FROM
points 
mvexel
fonte
1
Eu estava pensando sobre o ângulo do ponto de buffer na seção, mas não tenho tempo para entrar em detalhes. Outro aspecto são as unidades angulares. Você precisa multiplicar o resultado em radianos de ST_Azimuth por 180,0 / pi () para obter resultados em graus.
Mike T
Sim, obrigado, eu uso a função graus () do PostgreSQL para isso.
Mvexel
Cutelo. (Eu nem sabia que havia uma função de graus até agora.) Seria bom agrupar toda essa lógica em uma chamada de função, mas estou tendo dificuldade em conceituar como isso funcionaria, ou seja ST_IntersectionAngle(...?
Mike T
Fiquei realmente surpreso que não é uma função PostGIS. Obrigado pelo seu feedback sobre isso.
Mvexel #
2

Recentemente, tive que calcular a mesma coisa, mas decidi por uma abordagem mais simples e provavelmente mais rápida.

Para encontrar os pontos extras para o cálculo do azimute, basta verificar uma permíria do comprimento atrás da interseção (ou depois, no raro caso em que isso ocorre no início da linha) usando ST_Line_Locate_Point e ST_Line_Interpolate_Point :

abs(degrees( 
  ST_Azimuth (
    intersection, 
    ST_Line_Interpolate_Point(
      line1, 
      abs(ST_Line_Locate_Point(line1, intersection) - 0.0001)
    )
  )
  -
  ST_Azimuth (
    intersection, 
    ST_Line_Interpolate_Point(
      line2, 
      abs(ST_Line_Locate_Point(line2, intersection) - 0.0001)
    )
  )
))

A permíria foi arbitrária e, para resultados mais consistentes, seria melhor usar um deslocamento absoluto. Para, por exemplo, verificar 20m antes, você alteraria 0,0001 para 20/ST_Length(line1)e 20/ST_Length(line2)respectivamente.

lynxlynxlynx
fonte