.
Sie können eine rekursive Abfrage verwenden , um den nächsten Nachbarn jedes Punkts beginnend mit jedem erkannten Ende der Linien zu untersuchen, die Sie erstellen möchten.
Voraussetzungen : Bereiten Sie eine Postgis-Ebene mit Ihren Punkten und eine weitere mit einem einzelnen mehrzeiligen Objekt vor, das Ihre Straßen enthält. Die beiden Schichten müssen sich auf demselben CRS befinden. Hier ist der Code für den von mir erstellten Testdatensatz. Bitte ändern Sie ihn nach Bedarf. (Getestet auf Postgres 9.2 und Postgis 2.1)
WITH RECURSIVE
points as (SELECT id, st_transform((st_dump(wkb_geometry)).geom,2154) as geom, my_comment as com FROM mypoints),
roads as (SELECT st_transform(ST_union(wkb_geometry),2154) as geom from highway),
Hier sind die Schritte :
Generieren Sie für jeden Punkt die Liste aller Nachbarn und ihrer Entfernung, die diese drei Kriterien erfüllen.
- Die Entfernung darf einen benutzerdefinierten Schwellenwert nicht überschreiten (dies vermeidet die Verknüpfung mit einem isolierten Punkt).
graph_full as (
SELECT a.id, b.id as link_id, a.com, st_makeline(a.geom,b.geom) as geom, st_distance(a.geom,b.geom) as distance
FROM points a
LEFT JOIN points b ON a.id<>b.id
WHERE st_distance(a.geom,b.geom) <= 15
),
- Der direkte Weg darf keine Straße überqueren
graph as (
SELECt graph_full.*
FROM graph_full RIGHT JOIN
roads ON st_intersects(graph_full.geom,roads.geom) = false
),
Die Entfernung darf ein benutzerdefiniertes Verhältnis der Entfernung zum nächsten Nachbarn nicht überschreiten (dies sollte einer unregelmäßigen Digitalisierung besser Rechnung tragen als eine feste Entfernung). Dieser Teil war tatsächlich zu schwer zu implementieren und hielt sich an einen festen Suchradius
Nennen wir diese Tabelle "das Diagramm".
Wählen Sie das Ende des Linienpunkts aus, indem Sie es mit dem Diagramm verbinden und nur Punkte beibehalten, die genau einen Eintrag im Diagramm haben.
eol as (
SELECT points.* FROM
points JOIN
(SELECT id, count(*) FROM graph
GROUP BY id
HAVING count(*)= 1) sel
ON points.id = sel.id),
Nennen wir diese Tabelle "eol" (Zeilenende)
einfach? dass die Belohnung für das Erstellen eines großartigen Diagramms, aber des Festhaltens, im nächsten Schritt verrückt wird
Richten Sie eine rekursive Abfrage ein, die ab jedem Eol von Nachbarn zu Nachbarn wechselt
- Initialisieren Sie die rekursive Abfrage mithilfe der Tabelle eol und fügen Sie einen Zähler für die Tiefe, einen Aggregator für den Pfad und einen Geometriekonstruktor zum Erstellen der Linien hinzu
- Fahren Sie mit der nächsten Iteration fort, indem Sie mithilfe des Diagramms zum nächsten Nachbarn wechseln und sicherstellen, dass Sie den Pfad niemals rückwärts verwenden
- Nachdem die Iteration abgeschlossen ist, behalten Sie nur den längsten Pfad für jeden Startpunkt bei (wenn Ihr Datensatz einen potenziellen Schnittpunkt zwischen Erwartungslinien enthält, würde dieser Teil mehr Bedingungen benötigen).
recurse_eol (id, link_id, depth, path, start_id, geom) AS (--initialisation
SELECT id, link_id, depth, path, start_id, geom FROM (
SELECT eol.id, graph.link_id,1 as depth,
ARRAY[eol.id, graph.link_id] as path,
eol.id as start_id,
graph.geom as geom,
(row_number() OVER (PARTITION BY eol.id ORDER BY distance asc))=1 as test
FROM eol JOIn graph ON eol.id = graph.id
) foo
WHERE test = true
UNION ALL ---here start the recursive part
SELECT id, link_id, depth, path, start_id, geom FROM (
SELECT graph.id, graph.link_id, r.depth+1 as depth,
path || graph.link_id as path,
r.start_id,
ST_union(r.geom,graph.geom) as geom,
(row_number() OVER (PARTITION BY r.id ORDER BY distance asc))=1 as test
FROM recurse_eol r JOIN graph ON r.link_id = graph.id AND NOT graph.link_id = ANY(path)) foo
WHERE test = true AND depth < 1000), --this last line is a safe guard to stop recurring after 1000 run adapt it as needed
Nennen wir diese Tabelle "recurse_eol".
Behalten Sie für jeden Startpunkt nur die längste Linie bei und entfernen Sie jeden exakten doppelten Pfad. Beispiel: Die Pfade 1, 2, 3, 5 und 5, 3, 2, 1 sind dieselbe Linie, die durch ihre zwei unterschiedlichen "Zeilenende" entdeckt wird.
result as (SELECT start_id, path, depth, geom FROM
(SELECT *,
row_number() OVER (PARTITION BY array(SELECT * FROM unnest(path) ORDER BY 1))=1 as test_duplicate,
(max(depth) OVER (PARTITION BY start_id))=depth as test_depth
FROM recurse_eol) foo
WHERE test_depth = true AND test_duplicate = true)
SELECT * FROM result
Überprüft manuell verbleibende Fehler (isolierte Punkte, überlappende Linien, seltsam geformte Straße)
Wie versprochen aktualisiert, kann ich immer noch nicht herausfinden, warum rekursive Abfragen manchmal nicht genau dasselbe Ergebnis liefern, wenn sie von der gegenüberliegenden EOL derselben Zeile ausgehen, sodass möglicherweise ab sofort ein Duplikat in der Ergebnisebene verbleibt.
Fühlen Sie sich frei zu fragen, ich verstehe total, dass dieser Code mehr Kommentare benötigt. Hier ist die vollständige Abfrage:
WITH RECURSIVE
points as (SELECT id, st_transform((st_dump(wkb_geometry)).geom,2154) as geom, my_comment as com FROM mypoints),
roads as (SELECT st_transform(ST_union(wkb_geometry),2154) as geom from highway),
graph_full as (
SELECT a.id, b.id as link_id, a.com, st_makeline(a.geom,b.geom) as geom, st_distance(a.geom,b.geom) as distance
FROM points a
LEFT JOIN points b ON a.id<>b.id
WHERE st_distance(a.geom,b.geom) <= 15
),
graph as (
SELECt graph_full.*
FROM graph_full RIGHT JOIN
roads ON st_intersects(graph_full.geom,roads.geom) = false
),
eol as (
SELECT points.* FROM
points JOIN
(SELECT id, count(*) FROM graph
GROUP BY id
HAVING count(*)= 1) sel
ON points.id = sel.id),
recurse_eol (id, link_id, depth, path, start_id, geom) AS (
SELECT id, link_id, depth, path, start_id, geom FROM (
SELECT eol.id, graph.link_id,1 as depth,
ARRAY[eol.id, graph.link_id] as path,
eol.id as start_id,
graph.geom as geom,
(row_number() OVER (PARTITION BY eol.id ORDER BY distance asc))=1 as test
FROM eol JOIn graph ON eol.id = graph.id
) foo
WHERE test = true
UNION ALL
SELECT id, link_id, depth, path, start_id, geom FROM (
SELECT graph.id, graph.link_id, r.depth+1 as depth,
path || graph.link_id as path,
r.start_id,
ST_union(r.geom,graph.geom) as geom,
(row_number() OVER (PARTITION BY r.id ORDER BY distance asc))=1 as test
FROM recurse_eol r JOIN graph ON r.link_id = graph.id AND NOT graph.link_id = ANY(path)) foo
WHERE test = true AND depth < 1000),
result as (SELECT start_id, path, depth, geom FROM
(SELECT *,
row_number() OVER (PARTITION BY array(SELECT * FROM unnest(path) ORDER BY 1))=1 as test_duplicate,
(max(depth) OVER (PARTITION BY start_id))=depth as test_depth
FROM recurse_eol) foo
WHERE test_depth = true AND test_duplicate = true)
SELECT * FROM result