Łączenie punktów (przystanków autobusowych), które nie leżą na liniach (LINESTRING), do sieci?

9

Muszę podłączyć przystanki autobusowe (punkty) do warstwy sieci (dane OSM). Te przystanki autobusowe nie leżą bezpośrednio na liniach (patrz zrzut ekranu), a ich lokalizacja nie powinna być zmieniana. Używam PostGIS, pgrouting i QGIS, a sieć jest już routowalna z kolumnami źródłowymi i docelowymi itp.

wprowadź opis zdjęcia tutaj

Głównie chcę później zrobić dwie rzeczy:

  1. Ustalanie odległości między przystankami autobusowymi za pomocą analizy najkrótszej ścieżki.
  2. Tworzenie izochron z odległościami pieszymi od przystanku autobusowego za pomocą sieci OSM.

Aby uzyskać dokładne wartości, konieczne jest, aby trasy „zaczynały się” i „zatrzymywały” jak najbliżej przystanków autobusowych. W wielu przypadkach najbliższy istniejący węzeł będzie zbyt daleko, aby uzyskać dokładne wartości. Ale nie powinno być trasy do faktycznej lokalizacji przystanku autobusowego. W moim przykładzie na zdjęciu widać, jak powinna wyglądać trasa między przystankami.

Czy istnieje możliwość automatycznego wstawiania nowych węzłów do sieci (LINESTRING), które znajdują się najbliżej przystanków autobusowych, czy też możliwe jest rozpoczęcie routingu w rodzaju „fikcyjnego punktu”, który jest ustawiony tylko dla zapytania (podobnie do drogi wtyczka graficzna w QGIS robi)?

Setraworks
źródło

Odpowiedzi:

5

Pierwsza część rozwiązania jest następująca:

SELECT a.id, ST_Closestpoint(ST_Collect(b.geom_way), a.geom) AS geom 
FROM point_table a, line_table b
GROUP BY a.id, a.geom;

To przyciąga przystanki do linii sieci drogowej, jak widać na zdjęciu i działa dość łatwo.

wprowadź opis zdjęcia tutaj

Następnie spróbuję podzielić linie w miejscach punktów. Po podzieleniu linii chcę ponownie użyć pgr_createTopology. Następnie powinno być możliwe utworzenie zapytania w celu znalezienia najbliższych węzłów do przystanków autobusowych, które będą później moimi nowo wygenerowanymi węzłami w „punktach podziału”.

Byłbym wdzięczny, gdyby ktoś miał dla mnie podpowiedź, jak rozdzielić liniowanie z elementami punktowymi w postgis, ponieważ po spojrzeniu na podobne pytania nie wydaje się obecnie na to łatwe rozwiązanie.

Setraworks
źródło
ST_Split (coś, ostrze)
simplexio
1
dodając komentarz, ponieważ wcale tego nie testowałem, syntac prawdopodobnie źle ... ... wybierz *, st_split (a.lg, a.pg) z (wybierz *, lines.g jako lg, points.geom jako pg z punktów połącz linie na ST_intersect (p.geom, l.geom)) jako kolekcję zwrotów podzieloną, więc nadal musisz wyciągnąć z niej wszystkie linie ...
simplexio
2

To jest moje pełne rozwiązanie. Wymaga to pewnego rodzaju włamania, aby wykonać podział: zdobywam punkty na liniach (sposoby, używając terminologii OSM) za pomocą ST_ClosestPoint, a następnie buforuję je na bardzo małą odległość, aby podział rzeczywiście zaczął działać. W przeciwnym razie błędy niedokładności / zaokrąglania zapobiegały podziałowi.

Ma to problem polegający na tym, że generuje dwa podziały w każdej linii na punkt (z powodu buforowania). Na mój użytek było to w porządku, ponieważ później poprowadziłem trasę między punktami podziału najbliższymi oryginalnym punktom, które znajdowały się poza linią, i mógł to być jeden z dwóch punktów podziału linii przecięcia z buforem.

Zacząłem od pobrania danych OSM i impotowania ich do Postgres:

CITY="MY_CITY"
BBOX="-46.6003,-23.7362,-46.4806,-23.5965"
wget --progress=dot:mega -O "$CITY.osm" "http://www.overpass-api.de/api/xapi?*[bbox=${BBOX}][@meta]"

# create database
createdb my_database
# add extensions
psql -d my_database -c "CREATE EXTENSION postgis;"
psql -d my_database -c "CREATE EXTENSION pgrouting;"

# import osm data to postgres database
osm2pgrouting \
    -f MY_CITY.osm \
    -d my_database \
    -U user

# load points into db
shp2pgsql -I -s 4326 points_to_split_ways.shp public.points_to_split_ways | psql -d my_database

Podział sposobów za pomocą bufora:

WITH pts_ways AS (
  -- get nearest way for each point we want to split the ways by
  SELECT s.gid AS pt_id, ws.gid AS way_gid, s.geom AS pt_geom, ws.the_geom AS way_geom FROM points_to_split_ways s
  CROSS JOIN LATERAL
  (
    SELECT w.gid, w.the_geom
    FROM ways w
    ORDER BY s.geom <-> w.the_geom LIMIT 1
  ) AS ws
), pts_on_ways AS (
  -- "move" these points to be on top of the ways
  SELECT pt_id, way_gid, ST_ClosestPoint(way_geom, pt_geom) as geom
  FROM pts_ways
), ways_without_pts AS (
  -- get the ways that don't have any points on them
  SELECT the_geom as the_geom, gid as way_gid FROM ways
  WHERE gid NOT IN (SELECT way_gid FROM pts_ways)
)
SELECT
  way_gid as old_id,
  -- we need to build a new unique ID, because split ways will share the old ID
  row_number() over(order by way_gid) as gid,
  -- this is the split way geometry
  the_geom
FROM (
  SELECT 
    way_gid,
    -- split the ways and dump into indiviudal segments
    (ST_Dump(ST_Split(line_geom, pt_geom))).geom AS the_geom
  FROM (
    (SELECT the_geom as line_geom, gid FROM ways) AS lines
    LEFT JOIN
    -- HACK: use a buffer to fix imprecisions / rounding errors
    -- this will generate one extra splitting per point (each buffer will intersect each way twice)
    -- but it's ok for our purposes
    -- also, collect them grouped by the way to handle cases where there are multiple points on the same way
    (SELECT ST_Collect(ST_Buffer(geom, 0.000001)) as pt_geom, way_gid FROM pts_on_ways GROUP BY way_gid) AS pts
    ON lines.gid = pts.way_gid
  ) AS tmp1
  -- union the ways without points, otherwise you'd get only the ones that were split
  UNION ALL
  SELECT way_gid, the_geom FROM ways_without_pts
) AS tmp2;

Utwórz topologię potrzebną do routingu z pgroutingiem:

SELECT UpdateGeometrySRID('ways_split','the_geom', 4326);
SELECT find_srid('public','ways_split','the_geom');
ALTER TABLE ways_split ADD COLUMN "source" integer;
ALTER TABLE ways_split ADD COLUMN "target" integer;
ALTER TABLE ways_split ADD PRIMARY KEY (gid);
ALTER TABLE ways_split ADD CONSTRAINT ways_source_fkey FOREIGN KEY (source) REFERENCES ways_split_vertices_pgr (id) MATCH FULL;
ALTER TABLE ways_split ADD CONSTRAINT ways_target_fkey FOREIGN KEY (target) REFERENCES ways_split_vertices_pgr (id) MATCH FULL;
SELECT pgr_createTopology('ways_split', 0.00001, 'the_geom', 'gid', clean := TRUE);
SELECT pgr_analyzeGraph('ways_split', 0.000001, the_geom := 'the_geom', id := 'gid');
bplmp
źródło
Moją pierwszą myślą było również buforowanie. Ale jeśli możesz uzyskać „odległość do najbliższego”, buforować tę kwotę, utworzyć punkt w tym przecięciu ... to możesz utworzyć linię z punktami końcowymi składającymi się z oryginalnego punktu i „najbliższego” punktu do niego.
Mox
1

Ponieważ pracuję nad podobnym zadaniem, chciałem tylko opowiedzieć o podejściu, którego obecnie używam. Korzysta z GRASS GIS, ale jeśli chodzi o moje eksperymenty z PostGIS, dość skomplikowane jest dodawanie wielu nowych punktów do istniejących LineStrings poprzez podzielenie tych LineStrings w odpowiednich lokalizacjach - chociaż jestem pewien, że istnieje wygodne rozwiązanie.

Teraz skorzystałem z funkcji GRASS GIS, v.netkorzystając z opcji connect. Po prostu wybierz input vector line layeri points layer. Istnieje możliwość przyciągnięcia punktów do najbliższego punktu na liniach lub utworzenia nowych połączeń między najbliższym punktem na liniach i nowym punktem.

Oto zdjęcie przed i po. Po prawej stronie dla każdego punktu warstwy punktów dodano węzeł w sieci drogowej: wprowadź opis zdjęcia tutaj

Następnie w PostGIS, po utworzeniu ..._vertices_pgrtabeli poza siecią, po prostu przypisz swoje punkty do najbliższego wierzchołka, abyś mógł ich użyć w swoich żądaniach routingu. Do tego zadania możesz użyć ST_ClosestPointfunkcji wykonanej przez @Setraworks w jego odpowiedzi.

Wady tego podejścia to:

  • łączenie punktów z liniami należy wykonać w GRASS GIS
  • obliczane trasy mogą składać się z wielu elementów (w zależności od ilości nowo dodanych punktów)
  • dynamiczne dodawanie nowych punktów nie jest możliwe

Podejście to działa dobrze, jeśli masz określoną liczbę punktów do dodania do sieci (jak w przykładzie pytania z przystankami autobusowymi).

Jeśli ktoś może podać działający przykład za pomocą PostGIS, chciałbym o tym przeczytać!

SchoGeo
źródło
0

Jest post, który omawia podobny problem, można go zobaczyć w następującej lokalizacji: http://osdir.com/ml/qgis-user-gis/2011-11/msg00220.html

Ryan Garnett
źródło
Jest to tylko część możliwego rozwiązania, ponieważ po przyciągnięciu punktów do linii punkty leżą bezpośrednio na liniach, ale nadal nie są częścią sieci.
Setraworks
Jeśli oczekujesz odpowiedzi, która zapewni ci wszystkie twoje wymagania, możesz być rozczarowany. To może doprowadzić cię do połowy, a następnie możesz skupić się na drugiej części, której brakuje.
Ryan Garnett
Myślę, że masz rację Ryan. Udało mi się już przyciągnąć punkty do linii, więc następnym krokiem będzie dowiedzieć się, jak podzielić linie z punktami w postgis. Dziękujemy za pomoc do tej pory!
Setraworks
Cieszę się, że mogłem pomóc. Istnieją narzędzia, które dzielą linię za pomocą punktu, ale nadal będę szukał opcji w PostGIS. Powodzenia
Ryan Garnett
@ Setraworks możesz zajrzeć do następującej opcji PostGIS (ST_Split) postgis.net/docs/ST_Split.html . Możesz podzielić linię za pomocą punktu, oto wyjaśnienie z PostGIS: Funkcja obsługuje podział linii po punkcie, linia po linii, wielobok po linii. Zwrócona geometria jest zawsze kolekcją.
Ryan Garnett