Vẽ đường giữa các điểm ở khoảng cách cụ thể trong PostGIS?


9

Tôi có một dữ liệu về các điểm dọc theo đường phố, tôi muốn biến những chấm đó thành những đường màu đơn giản. Bất kỳ con trỏ nào vấn đề này có thể được gọi hoặc bất kỳ thuật toán nào có thể giúp tôi giải quyết vấn đề này? Điểm dọc theo đường tôi muốn biến thành dòng.

Tôi đã hy vọng sử dụng các PostGISchức năng để làm điều này nhưng tôi mở để đề xuất, đây là dữ liệu từ một .shptệp.

Edit1: Cập nhật hình ảnh để chứng minh giải pháp lý tưởng cho vấn đề này.

Vẽ đường thẳng sẽ hoàn toàn dựa trên khoảng cách giữa các điểm đó, không có gì khác tôi có thể sử dụng để nhóm chúng theo. Lý tưởng nhất sẽ là điểm ở khoảng cách tối đa được chỉ định dọc theo đường chiếu? Và theo dòng được chiếu, tôi có nghĩa là tìm điểm thứ 1 rồi đến điểm tiếp theo gần nhất, sau đó chiếu một dòng và kiểm tra xem có điểm nào trên dòng này ở khoảng cách tối đa với bất kỳ điểm nào trên dòng không.


1
Phần mềm nào bạn định sử dụng?
ArMoraer

bạn đang cố gắng biến những thứ này thành vỉa hè?
DPSSpatial

Tôi đã hy vọng sử dụng các hàm PostGIS để làm điều này nhưng tôi mở để đề xuất, đây là dữ liệu từ tệp .shp.
Mahakala

1
Bạn có thể hiển thị chính xác những điểm bạn muốn kết nối trên bản vẽ của bạn hoặc trên bản vẽ khác không? Có phải chỉ có hai điểm tại một thời điểm? Hay ba? Là khoảng cách giữa các điểm nên được kết nối luôn giống nhau hay là "chỉ" dưới một ngưỡng nhất định?
Peter Horsbøll Møller

1
Cảm ơn cả @dbaston và MarHoff, tôi sẽ không có thời gian để kiểm tra ý tưởng của bạn cho đến cuối tháng 4, tôi ước tôi có thể chia tiền thưởng giữa nhưng tôi sẽ cần trao giải thưởng này cho 1 bạn và dbaston đã cho tôi một số truy vấn vì vậy tôi sẽ chấp nhận phản hồi của anh ấy Thx tất cả những người đã dành thời gian để trả lời! Cộng đồng tuyệt vời là một phần của :-)
Mahakala

Câu trả lời:


8

Hay nói, là một tài tài của, qua, qua, qua một tài khác, qua giữ, qua một tài khác

Bạn có thể sử dụng truy vấn đệ quy để khám phá hàng xóm gần nhất của từng điểm bắt đầu từ mỗi đầu dòng được phát hiện mà bạn muốn xây dựng.

Điều kiện tiên quyết : chuẩn bị một lớp postgis với các điểm của bạn và một lớp khác với một đối tượng Multi-linestring duy nhất chứa đường của bạn. Hai lớp phải nằm trên cùng một CRS. Đây là mã cho tập dữ liệu thử nghiệm mà tôi đã tạo, vui lòng sửa đổi nó nếu cần. (Đã thử nghiệm trên postgres 9.2 và 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),

nhập mô tả hình ảnh ở đây

Dưới đây là các bước :

  1. Tạo cho mỗi điểm danh sách của mọi hàng xóm và khoảng cách của họ đáp ứng ba tiêu chí.

    • Khoảng cách không được vượt quá ngưỡng do người dùng xác định (điều này sẽ tránh liên kết đến điểm bị cô lập) nhập mô tả hình ảnh ở đây
      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
      ),
      
    • Đường đi trực tiếp không được qua đường nhập mô tả hình ảnh ở đây
      graph as (
      SELECt graph_full.*
      FROM graph_full RIGHT JOIN
      roads ON st_intersects(graph_full.geom,roads.geom) = false
      ),
      
    • Khoảng cách không được vượt quá tỷ lệ do người dùng xác định khoảng cách từ hàng xóm gần nhất (điều này sẽ phù hợp với số hóa không đều hơn khoảng cách cố định) Phần này thực sự quá khó để thực hiện, bám vào bán kính tìm kiếm cố định

    Hãy gọi bảng này là "biểu đồ"

  2. Chọn điểm cuối của dòng bằng cách nối vào biểu đồ và chỉ giữ lại điểm có chính xác một mục trong biểu đồ. nhập mô tả hình ảnh ở đây

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

    Hãy gọi bảng này là "eol" (cuối dòng) có
    dễ không? rằng phần thưởng cho việc thực hiện một biểu đồ tuyệt vời nhưng mọi thứ sẽ tiếp tục phát điên ở bước tiếp theo

  3. Thiết lập một truy vấn đệ quy sẽ chuyển từ hàng xóm sang hàng xóm bắt đầu từ mỗi eol nhập mô tả hình ảnh ở đây

    • Khởi tạo truy vấn đệ quy bằng bảng eol và thêm bộ đếm cho độ sâu, bộ tổng hợp cho đường dẫn và hàm tạo hình học để xây dựng các dòng
    • Chuyển sang lần lặp tiếp theo bằng cách chuyển sang hàng xóm gần nhất bằng biểu đồ và kiểm tra xem bạn có bao giờ đi lùi bằng đường dẫn không
    • Sau khi lặp lại xong, chỉ giữ đường dẫn dài nhất cho mỗi điểm bắt đầu (nếu tập dữ liệu của bạn bao gồm giao điểm tiềm năng giữa các dòng mong đợi mà phần đó sẽ cần nhiều điều kiện hơn)
    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
    

    Hãy gọi bảng này là "recurse_eol"

  4. Chỉ giữ dòng dài nhất cho mỗi điểm bắt đầu và xóa mọi đường dẫn trùng lặp chính xác Ví dụ: đường dẫn 1,2,3,5 VÀ 5,3,2,1 là cùng một dòng được phát hiện bởi hai điểm khác biệt "cuối dòng"

    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. Kiểm tra thủ công các lỗi còn lại (các điểm bị cô lập, các đường chồng chéo, đường có hình dạng kỳ lạ)


Được cập nhật như đã hứa, tôi vẫn không thể hiểu tại sao đôi khi truy vấn đệ quy không cho kết quả chính xác khi bắt đầu từ eol đối diện của cùng một dòng để một số trùng lặp có thể vẫn còn trong lớp kết quả như bây giờ.

Hãy hỏi tôi hoàn toàn hiểu rằng mã này cần thêm ý kiến. Đây là truy vấn đầy đủ:

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


Xin chào @MarHoff, cảm ơn câu trả lời của bạn, tôi có vài thứ phải theo dõi. Tôi không mong đợi một giải pháp đầy đủ, chỉ là một gợi ý nơi để tìm câu trả lời. Tôi muốn hiểu điều này nhiều hơn và tôi sẽ tiếp tục đào và có thể có nhiều câu hỏi sau này. Tôi cần hiểu thuật toán của bạn và điều này sẽ khiến tôi mất chút thời gian :)
Mahakala

Có một kịch bản hoạt động, xem trước tại đây qgiscloud.com/MarHoff/test_qgiscloud_bis một cảnh báo nhỏ cho việc sao chép vẫn còn ... Câu đố này rất thú vị
MarHoff

cảm ơn bạn @MarHoff, nếu tôi có thể chia tiền thưởng này, tôi không thể thấy làm thế nào tôi có thể trao cho bạn bất kỳ điểm nào, nhưng cảm ơn rất nhiều vì đã xem xét điều này và bằng chứng của bạn. Trông thật đấy :)
Mahakala

Làm xong. Cảm ơn vì câu đố, và xin lỗi vì đã ca ngợi. Nếu câu trả lời khác đã làm điều đó cho bạn thì đôi khi hoàn toàn đơn giản là tốt nhất ... Câu trả lời của tôi có lẽ hơi bị lật đổ. Mặc dù ví dụ hay về việc sử dụng CTE + truy vấn đệ quy + chức năng Windows + postgis trên một truy vấn duy nhất;)
MarHoff

8

Như @FelixIP chỉ ra, bước đầu tiên là tìm các điểm sẽ tạo thành mỗi dòng. Bạn có thể làm điều này bằng cách gọi ST_ClusterWithin với khoảng cách tách tối đa của bạn:

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

Sau đó, bạn sẽ cần sử dụng một số heuristic để xây dựng một dòng thông qua tất cả các điểm trong mỗi cụm. Ví dụ: nếu bạn có thể giả sử các dòng mong muốn là Y-monotone, bạn có thể sắp xếp các điểm trong mỗi cụm và đưa chúng vào ST_MakeLine . Kết hợp tất cả lại với nhau sẽ như thế này:

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

Cách để đi nhưng cách tiếp cận Y-monotone (hoặc thậm chí chuyển đổi giữa X / Y-monotone) sẽ không hoạt động tốt nếu tập dữ liệu chứa đường cong. Có phải vậy không? Thuật toán đặt hàng là phần khó nhất của câu hỏi này IMHO.
MarHoff

@MarHoff: có đường cong sẽ là một vấn đề, nhưng tôi đang cố gắng để phần lớn dữ liệu được chuyển đổi tự động và phần còn lại sẽ cần phải được thực hiện thủ công. Hoặc tôi sẽ tiếp tục đào sâu vào chủ đề để tìm ra giải pháp nhưng có thể mất nhiều thời gian hơn là nhờ ai đó sửa dữ liệu còn lại. Tôi sẽ cần đánh giá kết quả để có thể quyết định. Thx đã chỉ ra điều này!
Mahakala

Statut điều chỉnh Tôi chỉ nghĩ về một mẹo tôi cần kiểm tra ...
MarHoff

Có cách nào mạnh mẽ để làm điều này không liên quan đến việc thử tất cả các thứ tự điểm có thể và tìm ra điểm nào cho tổng chiều dài ngắn nhất không?
dbaston

Nếu các nhóm điểm này luôn đi theo các đường, bạn chiếu vị trí của điểm lên đoạn đường (ST_Line_Locate_Point), sau đó sắp xếp các điểm theo kết quả.
ngày
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.