PostGISで特定の距離にあるポイント間に線を引きますか?


9

通り沿いのポイントのデータがあります。それらの点を単純な色付きの線に変えたいと思います。この問題が何と呼ばれるか、またはこれを解決するのに役立つアルゴリズムはありますか? 通り沿いのポイントを線に変えたい。

PostGISこれを行うために関数を使用することを望んでいましたが、提案を受け入れることが.shpできます。これはファイルのデータです。

Edit1:この問題の理想的なソリューションを示すために画像を更新しました。

線を描くことは、純粋にそれらの点の間の距離に基づいています。それらをグループ化するために使用できるものは他にありません。理想的には、これは投影された線に沿って指定された最大距離のポイントになるでしょうか?そして、投影された線とは、最初の点を見つけ、次にそれに最も近い点を見つけて線を投影し、この線上にすでに線上にある点の最大距離にある点があるかどうかを確認することを意味します。


1
どのソフトウェアを使用する予定ですか?
ArMoraer 2016

これらを歩道に変えようとしていますか?
DPSSpatial 16

これを行うためにPostGIS関数を使用することを望んでいましたが、私は提案を受け入れることができます。これは.shpファイルからのデータです。
マハーカーラ

1
図面または別の図面で接続するポイントを正確に示していただけますか?一度に2点だけですか?または3つですか?接続する必要があるポイント間の距離は常に同じですか、それとも特定のしきい値を「ちょうど」下回っていますか?
PeterHorsbøllMøller2016

1
@dbastonとMarHoffの両方に感謝します。4月下旬までアイデアをテストする時間はありません。賞金を分割できればいいのですが、これを1人に授与する必要があります。dbastonもいくつかのクエリを調べましただから私は彼の反応を受け入れます。回答に時間を費やしたすべての人に感謝!:-)の一部となる素晴らしいコミュニティ
Mahakala

回答:


8

再帰クエリを使用して、構築するラインの検出された各端から開始して、各ポイントの最近傍を探索できます。

前提条件:ポイントを含むpostgisレイヤーと、道路を含む1つのMulti-linestringオブジェクトを含むレイヤーを準備します。2つの層は同じ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. 各ポイントについて、これら3つの基準を満たすすべての隣人とその距離のリストを生成します。

    • 距離はユーザー定義のしきい値を超えてはなりません(これにより、孤立したポイントへのリンクが回避されます) ここに画像の説明を入力してください
      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. グラフに結合し、グラフ内のエントリが1つだけあるポイントのみを保持して、ラインの終点を選択します。 ここに画像の説明を入力してください

    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"(行末)と
    簡単に呼びましょう。優れたグラフを作成することに対する報酬は次のステップで手に負えなくなることになる

  3. 各eolから開始してネイバーからネイバーに循環する再帰クエリを設定する ここに画像の説明を入力してください

    • 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は、2つの異なる「線の終わり」によって検出された同じ線です

    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. 残りのエラー(孤立したポイント、重複する線、奇妙な形のストリート)を手動でチェックします


約束どおりに更新されましたが、同じ行の反対のEOLから開始したときに再帰クエリがまったく同じ結果を返さない場合があるので、現時点で結果レイヤーに重複が残っている理由を理解できません。

このコードにはもっとコメントが必要だと私は完全に理解しています。以下は完全なクエリです。

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

@MarHoffありがとう 本物に見える:)
Mahakala

できました。パズルをありがとう、そして怒って申し訳ありません。他の答えがあなたのためにそれをしたなら、それはいつか完全に大丈夫ですいつか単純なのが最善です...私の答えは多分それを少し考えすぎていました。CTE +再帰クエリ+ Windows関数+
postgisを

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:はい、カーブした道路が問題になりますが、私はほとんどのデータを自動的に変換しようとしているため、残りは手動で行う必要があります。または、トピックをさらに掘り下げて解決策を見つけますが、誰かに残りのデータを修正させるよりも時間がかかる場合があります。決定できるように結果を評価する必要があります。これを指摘してくれてありがとう!
Mahakala

Statutが調整した私はチェックアウトする必要があるトリックを考えました...
MarHoff

ポイントの可能なすべての順序付けを試行し、どれが最短の全長を与えるかを見つけることを含まない、これを行うための堅牢な方法はありますか?
dbaston

これらのポイントのセットが常に道路に沿っている場合は、ポイントの位置を道路セグメント(ST_Line_Locate_Point)に投影し、結果に基づいてポイントを並べ替えます。
トラビス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.