QGISで特定の距離にランダムにポイントを再配置

Nov 27 2020

私はアフリカの援助プロジェクトを特定するGPS座標を持っています。ここで、これらのプロジェクトを再配置して、各プロジェクトが元の場所からランダムな方向に100kmずつ再配置されるようにします。

(追加:国レイヤーがあります。QGISを元の国内にのみ再配置するように強制できますか?)

Virtual Layer&Geometry Generatorを試しましたが、どちらも今のところ機能しませんでした。リードはありますか?

私はQGIS3.14.15を使用しています。

回答

14 babel Nov 27 2020 at 22:42

この回答は、式を徐々に複雑にするソリューションを取得するために、数回更新されています。最終的な解決策については、一番下に直接移動する必要があります(ステップ5)。ただし、元の回答を更新した履歴は、非常に簡単な方法から始めて、このソリューションがどのように機能するかについてのステップバイステップガイドになります。それは、より複雑なQGIS式がどのように機能し、それで何が達成できるかについての洞察を与えます。複雑さの各ステップは水平方向のルールで区切られ、ステップ1、ステップ2 ...からステップ5という名前が付けられているため、これらのステップに簡単にジャンプできます。ステップ4は、式のさまざまな部分がどのように組み合わされるかについての詳細な説明です。


ステップ1

これを行うには、次の式を使用します(国内に再配置するための追加情報については、以下を参照してください)。

project ( $geometry , 100000 , radians ( rand ( 0,360) ) ) 

視覚化の目的でのみ必要な場合は、シンボルレイヤーを追加し、geoemtryジェネレーターとして定義して、上記の式を貼り付けます。CRSの単位がメートルでない場合は、100000を変更します。パン、ズーム、または別のアクティビティを実行するたびに、レイヤーが更新され、ランダムなポイントが新しい場所に配置されることに注意してください。たとえば50〜100 kmのランダムな距離も必要な場合は、100000をに置き換えrand ( 50000, 100000)ます。

実際のジオメトリが必要な場合は、メニューprocessing / toolbox / geoemtry by expressionを使用して新しいレイヤーを生成し、同じ式を使用します。

結果:赤い点:オリジナル; 青い点:再配置されたポイント。

新しいジオメトリを作成せずに、元のポイントレイヤーのランダムポイントのx座標とy座標を作成する可能性があります。これを行うには、フィールド計算機を使用して、let'ssayx_coordinateとという名前の新しいフィールドを作成しますy_coordinate。式x(geometry)を使用してy(geometry)、ランダムに再配置されたポイントの座標を含むそれぞれに個別のフィールドを作成しますが、ジオメトリは再配置されたポイントを作成するための式です(下のスクリーンショットでは、下からより洗練された式を使用しました)。x- / y-座標が置き換えられた値を持つ2つの新しいフィールドを取得します。それらは永続的に保存され、変更/更新されなくなります(仮想フィールドを使用してコンテンツを変更できます)。

これで、次の式のシンボルレイヤーを使用して、このフィールドを使用して、再配置された位置にあるポイントを視覚化できます。

make_point(
   "x_coordinate" ,  
   "y_coordiante" 
)

この式は、次の画面で、置き換えられた白いポイントを生成します。必要に応じて、元の赤いポイントの元の表現を削除して、再配置されたポイントのみを保持できます。


ステップ2

国内でのみポイントを再配置するための洗練されたバージョン。同じ解決策ですが、上記の式の代わりに、ここでこれを使用します。'countries'国のポリゴンを含むレイヤーの名前に変更することに注意してください。両方のレイヤーとプロジェクトは同じCRS内にある必要があり、投影されたCRSを使用するのが最適です(地理的なCRSでは、距離の測定は意味がありません)。

ここでも、この層は、フィールドが必要です'fid'(一意の整数が含まれていない以前のバージョン、国名などの任意の一意のフィールド値に述べたように、)。'fid'次の式で、一意の整数を含む国レイヤーの任意のフィールド名に置き換えることができます。

with_variable ( 
   'boundary',
    boundary ( 
      intersection (
         buffer ( $geometry, 100000),
         geometry(
            get_feature( 
               'countries',
                'fid',
                to_int ( array_to_string (
                   overlay_within (
                      'countries', 
                      fid)
                 ))
            )
          )
       )
   ), 
   line_interpolate_point (
      @boundary,
      rand (
         0,
         length(@boundary)
      )
   )
)

ステップ3

そして、表現のさらなる完成度:上記のバージョンでは、元のポイントが境界の近く(<100 km)にある場合、再配置されたポイントは境界自体に配置されることがあり、したがって元のポイントから100km未満離れています。 。上記の式は、元のポイントの周りに円を作成し、現在の国と交差します。この交差点の外側の線を取り、この外側の境界にランダムなポイントを配置します。元のポイントが100 kmの距離内にある場合、この境界には国の境界のセグメントが含まれることがあります。

これを防ぐために、それに応じて式を変更し、異なる操作を追加しました。今説明した境界から、すべての国の境界をクリップ(抽出)します。残りの線(ポイントの現在の国内の元のバッファ円の部分のみが残ります)では、新しいポイントがランダムに配置されます。(すべての境界セグメントが完全にクリップされているわけではないため、式の改訂版については、この回答の下部を参照してください):

with_variable ( 
   'boundary',
    
    difference(
        boundary ( 
          intersection (
             buffer ( $geometry, 100000),
             geometry(
                get_feature( 
                   'countries',
                    'fid',
                    to_int ( array_to_string (
                       overlay_within (
                          'countries', 
                          fid)
                     ))
                )
              )
           )
      ), 
      boundary ( 
         aggregate ( 
            'countries', 
            'collect',
            $geometry)
      )),
   line_interpolate_point (
      @boundary,
      rand (
         0,
         length(@boundary)
      )
   )
)

ステップ4

式がどのように機能するかをよりよく理解するために、スクリーンショット付きの説明に従います。

  1. ポイントの周囲に100kmのバッファを作成します:(buffer ( $geometry, 100000)ポリゴン)

  1. 他のレイヤーから国のポリゴン形状を取得します。

式:

geometry(
   get_feature(
         'countries',
      'fid',
      to_int ( 
         array_to_string (
            overlay_within (
           'countries', 
                   fid)
         )
      )
   )
)

(ポリゴン)

  1. 1と2の交差点:現在の国(2)内にあるバッファー(1)の部分のみ

intersection ( a, b)、aは1からの式、bは2からの式

  1. 3の外側の境界(国内にあるバッファーの一部)を作成しますboundary (c)。一方、cは3(行)からの式です。

  1. 国レイヤーのポリゴン境界から国の境界を線として取得します。

式:

boundary ( 
       aggregate ( 
          'countries', 
          'collect',
          $geometry)
    )

(ライン)

  1. 4(国内のバッファの外側の境界)から国の境界(5)をクリップ(抽出)しますdifference(d, e)。一方、dは4からの式、eは5からの式です。

  1. ここで、noからライン上のどこかにランダムなポイントを設定します。6式でline_interpolate_point(geometry,distance)一方
  • ジオメトリは6からの表現です(したがって、国外にあり、国境のない部分を除く100 kmの円)
  • この線の距離はrand(min,max)、min = 0およびmax =このgeometry線の全長(6番から)の式を使用してランダムに作成されます。したがって、点は線の始点と終点の間のどこかにランダムに割り当てられます。
  1. 式の最後(または最初の部分)はwith_variable(name,value,expression)次のとおりです。これは変数を設定し、この関数内に1から7まで作成したすべての要素が一緒にあります。
  • A:変数の名前は(最初の引数)です'boundary'(Cで参照されます。以下を参照)。名前は任意であり、他の名前でもかまいません。

  • B:その値(2番目の引数)は、noで作成された式全体です。6(1から5までの要素を含む)、したがって、国境のない円線と

  • C:式(3番目の引数)は、line_interpolate_point noからの式です。入力ジオメトリ(Bからの値)を変数として取る7、 @boundary上記で定義された名前(Aを参照)(変数の名前の前の@文字は変数をアドレス指定するためのQGIS規則です)。

結果は次のようになります-青:id-labelの元のポイント、白:id-labelの再配置されたポイント:


ステップ5

そして(願わくば最後の)改善:私が今だけ気付いたように、場合によっては、ポイントが元のポイントから100km未満の境界にまだあることがあります。円が交差する境界セグメントはクリップされません。以下を参照してください。

これが私がこの質問をした背景です:QGIS式で別の線分と交差する線分の取得

しかし、完全を期すためにここに含める簡単な解決策があり、「最終的な」答えが得られます。ステップ4、パート5(国境を取得)では、国境を使用する代わりに、たとえば1メートルのバッファーを作成します。境界線の周りなので、式のこの部分は少し異なって見えます。

buffer (
   boundary ( 
      aggregate ( 
         'countries', 
         'collect',
          $geometry)
       ) ,
    1 
)

QGISは境界の周りのバッファを計算する必要があるため、マップイメージのレンダリングにはかなりの時間がかかることに注意してください。この最後の解決策は、を使用して、geometry by expression toolパンまたはズームするたびにレンダリングを開始するシンボルレイヤーではなく、すべての新しいジオメトリを1回だけ作成することをお勧めします。

式の数値1を増やすと、ランダムなポイントを境界からどれだけ離すかを制御できます。20000に設定すると、[メートルを単位として投影されたCRSを使用する場合はメートル]に設定すると、ポイントが再配置されます。元のポイントから100km離れたまま、境界から少なくとも20kmの距離が必要です。

したがって、これらのランダムな点を生成する最終的な式は次のとおりです。

with_variable ( 
   'boundary',

    difference(
        boundary ( 
          intersection (
             buffer ( $geometry, 100000),
             geometry(
                get_feature( 
                   'countries',
                    'fid',
                    to_int ( array_to_string (
                       overlay_within (
                          'countries', 
                          fid)
                     ))
                )
              )
           )
      ), 
      buffer (boundary ( 
         aggregate ( 
            'countries', 
            'collect',
            $geometry)
      ),1)),
   line_interpolate_point (
      @boundary,
      rand (
         0,
         length(@boundary)
      )
   )
)

それはすべての人々です!