Costruire un poligono su un'area raggiungibile


10

Attualmente sto lavorando nel campo delle isocrone e degli algoritmi sottostanti. Ciò che ora causa problemi non è il calcolo se lo stesso isochrone, ma la visualizzazione dei risultati.
Il risultato del mio algoritmo isochrone sono punti e spigoli. In effetti ho una soluzione funzionante, ma per 3873 edge e per 1529 nodi le cose sembrano richiedere per sempre (circa 2,0 secondi sul mio laptop Lenovo T440s che contiene una CPU Core i7 2015 e un SSD piuttosto veloce). Invece di secondi voglio qualcosa di più simile a msec :-).

Forse qualcuno può aiutarmi a ridurre il tempo di calcolo necessario per costruire i poligoni che visualizzano le aree raggiungibili.

Ma aspetta ... prima le prime cose!
Ecco una visualizzazione dei bordi che sono il risultato del calcolo del mio isochrone: Risultato del calcolo isocrono (scheletro esistente di linestring) questi bordi sono memorizzati in una tabella del database PostGIS e sono semplici stringhe di linea.

Quello che voglio mostrare all'utente è simile al seguente: inserisci qui la descrizione dell'immagine Nota le aree disconnesse nell'estremo sud e molto ad est dell'immagine. Questi dovrebbero essere disegnati come aree separate (quindi non è consentita la fusione qui :-))

Attualmente sto usando questa query:

SELECT ST_AsGeoJson(St_Transform(ST_Multi(ST_Collect(polygons)), 4326)) AS coverage FROM (
    SELECT ST_MakePolygon(ST_ExteriorRing(ST_GeometryN(segments, generate_series(1, ST_NumGeometries(segments))))) AS polygons FROM (
        SELECT ST_Union(ST_Buffer("GEOMETRY", 20, 'quad_segs=2')) AS segments FROM my_edges AS a
    ) AS b
) AS c

Ho già fatto alcuni esperimenti e ho anche letto molta documentazione, ma non riesco proprio a trovare una soluzione migliore.
Ai miei occhi il grosso problema è l'uso di ST_Union (come indicato nei documenti questa funzione può essere lenta). La cosa molto interessante è che la sua sostituzione con ST_Collect sembra rallentare il calcolo ST_Buffer in modo che la query nel complesso richieda ancora più tempo, sebbene non riempia le aree tra i bordi (crea solo un buffer attorno alle linee ):

SELECT ST_AsGeoJson(St_Transform(ST_Multi(ST_Collect(polygons)), 4326)) AS coverage FROM (
    SELECT ST_Buffer(ST_Collect(ST_LineMerge("GEOMETRY")), 20, 'quad_segs=2') AS polygons FROM my_edges AS a
) AS b

Questo richiede circa 3,8 secondi sul mio sistema (quindi quasi il doppio del tempo). La mia prima conclusione su questo piccolo benchmark è che ST_Buffer diventa inaspettatamente lento quando si tratta di MultiLineStrings (anche più lento di quando si creano buffer per ogni linea e si uniscono i buffer - che ai miei occhi è solo strano)

Ho anche provato a usare le forme alfa (usando l'implementazione di pgRouting), ma poiché non esiste un valore alfa da impostare (e in effetti non vorrei davvero ora quale valore impostare tale valore) ottengo solo un grande poligono ( quindi perderei le regioni del sud e dell'est come regioni separate che non è quello che voglio).
Anche ST_Polygonize (che è stata la prima cosa che mi è venuta in mente) non ha prodotto risultati utilizzabili, ma forse mi sono perso qualcosa qui ...

Esiste un modo migliore per creare l'area mostrata in PostGIS? Forse anche usando il codice java (jts) o il codice javascript lato client (jsts)? In effetti potrei vivere perdendo alcuni dettagli finché le aree mostrate nel mio risultato rimangono separate e il calcolo diventa (molto) più veloce.


Non puoi semplicemente usare ST_Exteriorring (ST_Dump (ST_Union (ST_Buffer (geom, ....))). Geom visto che qualsiasi cosa bufferizzata sarà comunque un poligono e ST_Union collegherà tutte le geometrie che si intersecano, quindi non è necessario MakePolygon o GeometryN. Potrebbe essere necessario verificare Linestring che a volte risultano da ST_Union dopo un buffer, ma è facile con ST_GeometryType (geom). Per quanto riguarda l'utilizzo di Java o jsts, è possibile, ma è improbabile che sia più veloce, dato che un gran parte delle funzioni Postgis (GEOS) sono in primo luogo le porte C / C ++ di JTS
John Powell,

Hai ragione, funziona, ma in realtà non è più veloce (richiede ~ 3.1sec, mentre l'utilizzo di GeometryN richiede 2sec). Ecco cosa ho usato: SELECT ST_AsGeoJson (ST_Transform (ST_Exteriorring ((ST_Dump (ST_Union (ST_Buffer ("GEOMETRY", 20)))). Geom), 4326)) FROM my_edges;
Nikolaus Krismer,

@ john-barça: Oh .. Mi appannare la parte quad_segs = 2 in ST_Buffer quando provo il tuo approccio ... con quel cambiamento le query sono pari (entrambe a circa 2 secondi). Tuttavia, questo è ancora molto lento (ai miei occhi), c'è un altro modo per provarlo?
Nikolaus Krismer,

Problema interessante .... vuoi condividere alcuni dati di test?
dbaston,

Se aiuta, sono felice di condividere alcuni dati. Tutte le cose che faccio qui sono open source, quindi questo non dovrebbe essere un grosso problema. Prima cosa da notare: un'applicazione web per i test si trova in dbis-isochrone.uibk.ac.at:8080/testing . Maggiori informazioni sulle cose su cui lavoro sono disponibili su dbis-isochrone.uibk.ac.at . Nella sezione "collegamenti" del sito web ci sono alcuni ulteriori riferimenti (inclusi alcuni dati di prova)
Nikolaus Krismer,

Risposte:


5

Mettendo da parte la serializzazione GeoJSON, il mio laptop impiega circa 6,3 secondi:

SELECT
  ST_MakePolygon(
    ST_ExteriorRing(
      (ST_Dump(
        ST_Union(
          ST_Buffer(geom, 20, 2)))).geom))
FROM bz_edges

Guardando i dati in OpenJUMP, ho notato un bel po 'di dettagli nei segmenti di strada, rispetto al livello di dettaglio desiderato nell'output. Sembra che anche una semplificazione al volo di queste linee possa produrre un grande aumento di velocità in PostGIS:

SELECT
  ST_MakePolygon(
    ST_ExteriorRing(
      (ST_Dump(
        ST_Union(
          ST_Buffer(ST_Simplify(geom, 10), 20, 2)))).geom))
FROM bz_edges

che porta le cose a 2,3 secondi. Ho pensato che avrei potuto fare meglio memorizzando la geometria generalizzata in una colonna separata, invece di calcolarla al volo, ma in realtà non ha fornito alcun vantaggio aggiuntivo.

A seconda della quantità di codice che sei disposto a scrivere, puoi quasi sicuramente fare di meglio in Java, se non altro perché puoi sfruttare più core. (Per quello che vale, JTS esegue l'operazione sopra in 2,8 secondi). Un approccio potrebbe essere quello di estendere CascadedPolygonUnionper far sì che alcune delle operazioni sindacali avvengano in parallelo. (aggiornamento - ecco un ParallelCascadedPolygonUnion )

Ho notato nei dati di esempio che i bordi sono memorizzati con riferimenti ai loro nodi iniziale e finale, ovvero che hai un grafico predefinito. Ho il sospetto che puoi generare questi poligoni molto più rapidamente se lavori dal grafico invece di utilizzare operazioni geometriche generiche. Ad esempio, penso che potresti così qualcosa del genere:

  1. identificare i componenti collegati del grafico
  2. per ogni componente collegato, trova il nodo con la coordinata X minima (garantito che si trovi all'esterno del componente)
  3. percorrere i bordi del componente, girando sempre a sinistra (o destra) quando possibile. Questo ti darà l'anello esterno di ciascun componente.
  4. poligonizzare l'anello esterno e il buffer in modo appropriato.

Grazie ... la semplificazione è un grande e persino "semplice" miglioramento. Il mio laptop ha impiegato fino a 1,5 secondi il tempo necessario. Non è dove voglio essere, ma un po 'meglio.
Nikolaus Krismer,

Per quanto riguarda la soluzione suggerita (punti 1-4). Sembra anche molto semplice e vale la pena provare. Ho pensato a qualcosa di simile, ma sono bloccato al punto 1 (molto presto :-)). Come identificare i componenti connessi (l'unica cosa che mi viene in mente è una query ricorsiva che potrebbe anche essere molto lenta).
Nikolaus Krismer,

@NikolausKrismer Uso sia JGraphT che il telaio per attività come questa. Se invece scrivi i tuoi metodi grafici (non è una cattiva idea per le migliori prestazioni), una ricerca approfondita ti troverà i componenti. (Potresti trovarli nel prossimo PostGIS 2.2 con, ST_ClusterIntersectingma penso che vorrai comunque che qualsiasi tipo di elaborazione grafica avvenga al di fuori del database, quindi questo probabilmente non è utile).
dbaston,

questi sono alcuni ottimi suggerimenti. Ho guardato JGraphT e questo potrebbe sicuramente aiutare a risolvere il mio problema. Tuttavia, ho anche esaminato Postgis 2.2 e la funzione ST_ClusterIntersecting -> sono necessari circa 200-250 msec per identificare i diversi cluster nel caso sopra. Per me va bene (JGraphT potrebbe sicuramente fare di meglio). Ora ho a che fare con la creazione dell'anello esterno (ST_ExteriorRing fallisce, poiché ST_MakePolygon dice che i miei collegamenti non sono shell)
Nikolaus Krismer,

Vedo due complicazioni: (a) hai bisogno non solo dell'anello esterno, ma anche di tutti i segmenti che si estendono verso l'esterno da quell'anello, e (b) sembra che le tue linee non si intersecino effettivamente in alcune intersezioni. Dovrai correggere (b) se vuoi provare a costruire una geometria dai risultati di una camminata del grafico.
dbaston,
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.