Сігналы абмеркавання
Ви можете використовувати рекурсивний запит для вивчення найближчого сусіда кожної точки, починаючи з кожного виявленого кінця рядків, які ви хочете побудувати.
Необхідні умови : підготуйте постгігічний шар із вашими точками та ще один з одним об'єктом багаторядкових рядків, що містить ваші дороги. Два шари повинні бути на одній CRS. Ось код для створеного нами тестового набору даних, будь ласка, змініть його за потребою. (Випробувано на postgres 9.2 та 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),
Ось такі кроки :
Створіть для кожного пункту список усіх сусідів та їх відстань, які відповідають цим критеріям.
- Відстань не повинна перевищувати визначений користувачем поріг (це дозволить уникнути посилання на окрему точку)
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),
Назвемо цю таблицю "еол" (кінець рядка)
простою? що винагорода за відмінний графік, але затримані речі піде на наступний крок
Налаштуйте рекурсивний запит, який буде переходити від сусідів до сусідів, починаючи з кожного еолу
- Ініціалізуйте рекурсивний запит за допомогою таблиці eol та додайте лічильник глибини, агрегатор для шляху та конструктор геометрії для побудови ліній
- Перейдіть до наступної ітерації, перейшовши на найближчого сусіда за допомогою графіка і перевіривши, що ви ніколи не повернетесь назад, використовуючи шлях
- Після завершення ітерації збережіть лише найдовший шлях для кожної початкової точки (якщо ваш набір даних включає потенційне перетин між очікуваними лініями, для цієї частини знадобиться більше умов)
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
Назвемо цю таблицю "recurse_eol"
Зберігайте лише найдовший рядок для кожної початкової точки та видаляйте кожен точний повторюваний шлях Приклад: шляхи 1,2,3,5 та 5,3,2,1 - це той самий рядок, який виявлено двома різними "кінцем рядка"
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
Вручну перевіряє залишилися помилки (поодинокі точки, лінії, що перекриваються, вулиця дивовижної форми)
Оновлений, як було обіцяно, я досі не можу з’ясувати, чому іноді рекурсивний запит не дає точно такого ж результату, починаючи з протилежного еолу того ж рядка, тому певний дублікат може залишатися в шарі результатів, як і зараз.
Не соромтеся запитати, я повністю розумію, що цей код потребує більше коментарів. Ось повний запит:
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