"Mapas de rota" ponto a ponto curvados

39

Recentemente, estive pesquisando páginas da web de companhias aéreas que exibem suas rotas que partem de uma determinada cidade para todas as outras cidades que atendem. Eu gostaria de poder criar rotas curvas semelhantes entre pontos. Alguém criou scripts ou funções que irão gerar arcos curvos como os exibidos neste exemplo ?

Caminhos de Vôo

No PostGIS, existe uma implementação do ST_MakeLine que permita especificar a quantidade de curva a ser usada ao conectar 2 pontos?

Enquanto atualmente estou usando o PostGIS e o QGIS, gostaria de saber sobre outras opções de software que podem criar a mesma aparência.

RyanDalton
fonte
Alguém sabe de alguma implementação legal disso? Exemplos ou o que seja?
Mark Boulder

Respostas:

26

Criar grandes círculos pode dar o efeito desejado.

Talvez algo como discutido em http://lists.osgeo.org/pipermail/postgis-users/2008-February/018620.html

Atualizar:

Eu segui essa ideia em "Visualizando conexões globais" . É uma solução puramente baseada em PostGIS usando reprojeção para criar arcos.

SELECT ST_Transform(
  ST_Segmentize(
    ST_MakeLine(
      ST_Transform(a.the_geom, 953027),
      ST_Transform(b.the_geom, 953027)
    ), 
  100000), 
4326)

(A definição de CRS para 953027 pode ser encontrada aqui: http://spatialreference.org/ref/esri/53027/ )

insira a descrição da imagem aqui

underdark
fonte
4
Gosto da ideia, embora com grandes círculos, o problema que você encontra é que, a distâncias mais curtas, você ainda acabe com uma linha geralmente reta. Eu gostaria de poder controlar a quantidade de arco que coloquei na linha (ie- comprimento do arco = distância * 2).
perfil completo de RyanDalton
1
Aqui está um bom exemplo do problema com simplesmente usando grandes círculos: gc.kls2.com/cgi-bin/...
RyanDalton
1
Após algumas pesquisas adicionais, encontrei este post que pode ser útil para ajudar esse método. mail-archive.com/[email protected]/...
RyanDalton
Para uso futuro dos leitores, pensei em seguir em frente e vincular à recente postagem no blog do @ underdark que aborda este tópico. underdark.wordpress.com/2011/08/20/...
RyanDalton
Isso é ótimo!! Usado em meu projeto para desenhar linhas entre checkins de usuários e locais de local, pegou de Forsquare
Lorenzo Barbagli
24

O problema é descobrir quanto dobrar os arcos para melhorar sua resolução visual.

Aqui está uma solução (entre as muitas possíveis). Vamos considerar todos os arcos que emanam de uma origem comum. Os arcos ficam mais lotados aqui. Para separá-los da melhor forma, vamos organizá-los para que se espalhem em ângulos igualmente espaçados . É um problema se desenharmos segmentos de linha reta da origem aos destinos, porque normalmente haverá grupos de destinos em várias direções. Vamos usar nossa liberdade para dobrar os arcos, a fim de espaçar os ângulos de partida o mais uniformemente possível.

Para simplificar, vamos usar arcos circulares no mapa. Uma medida natural da "dobra" em um arco do ponto y ao ponto x é a diferença entre o seu rolamento em y e o rolamento diretamente de y a x . Tal arco é um setor de um círculo no qual y e x se encontram; geometria elementar mostra que o ângulo de flexão é igual à metade do ângulo incluído no arco.

Para descrever um algoritmo, precisamos de um pouco mais de notação. Seja y o ponto de origem (conforme projetado no mapa) e seja x_1 , x_2 , ..., x_n os pontos de destino. Defina a_i como o rolamento de y a x_i , i = 1, 2, ..., n .

Como passo preliminar, suponha que os rolamentos (todos entre 0 e 360 ​​graus) estejam em ordem crescente: isso exige que calculemos os rolamentos e depois os classifiquemos; ambos são tarefas diretas.

Idealmente, gostaríamos que os rolamentos dos arcos fossem iguais a 360 / n , 2 * 360 / n etc., em relação a algum rolamento inicial. As diferenças entre os rolamentos desejados e os rolamentos reais são, portanto, iguais a i * 360 / n - a_i mais o rolamento de partida, a0 . A maior diferença é o máximo dessas n diferenças e a menor diferença é o mínimo. Vamos definir a0 para estar a meio caminho entre o máximo e o mínimo; este é um bom candidato para o rolamento inicial, pois minimiza a quantidade máxima de flexão que ocorrerá . Conseqüentemente, defina

b_i = i * 360 / n - a0 - a_i:

essa é a curvatura a ser usada .

É uma questão de geometria elementar desenhar um arco circular de y a x que subtenda um ângulo de 2 b_i, por isso vou pular os detalhes e seguir diretamente para um exemplo. Aqui estão ilustrações das soluções para 64, 16 e 4 pontos aleatórios colocados em um mapa retangular

texto alternativo

texto alternativo

texto alternativo

Como você pode ver, as soluções parecem ficar mais agradável como o número de destino pontos aumenta. A solução para n = 4 mostra claramente como os rolamentos estão igualmente espaçados, pois nesse caso o espaçamento é igual a 360/4 = 90 graus e, obviamente, esse espaçamento é exatamente alcançado.

Esta solução não é perfeita: você provavelmente pode identificar vários arcos que poderiam ser ajustados manualmente para melhorar o gráfico. Mas não fará um trabalho terrível e parece ser um bom começo.

O algoritmo também tem o mérito de ser simples: a parte mais complicada consiste em classificar os destinos de acordo com seus rumos.


Codificação

Não conheço o PostGIS, mas talvez o código que eu usei para desenhar os exemplos possa servir como um guia para implementar esse algoritmo no PostGIS (ou em qualquer outro GIS).

Considere o seguinte como pseudocódigo (mas o Mathematica o executará :-). (Se este site oferecer suporte ao TeX, como os de matemática, estatísticas e TCS, eu poderia tornar isso muito mais legível.) A notação inclui:

  • Os nomes de variáveis ​​e funções diferenciam maiúsculas de minúsculas.
  • [Alpha] é um caractere grego em letras minúsculas. ([Pi] tem o valor que você acha que deveria ter).
  • x [[i]] é o elemento i de uma matriz x (indexada a partir de 1).
  • f [a, b] aplica a função f aos argumentos a e b. Funções no caso apropriado, como 'Min' e 'Table', são definidas pelo sistema; funções com uma letra minúscula inicial, como 'ângulos' e 'deslocamento', são definidas pelo usuário. Os comentários explicam quaisquer funções obscuras do sistema (como 'Arg').
  • A tabela [f [i], {i, 1, n}] cria a matriz {f [1], f [2], ..., f [n]}.
  • O círculo [o, r, {a, b}] cria um arco do círculo centrado no o do raio r do ângulo a ao ângulo b (ambos em radianos no sentido anti-horário do leste a leste).
  • A ordenação [x] retorna uma matriz de índices dos elementos classificados de x. x [[Ordenação [x]]] é a versão classificada de x. Quando y tem o mesmo comprimento que x, y [[Ordenando [x]]] classifica y em paralelo com x.

A parte executável do código é misericordiosamente curta - menos de 20 linhas - porque mais da metade é de sobrecarga declarativa ou de comentários.

Desenhe um mapa

zé uma lista de destinos e yé a origem.

circleMap[z_List, y_] := 
Module[{\[Alpha] = angles[y,z], \[Beta], \[Delta], n},
    (* Sort the destinations by bearing *)
    \[Beta] = Ordering[\[Alpha]];
    x = z[[\[Beta] ]]; (* Destinations, sorted by bearing from y *)
    \[Alpha] = \[Alpha][[\[Beta]]]; (* Bearings, in sorted order *)
    \[Delta] = offset[\[Alpha]];
    n = Length[\[Alpha]];
    Graphics[{(* Draw the lines *)
        Gray, Table[circle[y, x[[i]],2 \[Pi] i / n + \[Delta] - \[Alpha][[i]]], 
             {i, 1, Length[\[Alpha]]}],
        (* Draw the destination points *)
        Red, PointSize[0.02], Table[Point[u], {u, x}]
    }]
]

Crie um arco circular de ponto xa ponto ycomeçando no ângulo \[Beta]relativo ao rolamento x -> y.

circle[x_, y_, \[Beta]_] /; -\[Pi] < \[Beta] < \[Pi] := 
Module[{v,  \[Rho], r, o, \[Theta], sign},
    If[\[Beta]==0, Return[Line[{x,y}]]];

    (* Obtain the vector from x to y in polar coordinates. *)
    v = y - x; (* Vector from x to y *)
    \[Rho] = Norm[v]; (* Length of v *)
    \[Theta] = Arg[Complex @@ v]; (* Bearing from x to y *)

    (* Compute the radius and center of the circle.*)
    r = \[Rho] / (2 Sin[\[Beta]]); (* Circle radius, up to sign *)
    If[r < 0, sign = \[Pi], sign = 0];
    o = (x+y)/2 + (r/\[Rho]) Cos[\[Beta]]{v[[2]], -v[[1]]}; (* Circle center *)

    (* Create a sector of the circle. *)
    Circle[o, Abs[r], {\[Pi]/2 - \[Beta] + \[Theta] + sign, \[Pi] /2 + \[Beta] + \[Theta] + sign}]
]

Calcule os rolamentos de uma origem para uma lista de pontos.

angles[origin_, x_] := Arg[Complex@@(#-origin)] & /@ x;

Calcule a faixa média dos resíduos de um conjunto de rolamentos.

xé uma lista de rolamentos em ordem classificada. Idealmente, x [[i]] ~ 2 [Pi] i / n.

offset[x_List] :=
Module[
    {n = Length[x], y},
    (* Compute the residuals. *)
    y = Table[x[[i]] - 2 \[Pi] i / n, {i, 1, n}];
    (* Return their midrange. *)
    (Max[y] + Min[y])/2
]
whuber
fonte
Devo mencionar que esta solução assume os destinos mais ou menos em torno da origem. Quando não é esse o caso, a idéia inteira (de rolamentos igualmente espaçados) não é boa. Mas pode ser facilmente corrigido introduzindo alguns destinos falsos dentro das lacunas angulares e depois removendo esses destinos (e seus arcos). Este processo pode ser automatizado, calculando a distância média entre os rolamentos e utilizando-se para identificar as lacunas grandes, etc .
whuber
Belos gráficos. Gostaria de saber se as companhias aéreas usam uma ferramenta automatizada quando elaboram os mapas de rotas mostrados na parte de trás de sua revista de bordo.
precisa saber é o seguinte
1
@ Kirk Eles provavelmente pagam alguém para fazer a cartografia manualmente :-). Fui inspirado por esta pergunta para ver se uma abordagem simples poderia criar gráficos razoavelmente bons. A resposta parece promissora. A propósito, esses gráficos foram produzidos pelo Mathematica 8 usando suas primitivas Circle e Point e um pouco de aritmética vetorial para encontrar os centros dos círculos.
whuber
Adoro o resultado que você me mostrou e este é o caminho a percorrer. Serei honesto, porém, me considero técnico, mas me perdi um pouco na fórmula que você deu, e como transformar isso em código PostGIS torna-se, portanto, quase impossível. Alguém aí tem alguma idéia de como traduzir o conceito de whuber em código viável? Vou tentar revisar e testar, mas a ajuda seria muito apreciada.
precisa saber é o seguinte
@ whuber- Obrigado pelo pseudocódigo atualizado. Teremos que ver se podemos implementá-lo no PostGIS.
11119 RyanDalton
5

Experimente ST_CurveToLine

Algo como, por exemplo:

SELECT ST_CurveToLine('CIRCULARSTRING(1 1,5 3,10 1)'::geometry) as the_geom;

Você pode visualizar isso copiando a consulta na caixa de texto e pressionando Map1 em http://www.postgisonline.org/map.php

Nicklas Avén
fonte
Acabei tentando fazer uma curva de um conjunto de cadeias de linhas de "dois pontos".
Brent Edwards
3

Acabei tentando fazer uma curva de um conjunto de cadeias de linhas de "dois pontos" usando a função ST_CurveToLine, como sugerido por @Nicklas Avén.

Passei os três conjuntos de coordenadas a seguir para a função ST_OffsetCurve:

  1. Início da linha original
  2. Ponto médio de um deslocamento de linha paralelo à linha original
  3. Fim da linha original

Eu usei a função ST_OffsetCurve para calcular o deslocamento - 1/10 do comprimento da linha original no meu exemplo.

Aqui está o SQL que eu usei para gerar as linhas curvas a partir das linhas retas originais:

    ST_CurveToLine('CIRCULARSTRING(' || st_x(st_startpoint(the_geom)) || ' ' || st_y(st_startpoint(the_geom)) || ', ' || st_x(st_centroid(ST_OffsetCurve(the_geom, st_length(the_geom)/10, 'quad_segs=4 join=bevel'))) || ' ' || st_y(st_centroid(ST_OffsetCurve(the_geom, st_length(the_geom)/10, 'quad_segs=4 join=bevel'))) || ', ' || st_x(st_endpoint(the_geom)) || ' ' ||  st_y(st_endpoint(the_geom)) || ')') AS the_curved_geom
Brent Edwards
fonte
Realmente útil, mas por algum motivo o resultado não respeita meu srid. Alguma idéia do porquê?
DMS02 26/10/16
Você poderia fornecer mais detalhes - srid da geometria de entrada, srid de saída está ausente, é diferente, são gerados erros (quais aplicativos - QGIS, PostgreSQL).
Brent Edwards
A tabela em que desejo inserir as linhas curvas resultantes tem uma restrição enforce_srid_geom. Quando executo a consulta, recebo um erro dizendo que esta consulta viola essa restrição. Com uma tabela sem essa restrição, ela funciona, mas ao adicioná-la ao QGIS, ela é listada com srid 0. Minha consulta: INSERT INTO test (the_curved_geom) SELECT [aqui seu SQL] FROM linhas
DMS02 28/16/16
Tente executar as postgis.net/docs/ST_GeometryType.html e postgis.net/docs/ST_SRID.html funções na coluna de geometria (the_curved_geom) e verificar se existem conflitos com sua tabela de teste e enforce_srid_geom. Nesse caso, você pode transformar a geometria / srid conforme necessário ou modificar sua tabela / restrição de teste.
Brent Edwards