Anda dapat menggunakan kueri rekursif untuk menjelajahi tetangga terdekat dari setiap titik mulai dari setiap ujung garis yang terdeteksi yang ingin Anda buat.
Prasyarat : siapkan layer postgis dengan poin Anda dan poin lain dengan objek multi-linestring tunggal yang berisi jalan Anda. Dua lapisan harus berada pada CRS yang sama. Ini adalah kode untuk set data uji yang saya buat, harap modifikasi sesuai kebutuhan. (Diuji pada postgres 9.2 dan 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),
Berikut langkah-langkahnya :
Buat untuk setiap titik daftar setiap tetangga dan jarak mereka yang memenuhi tiga kriteria ini.
- Jarak tidak boleh melebihi ambang batas yang ditentukan pengguna (ini akan menghindari menghubungkan ke titik yang terisolasi)
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
),
- Jalur langsung tidak boleh melewati jalan
graph as (
SELECt graph_full.*
FROM graph_full RIGHT JOIN
roads ON st_intersects(graph_full.geom,roads.geom) = false
),
Jarak tidak boleh melebihi rasio jarak yang ditentukan pengguna dari tetangga terdekat (ini harus mengakomodasi lebih baik digitalisasi tidak teratur daripada jarak tetap) Bagian ini sebenarnya terlalu sulit untuk diterapkan, melekat pada radius pencarian tetap
Sebut saja tabel ini "grafik"
Pilih ujung titik garis dengan bergabung ke grafik dan pertahankan hanya titik yang memiliki satu entri persis dalam grafik.
eol as (
SELECT points.* FROM
points JOIN
(SELECT id, count(*) FROM graph
GROUP BY id
HAVING count(*)= 1) sel
ON points.id = sel.id),
Sebut saja tabel ini "eol" (end of line) dengan
mudah? bahwa hadiah untuk melakukan grafik yang bagus tetapi hal-hal yang bertahan akan menjadi gila pada langkah berikutnya
Menyiapkan kueri rekursif yang akan berputar dari tetangga ke tetangga mulai dari setiap hari
- Inisialisasi kueri rekursif menggunakan tabel eol dan menambahkan penghitung untuk kedalaman, agregator untuk path, dan konstruktor geometri untuk membangun garis
- Pindah ke iterasi berikutnya dengan beralih ke tetangga terdekat menggunakan grafik dan periksa bahwa Anda tidak pernah mundur menggunakan jalur
- Setelah iterasi selesai, simpan hanya jalur terpanjang untuk setiap titik awal (jika dataset Anda menyertakan persimpangan potensial antara garis ekspektasi, bagian itu akan membutuhkan lebih banyak kondisi)
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
Sebut saja tabel ini "recurse_eol"
Pertahankan hanya garis terpanjang untuk setiap titik awal dan hapus setiap jalur duplikat yang tepat Contoh: jalur 1,2,3,5 DAN 5,3,2,1 adalah garis yang sama yang ditemukan oleh dua perbedaan "ujung jalur"
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
Secara manual memeriksa kesalahan yang tersisa (titik terisolasi, garis yang tumpang tindih, jalan berbentuk aneh)
Diperbarui seperti yang dijanjikan, saya masih tidak tahu mengapa kadang-kadang permintaan rekursif tidak memberikan hasil yang sama persis ketika mulai dari lawan dari baris yang sama sehingga beberapa duplikat mungkin tetap ada di lapisan hasil seperti sekarang.
Jangan ragu untuk bertanya, saya benar-benar mengerti bahwa kode ini membutuhkan lebih banyak komentar. Ini pertanyaan lengkapnya:
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