Проведення лінії між точками на конкретній відстані в PostGIS?


9

У мене є дані про точки по вулицях, я хотів би перетворити ці крапки в прості кольорові лінії. Будь-які вказівки, як можна назвати цю проблему, або алгоритми, які можуть допомогти мені вирішити цю проблему? Точки вздовж вулиці я хотів би перетворити на лінії.

Я сподівався використовувати PostGISфункції для цього, але я відкритий для пропозицій, це дані з .shpфайлу.

Edit1: оновлено зображення, щоб продемонструвати ідеальне рішення цієї проблеми.

Нанесення лінії буде суто базуватися на відстані між цими точками, я не можу використати їх для групування. В ідеалі це були б точки на максимальній заданій відстані вздовж прогнозованої лінії? І під проектованою лінією я маю на увазі знайти 1-ю точку, а потім наступну найближчу до неї, а потім спроектувати лінію та перевірити, чи є точки на цій лінії на максимальній відстані до будь-якої з тих, що вже знаходяться на лінії.


1
Яке програмне забезпечення ви плануєте використовувати?
ArMoraer

ти намагаєшся перетворити їх на тротуари?
DPSSpace 04

Я сподівався використати функції PostGIS для цього, але я відкритий для пропозицій, це дані з .shp-файлу.
Махакала

1
Чи можете ви точно показати, які точки ви хочете з'єднати на своєму малюнку чи на іншому малюнку? Це лише два моменти одночасно? Або три? Чи завжди відстань між точками, які слід з'єднати, однакове або це "просто" нижче певного порогу?
Пітер Хорсбёлл Мёллер

1
Велике спасибі і @dbaston, і MarHoff, я не встигну перевірити ваші ідеї до кінця квітня, я б хотів, щоб я міг розділити щедроту між собою, але мені потрібно присвоїти це 1 з вас, і dbaston дав мені кілька запитів. тому я прийму його відповідь. Thx всі, хто потребував часу, щоб відповісти! Велике співтовариство, яке має бути частиною :-)
Махакала

Відповіді:


8

Сігналы абмеркавання

Ви можете використовувати рекурсивний запит для вивчення найближчого сусіда кожної точки, починаючи з кожного виявленого кінця рядків, які ви хочете побудувати.

Необхідні умови : підготуйте постгігічний шар із вашими точками та ще один з одним об'єктом багаторядкових рядків, що містить ваші дороги. Два шари повинні бути на одній 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),

введіть тут опис зображення

Ось такі кроки :

  1. Створіть для кожного пункту список усіх сусідів та їх відстань, які відповідають цим критеріям.

    • Відстань не повинна перевищувати визначений користувачем поріг (це дозволить уникнути посилання на окрему точку) введіть тут опис зображення
      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
      ),
    • Відстань не повинна перевищувати визначене користувачем співвідношення відстані від найближчого сусіда (це має відповідати краще нерегулярній оцифровці, ніж фіксована відстань) Ця частина насправді була занадто важкою для реалізації, дотримувалася фіксованого радіуса пошуку.

    Назвемо цю таблицю "графіком"

  2. Виберіть кінцеву точку рядка, приєднавшись до графіка, зберігаючи лише одну точку, яка має точно один запис у графіку. введіть тут опис зображення

    eol as (
    SELECT points.* FROM
    points  JOIN
    (SELECT id, count(*) FROM graph 
    GROUP BY id
    HAVING count(*)= 1) sel
    ON points.id = sel.id),

    Назвемо цю таблицю "еол" (кінець рядка)
    простою? що винагорода за відмінний графік, але затримані речі піде на наступний крок

  3. Налаштуйте рекурсивний запит, який буде переходити від сусідів до сусідів, починаючи з кожного еолу введіть тут опис зображення

    • Ініціалізуйте рекурсивний запит за допомогою таблиці 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"

  4. Зберігайте лише найдовший рядок для кожної початкової точки та видаляйте кожен точний повторюваний шлях Приклад: шляхи 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
  5. Вручну перевіряє залишилися помилки (поодинокі точки, лінії, що перекриваються, вулиця дивовижної форми)


Оновлений, як було обіцяно, я досі не можу з’ясувати, чому іноді рекурсивний запит не дає точно такого ж результату, починаючи з протилежного еолу того ж рядка, тому певний дублікат може залишатися в шарі результатів, як і зараз.

Не соромтеся запитати, я повністю розумію, що цей код потребує більше коментарів. Ось повний запит:

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

Привіт @MarHoff, дякую за твою відповідь. Мені потрібно щось піти. Я не сподівався на повне рішення, просто на вказівки, де шукати відповіді. Я хочу це більше зрозуміти, і я буду продовжувати копати і, мабуть, матиму більше запитань пізніше. Мені потрібно зрозуміти ваш алгоритм, і це все одно займе у мене час :)
Mahakala

Отримав робочий скрипт, попередній перегляд тут qgiscloud.com/MarHoff/test_qgiscloud_bis невеликий застереження для дедуплікації залишається ... Більше не банально, ні більше тиску я здогадуюсь, тож я випущу реліз, коли зможу. Ця головоломка була
цікавою,

дякую @MarHoff, якби я міг би розділити цю нагороду, я не бачу, як я можу нагородити тебе будь-якими очками, але велике спасибі за те, що вивчив це та твій доказ. Виглядає справжньо :)
Махакала

Зроблено. Дякуємо за головоломку, і вибачте за те, що орендуєте. Якщо інша відповідь зробила це для вас, тоді це все нормально, коли-небудь просто, найкраще ... Моя відповідь, можливо, була трохи переосмисленою. Хоча приємний приклад використання CTE + рекурсивного запиту + функції Windows + постгігів на одному запиті;)
MarHoff

8

Як вказує @FelixIP, першим кроком є ​​пошук точок, які становлять кожен рядок. Це можна зробити, зателефонувавши до ST_ClusterWithin з максимальною відстані в роздільній відстані:

SELECT
  row_number() OVER () AS cid, 
  (ST_Dump(geom)).geom 
FROM (
  SELECT unnest(st_clusterwithin(geom, 0.05)) AS geom 
  FROM inputs) sq

Тоді вам потрібно буде використовувати евристику для побудови лінії через усі точки кожного кластеру. Наприклад, якщо ви можете вважати, що потрібні лінії є однотонними Y, ви можете сортувати точки у кожному кластері та подавати їх у ST_MakeLine . Поєднавши, що все разом виглядатиме так:

SELECT 
  ST_MakeLine(geom ORDER BY ST_Y(geom)) AS geom
FROM (
  SELECT row_number() OVER () AS cid, 
  (ST_Dump(geom)).geom FROM (
    SELECT unnest(st_clusterwithin(geom, 0.05)) AS geom 
    FROM inputs) sq) ssq 
GROUP BY cid

Шлях, але Y-монотонний (або навіть перемикання між X / Y-монотонним) підходом не працюватиме добре, якщо набір даних містить вигнуту дорогу. Це так? Алгоритм замовлення є найважчою частиною цього питання ІМХО.
MarHoff

@MarHoff: так, криві дороги будуть проблемою, але я намагаюся зробити більшість даних перетвореними автоматично, а відпочинок потрібно буде зробити вручну. Або я продовжую розбиратися в цій темі більше, щоб з’ясувати рішення, але це може зайняти більше часу, ніж те, щоб хтось зафіксував залишилися дані. Мені потрібно оцінити результати, щоб мати можливість визначитися. Thx для вказівки на це!
Махакала

Статут налаштований Я просто подумав про трюк, який мені потрібно перевірити ...
MarHoff

Чи існує якийсь надійний спосіб зробити це, який не передбачає спробу всіх можливих упорядкувань балів, а також пошук того, який із них дає найменшу загальну довжину?
дбастон

Якщо ці набори точок завжди слідують за дорогами, ви проектуєте позицію точки на відрізок дороги (ST_Line_Locate_Point), а потім упорядковуєте точки за результатом.
travis
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.