Verbindungspunkte (Bushaltestellen), die nicht auf Leitungen liegen (LINESTRING), mit dem Netzwerk?

9

Ich muss Bushaltestellen (Punkte) mit einer Netzwerkschicht (OSM-Daten) verbinden. Diese Bushaltestellen liegen weder direkt auf den Linien (siehe Screenshot) noch sollten ihre Position verschoben werden. Ich verwende PostGIS, pgrouting und QGIS und das Netzwerk ist bereits mit Quell- und Zielspalten usw. routbar.

Geben Sie hier die Bildbeschreibung ein

Hauptsächlich möchte ich danach zwei Dinge tun:

  1. Ermitteln der Entfernungen zwischen den Bushaltestellen mithilfe der Analyse des kürzesten Pfades.
  2. Erstellen von Isochronen mit Gehentfernung von der Bushaltestelle über das OSM-Netzwerk.

Um genaue Werte zu erhalten, muss das Routing am nächsten an den Bushaltestellen beginnen und anhalten. In vielen Fällen ist der nächstgelegene vorhandene Knoten zu weit entfernt, um genaue Werte zu erhalten. Es sollte jedoch keine Weiterleitung zum tatsächlichen Punkt der Bushaltestelle geben. In meinem Beispiel auf dem Bild sehen Sie, wie das Routing zwischen Stopps aussehen sollte.

Gibt es eine Möglichkeit, neue Knoten automatisch in das Netzwerk (LINESTRING) einzufügen, die den Bushaltestellen am nächsten liegen, oder ist es möglich, das Routing auf einer Art "Dummy-Punkt" zu starten, der nur für die Abfrage festgelegt wurde (ähnlich wie auf der Straße) Graph Plugin in QGIS macht)?

Setraworks
quelle

Antworten:

5

Der erste Teil der Lösung lautet:

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;

Dies rastet die Bushaltestellen an den Linien des Straßennetzes ein, wie Sie auf dem Bild sehen können, und funktioniert ganz einfach.

Geben Sie hier die Bildbeschreibung ein

Als nächstes werde ich versuchen, die Linien an den Stellen der Punkte zu teilen. Nach dem Aufteilen der Zeilen möchte ich pgr_createTopology wieder verwenden. Danach sollte es möglich sein, eine Abfrage zu erstellen, um die nächstgelegenen Knoten zu den Bushaltestellen zu ermitteln. Dies sind dann meine neu generierten Knoten an den "Splitpoints".

Ich wäre dankbar, wenn jemand einen Hinweis für mich hätte, wie man Linestring mit Punktmerkmalen in Postgis aufteilt, da es im Moment keine einfache Lösung dafür zu geben scheint, nachdem ich ähnliche Fragen betrachtet habe.

Setraworks
quelle
ST_Split (Somethingtocut, Klinge)
Simplexio
1
Kommentar hinzufügen, weil ich dies überhaupt nicht getestet habe, Syntac wahrscheinlich falsch ... ... wähle *, st_split (a.lg, a.pg) aus (wähle *, lines.g als lg, points.geom als pg von points Verbinden Sie Zeilen auf ST_intersect (p.geom, l.geom)) als eine, aber diese geteilte Rückgabesammlung, so dass Sie immer noch alle Zeilen daraus
entfernen müssen
2

Dies ist meine vollständige Lösung. Es ist eine Art Hack, um die Aufteilung durchzuführen: Ich erhalte die Punkte auf den Linien (Möglichkeiten, die OSM-Terminologie zu verwenden) ST_ClosestPointund puffere sie dann um einen sehr kleinen Abstand, damit die Aufteilung tatsächlich funktioniert. Andernfalls verhinderten Ungenauigkeiten / Rundungsfehler die Aufteilung.

Dies hat das Problem, dass auf jeder Linie pro Punkt zwei Teilungen erzeugt werden (aufgrund der Pufferung). Für meinen Gebrauch war dies in Ordnung, da ich später zwischen den Teilungspunkten geroutet habe, die den ursprünglichen Punkten am nächsten waren, die außerhalb der Linie lagen, und es könnte einer der beiden Teilungspunkte der Linienpufferkreuzung sein.

Ich habe zunächst OSM-Daten heruntergeladen und in Postgres übernommen:

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

Aufteilen der Wege mithilfe eines Puffers:

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;

Erstellen Sie die für das Routing mit pgrouting erforderliche Topologie:

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
quelle
Mein erster Gedanke war auch Puffer. Wenn Sie jedoch einen Abstand zum nächsten erhalten, diesen Betrag puffern und einen Punkt an diesem Schnittpunkt erstellen können, können Sie eine Linie mit den Endpunkten erstellen, die aus Ihrem ursprünglichen Punkt und dem nächstgelegenen Punkt bestehen.
Mox
1

Da ich an einer ähnlichen Aufgabe arbeite, wollte ich nur über den Ansatz berichten, den ich derzeit verwende. Dies nutzt GRASS GIS, aber was meine Experimente mit PostGIS angeht, ist es ziemlich kompliziert, vorhandenen LineStrings mehrere neue Punkte hinzuzufügen, indem diese LineStrings an den jeweiligen Standorten aufgeteilt werden - obwohl ich sicher bin, dass es eine bequeme Lösung gibt.

Ich habe jetzt die GRASS GIS- v.netFunktion mit der Option verwendet connect. Wählen Sie einfach input vector line layerund points layer. Es besteht die Möglichkeit, die Punkte entweder am nächsten Punkt auf den Linien zu fangen oder neue Verbindungen zwischen dem nächsten Punkt auf den Linien und dem neuen Punkt herzustellen.

Hier ist ein Vorher- und Nachher-Bild. Auf der rechten Seite wurde für jeden Punkt der Punktebene ein Knoten im Straßennetz hinzugefügt: Geben Sie hier die Bildbeschreibung ein

..._vertices_pgrWeisen Sie anschließend in PostGIS nach dem Erstellen Ihrer Tabelle aus dem Straßennetz Ihre Punkte dem nächstgelegenen Scheitelpunkt zu, damit Sie sie in Ihren Routing-Anforderungen verwenden können. Für diese Aufgabe können Sie die ST_ClosestPointFunktion verwenden, die @Setraworks in seiner Antwort ausgeführt hat.

Nachteile dieses Ansatzes sind:

  • Das Verbinden von Punkten mit Linien muss in GRASS GIS erfolgen
  • Routen, die berechnet werden, können aus vielen Komponenten bestehen (abhängig von der Anzahl der neu hinzugefügten Punkte).
  • dynamisches Hinzufügen neuer Punkte nicht möglich

Dieser Ansatz funktioniert einwandfrei, wenn Sie eine definierte Anzahl von Punkten zum Straßennetz hinzufügen müssen (wie im Beispiel der Frage mit Bushaltestellen).

Wenn jemand mit PostGIS ein funktionierendes Beispiel liefern kann, würde ich gerne darüber lesen!

SchoGeo
quelle
0

Es gibt einen Beitrag, in dem ein ähnliches Problem behandelt wird. Sie können diesen Beitrag an folgender Stelle sehen: http://osdir.com/ml/qgis-user-gis/2011-11/msg00220.html

Ryan Garnett
quelle
Dies ist nur ein Teil einer möglichen Lösung, da die Punkte nach dem Einrasten von Punkten auf Linien direkt auf den Linien liegen, aber immer noch nicht Teil des Netzwerks sind.
Setraworks
Wenn Sie eine Antwort erwarten, die Ihnen alle Ihre Anforderungen erfüllt, werden Sie möglicherweise enttäuscht sein. Dies kann Sie auf halbem Weg bringen, dann können Sie sich auf den anderen Teil konzentrieren, den Sie vermissen.
Ryan Garnett
Ich denke du hast recht Ryan. Ich habe es bereits geschafft, die Punkte an den Linien auszurichten. Der nächste Schritt besteht darin, herauszufinden, wie Linestrings mit Punkten in Postgis geteilt werden. Vielen Dank für Ihre bisherige Hilfe!
Setraworks
Froh, dass ich helfen konnte. Es gibt Tools, die eine Linie mit einem Punkt teilen, aber ich werde weiterhin nach einer Option in PostGIS suchen. Viel Glück
Ryan Garnett
@Setraworks Sie können sich die folgende PostGIS-Option (ST_Split) postgis.net/docs/ST_Split.html ansehen . Sie können eine Linie mit einem Punkt teilen. Hier ist die Erklärung von PostGIS: Die Funktion unterstützt das Teilen einer Linie nach Punkt, einer Linie nach Linie, eines Polygons nach Linie. Die zurückgegebene Geometrie ist immer eine Sammlung.
Ryan Garnett