“Mappe di percorso” punto a punto curve


39

Di recente ho esaminato le pagine Web delle compagnie aeree che mostrano i loro percorsi in partenza da una determinata città verso tutte le altre città che servono. Vorrei essere in grado di creare percorsi curvi simili tra i punti. Qualcuno ha creato script o funzioni che genereranno gli archi curvi come quelli visualizzati in questo esempio ?

Percorsi di volo

In PostGIS, esiste un'implementazione di ST_MakeLine che ti consentirebbe di specificare la quantità di curva da utilizzare quando si collegano 2 punti?

Mentre attualmente sto usando PostGIS e QGIS, sarei lieto di conoscere altre opzioni software che potrebbero essere in grado di creare lo stesso aspetto.


Qualcuno sa di qualche bella implementazione di questo? Esempi o altro?
Mark Boulder,

Risposte:


26

La creazione di grandi cerchi potrebbe darti l'effetto desiderato.

Forse qualcosa come discusso su http://lists.osgeo.org/pipermail/postgis-users/2008-February/018620.html

Aggiornare:

Ho seguito questa idea in "Visualizzazione delle connessioni globali" . È una soluzione puramente basata su PostGIS che utilizza la riproiezione per creare archi.

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

(La definizione di CRS per 953027 è disponibile qui: http://spatialreference.org/ref/esri/53027/ )

inserisci qui la descrizione dell'immagine


4
Mi piace l'idea, anche se con grandi cerchi, il problema che incontri è che a distanze più brevi finirai comunque con una linea generalmente retta. Mi piacerebbe essere in grado di controllare la quantità di arco che ho messo nella linea (cioè- lunghezza = distanza * 2).
Ryan Dalton,

1
Ecco un buon esempio del problema con il semplice utilizzo di grandi cerchie: gc.kls2.com/cgi-bin/…
RyanDalton

1
Dopo alcune ricerche aggiuntive, ho trovato questo post che può essere utile per aiutare questo metodo. mail-archive.com/postgis-users@postgis.refractions.net/…
RyanDalton

Per un futuro lettore, ho pensato di andare avanti e creare un collegamento al recente post sul blog di @ underdark che tratta questo argomento. underdark.wordpress.com/2011/08/20/...
RyanDalton

È fantastico!! Utilizzato nel mio progetto per tracciare linee tra i check-in degli utenti e le posizioni dei locali, prelevato da Forsquare
Lorenzo Barbagli il

24

Il problema è capire quanto piegare gli archi per migliorare la loro risoluzione visiva.

Ecco una soluzione (tra le tante possibili). Consideriamo tutti gli archi che provengono da un'origine comune. Gli archi sono più affollati qui. Per separarli al meglio, sistemiamoli in modo che si estendano in angoli equidistanti . È un problema se tracciamo segmenti di linea retta dall'origine alle destinazioni, perché in genere ci saranno gruppi di destinazioni in varie direzioni. Usiamo la nostra libertà per piegare gli archi per spaziare gli angoli di partenza il più uniformemente possibile.

Per semplicità usiamo archi circolari sulla mappa. Una misura naturale della "piega" in un arco dal punto y al punto x è la differenza tra il suo rilevamento su y e il rilevamento direttamente da y a x . Un tale arco è un settore di un cerchio su cui y e x sia menzogna; la geometria elementare mostra che l'angolo di curvatura è uguale alla metà dell'angolo incluso nell'arco.

Per descrivere un algoritmo abbiamo bisogno di un po 'più di notazione. Permettere y il punto di origine (come proiettato sulla mappa) e che x_1 , x_2 , ..., x_n siano i punti di destinazione. Definire a_i come cuscinetto da y a x_i , i = 1, 2, ..., n .

Come passo preliminare, supponiamo che i cuscinetti (tutti compresi tra 0 e 360 ​​gradi) siano in ordine crescente: questo richiede di calcolare i cuscinetti e quindi ordinarli; entrambi sono compiti semplici.

Idealmente, vorremmo che i cuscinetti degli archi fossero uguali a 360 / n , 2 * 360 / n , ecc., Rispetto ad alcuni cuscinetti di partenza. Le differenze tra i cuscinetti desiderati e i cuscinetti effettivi sono pertanto pari a i * 360 / n - a_i più il cuscinetto iniziale, a0 . La differenza più grande è il massimo di queste n differenze e la più piccola differenza è il loro minimo. Set di Let a0 di essere a metà strada tra il massimo ed il minimo; questo è un buon candidato per il cuscinetto iniziale perché minimizza la quantità massima di flessione che si verificherà . Di conseguenza, definire

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

questa è la piega da usare .

È una questione di geometria elementare da cui disegnare un arco circolare y a x che sottende un angolo di 2 b_i, quindi salterò i dettagli e passerò direttamente ad un esempio. Ecco le illustrazioni delle soluzioni per 64, 16 e 4 punti casuali posizionati all'interno di una mappa rettangolare

testo alternativo

testo alternativo

testo alternativo

Come puoi vedere, le soluzioni sembrano migliorare con l'aumentare del numero di punti di destinazione. La soluzione per n = 4 mostra chiaramente come i cuscinetti siano equidistanti, poiché in questo caso la spaziatura è pari a 360/4 = 90 gradi e ovviamente tale spaziatura è esattamente raggiunta.

Questa soluzione non è perfetta: probabilmente puoi identificare diversi archi che potrebbero essere modificati manualmente per migliorare la grafica. Ma non farà un lavoro terribile e sembra essere davvero un buon inizio.

L'algoritmo ha anche il merito di essere semplice: la parte più complicata consiste nell'ordinare le destinazioni in base ai loro cuscinetti.


Coding

Non conosco PostGIS, ma forse il codice che ho usato per disegnare gli esempi può servire da guida per l'implementazione di questo algoritmo in PostGIS (o in qualsiasi altro GIS).

Considera quanto segue come pseudocodice (ma Mathematica lo eseguirà :-). (Se questo sito supporta TeX, come fanno quelli matematici, statistici e TCS, potrei renderlo molto più leggibile.) La notazione include:

  • I nomi delle variabili e delle funzioni fanno distinzione tra maiuscole e minuscole.
  • [Alpha] è un carattere greco minuscolo. ([Pi] ha il valore che pensi che dovrebbe avere.)
  • x [[i]] è l'elemento i di un array x (indicizzato a partire da 1).
  • f [a, b] applica la funzione f agli argomenti a e b. Le funzioni nel caso appropriato, come "Min" e "Tabella", sono definite dal sistema; le funzioni con una lettera iniziale minuscola, come "angoli" e "offset", sono definite dall'utente. I commenti spiegano eventuali funzioni di sistema oscure (come "Arg").
  • La tabella [f [i], {i, 1, n}] crea l'array {f [1], f [2], ..., f [n]}.
  • Cerchio [o, r, {a, b}] crea un arco del cerchio centrato a o del raggio r dall'angolo a all'angolo b (entrambi in radianti in senso antiorario da est).
  • L'ordinamento [x] restituisce una matrice di indici degli elementi ordinati di x. x [[Ordering [x]]] è la versione ordinata di x. Quando y ha la stessa lunghezza di x, y [[Ordine [x]]] ordina y in parallelo con x.

La parte eseguibile del codice è misericordiosamente breve - meno di 20 righe - perché oltre la metà è costituita da spese generali dichiarative o commenti.

Disegna una mappa

zè un elenco di destinazioni ed yè l'origine.

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}]
    }]
]

Crea un arco circolare da un punto xall'altro yiniziando dall'angolo \[Beta]relativo al cuscinetto 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}]
]

Calcola i cuscinetti da un'origine a un elenco di punti.

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

Calcola la gamma media dei residui di un set di cuscinetti.

xè un elenco di cuscinetti in ordine ordinato. 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
]

Devo dire che questa soluzione presuppone che le destinazioni circondino più o meno l'origine. In caso contrario, l'intera idea (di cuscinetti equidistanti) non è valida. Ma può essere facilmente risolto introducendo alcune destinazioni false negli spazi angolari e successivamente rimuovendo tali destinazioni (e i loro archi). Questo processo può essere automatizzato calcolando la distanza media tra i cuscinetti e utilizzandola per identificare le grandi lacune, ecc .
whuber

Bella grafica. Mi chiedo se le compagnie aeree utilizzino uno strumento automatizzato quando elaborano le mappe delle rotte mostrate sul retro della rivista di bordo.
Kirk Kuykendall,

1
@Kirk Probabilmente pagano qualcuno per fare la cartografia manualmente :-). Sono stato ispirato da questa domanda per vedere se un approccio semplice potesse creare una grafica ragionevolmente buona. La risposta sembra promettente. Queste grafiche, tra l'altro, sono state prodotte da Mathematica 8 usando i suoi primitivi Circle e Point e un po 'di aritmetica vettoriale per trovare i centri del cerchio.
whuber

Adoro il risultato che hai mostrato e per me questa è la strada da percorrere. Sarò onesto però, mi considero tecnico ma mi sono perso un po 'nella formula che hai dato e come trasformarlo in codice PostGIS diventa quindi quasi impossibile. Qualcuno là fuori ha qualche idea su come tradurre il concetto di whuber in codice praticabile? Proverò a recensire e a provarlo, ma l'aiuto sarebbe molto apprezzato.
Ryan Dalton,

@ whuber- Grazie per lo pseudocodice aggiornato. Dovremo vedere se possiamo effettivamente implementarlo in PostGIS.
Ryan Dalton,



3

Ho finito per provare questo per curvare una serie di linestring "a due punti" usando la funzione ST_CurveToLine come suggerito da @Nicklas Avén.

Ho passato i seguenti 3 set di coordinate alla funzione ST_OffsetCurve:

  1. Inizio della riga originale
  2. Punto medio di uno scostamento di linea parallelo alla linea originale
  3. Fine della linea originale

Nel mio esempio ho usato la funzione ST_OffsetCurve per calcolare l'offset - 1/10 della lunghezza della linea originale.

Ecco l'SQL che ho usato per generare le linee curve dalle linee rette originali:

    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

Davvero utile, ma per qualche motivo il risultato non rispetta il mio srido. Qualche idea sul perché?
DMS02,

Potresti fornire qualche dettaglio in più - srid della geometria di input, manca srid di output, è diverso, vengono generati errori (quali applicazioni - QGIS, PostgreSQL).
Brent Edwards

La tabella in cui voglio inserire le linee curve risultanti ha un vincolo enforce_srid_geom. Quando eseguo la query, viene visualizzato un errore che indica che questa query viola tale vincolo. Con un tavolo senza che vincolo funziona, ma poi quando si aggiunge a QGIS è elencato con SRID 0. Il mio query: INSERT INTO prova (the_curved_geom) SELECT [qui il tuo SQL] dalle linee
DMS02

Prova a eseguire le funzioni postgis.net/docs/ST_GeometryType.html e postgis.net/docs/ST_SRID.html nella colonna della geometria (the_curved_geom) e controlla se ci sono conflitti con la tabella di test e enforce_srid_geom. In tal caso, è possibile trasformare la geometria / srid in base alle esigenze o modificare la tabella di prova / il vincolo.
Brent Edwards,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.