ポリゴン内にあるポイントを生成する


30

ポリゴンフィーチャがあり、その中にポイントを生成できるようにします。これは1つの分類タスクに必要です。

ポリゴン内にランダムポイントを生成するのは、時間がかかるので予測できないため、機能しません。


3
それどころか、時間は予測可能です。これは、多角形の範囲の面積を多角形の面積で割った比に比例し、1つのポイントを生成してテストするのに必要な時間を掛けたものです。時間はわずかに変化しますが、変化はポイント数の平方根に比例します。大きな数字の場合、これは重要ではなくなります。必要に応じて曲がりくねったポリゴンをよりコンパクトなピースに分割し、この面積比を低い値に下げます。フラクタルポリゴンのみが問題を引き起こしますが、それらがあるとは思えません!
whuber



@パブロ:良い発見。しかし、これらの質問の両方は、ソフトウェアの特定のポリゴン内の点の通常の配列を配置する両方の心配はなく、ランダムなポイントである
whuber

whuberの違いは、ポリゴン内でのランダムポイントと通常のポイント生成の違いです。
マッパーズ

回答:


20

ポリゴンを三角形に分解してから、それらの内部にポイントを生成します。(均一な分布の場合、各三角形に面積で重み付けします。)


2
+1シンプルで効果的。三角形と二等辺直角三角形の間に(簡単に計算された)面積保存マッピングが存在するため、均一にランダムなポイントが拒否なしで三角形内に生成できることを指摘する価値があります。 y座標がx座標を超えています。2つのランダムな座標を生成し、それらを並べ替えて二等辺三角形のランダムポイントを取得し、それを元の三角形にマッピングします。
whuber

+1 引用した記事で参照されているトライリニア座標の議論が本当に好きです。これは、表面が三角形のテッセレーションとして表される球体に適していると思います。投影面では、それは本当にランダムな分布ではないでしょうか?
カーククイケンドール

@whuber-+1 別の方法(リンク内ではあるが、手を振った)は、共有されたエッジを横切って均一にサンプリングされた四角形から拒否されたポイントを反射し、三角形に戻すことです。
ダンS.

@Kirk-引用リンクは、「正しい」方法の前に、トライリニア座標を含む誤った(不均一な)サンプリング方法のリストを表示するという点で、反有用です。トリリニア座標で均一なサンプリングを取得する直接的な方法があるようには見えません。3Dのランダムな単位ベクトルをlat / lonの同等物に変換することにより、球全体で均一なサンプリングにアプローチしますが、それは私だけです。(球面三角形/多角形に制限されたサンプリングについては不明です。)(例えばwgs84での真に均一なサンプリングについても不明です:ちょうどピッキング角度は極に少し偏っていると思います。)
ダンS.

1
@Dan球体を均一にサンプリングするには、円筒形の等面積投影法を使用します(座標は経度と緯度の余弦です)。投影を使用せずにサンプリングする場合は、美しいトリックがあります。3つの独立した標準正規変量(x、y、z)を生成し、それらを点(R x / n、R y / n、R * z / n )に投影します)ここで、n ^ 2 = x ^ 2 + y ^ 2 + z ^ 2で、Rは地球の半径です。必要な場合は、(lat、lon)に変換します(回転楕円体で作業する場合に正規の緯度を使用)。この3変量正規分布は球対称であるため機能します。三角形をサンプリングするには、投影に固執します。
whuber

14

この質問にQGISタグを配置すると、境界層でランダムポイントツールを使用できます。

ここに画像の説明を入力してください

コードを探している場合は、基礎となるプラグインのソースコードが役立ちます。


1
5年後でも、まだ本当に役に立ちます!
座礁した子供

10

ポリゴンの範囲を決定し、その範囲内でXおよびY値の乱数生成を制限できます。

基本プロセス:1)多角形の頂点のmaxx、maxy、minx、minyを決定します。2)これらの値を境界として使用してランダムポイントを生成しますテスト

交差テストのアルゴリズム(C#)は次のとおりです。

bool PointIsInGeometry(PointCollection points, MapPoint point)
{
int i;
int j = points.Count - 1;
bool output = false;

for (i = 0; i < points.Count; i++)
{
    if (points[i].X < point.X && points[j].X >= point.X || points[j].X < point.X && points[i].X >= point.X)
    {
        if (points[i].Y + (point.X - points[i].X) / (points[j].X - points[i].X) * (points[j].Y - points[i].Y) < point.Y)
        {
            output = !output;
        }
    }
    j = i;
}
return output;
}

10

ほとんどの面倒な作業を行う優れたライブラリがいくつかあります。

Pythonで[shapely] [1]を使用した例。

import random
from shapely.geometry import Polygon, Point

def get_random_point_in_polygon(poly):
     minx, miny, maxx, maxy = poly.bounds
     while True:
         p = Point(random.uniform(minx, maxx), random.uniform(miny, maxy))
         if poly.contains(p):
             return p

p = Polygon([(0, 0), (0, 2), (1, 1), (2, 2), (2, 0), (1, 1), (0, 0)])
point_in_poly = get_random_point_in_polygon(mypoly)

または.representative_point()、オブジェクト内のポイントを取得するために使用します(dainによると)。

ジオメトリックオブジェクト内にあることが保証されている、安価に計算されたポイントを返します。

poly.representative_point().wkt
'POINT (-1.5000000000000000 0.0000000000000000)'

  [1]: https://shapely.readthedocs.io

2
shapely.geometryインポートからではないでしょうか...?
PyMapr

1
また、使用することができますrepresentative_point:メソッドshapely.readthedocs.io/en/latest/...を
デイン

6

Rがオプションの場合?spsamplespパッケージ内を参照してください。ポリゴンは、rgdalパッケージに組み込まれているGDALがサポートする形式から読み込むことができspsample、さまざまなサンプリングオプションを使用してインポートされたオブジェクトで直接機能します。


+1-Rはオープンソースであるため、複製したい場合はいつでもソースにアクセスして、それらがどのように行われるかを確認できます。点パターンについては、spatstatパッケージのシミュレーションツールにも興味があるかもしれません。
アンディW

5

GIS分析の点でほとんど必要としないソリューションを提供したいと思います。特に、ポリゴンを三角形分割する必要はありません。

擬似コードで指定された次のアルゴリズムは、基本的なリスト処理機能(作成、長さの検索、追加、並べ替え、サブリストの抽出、および連結)と間隔[0、1)でのランダムフロートの生成に加えて、いくつかの簡単な操作を指します:

Area:        Return the area of a polygon (0 for an empty polygon).
BoundingBox: Return the bounding box (extent) of a polygon.
Width:       Return the width of a rectangle.
Height:      Return the height of a rectangle.
Left:        Split a rectangle into two halves and return the left half.
Right:       ... returning the right half.
Top:         ... returning the top half.
Bottom:      ... returning the bottom half.
Clip:        Clip a polygon to a rectangle.
RandomPoint: Return a random point in a rectangle.
Search:      Search a sorted list for a target value.  Return the index  
             of the last element less than the target.
In:          Test whether a point is inside a polygon.

これらはすべて、ほぼすべてのGISまたはグラフィックスプログラミング環境で利用できます(そうでない場合はコーディングが簡単です)。 Clip縮退したポリゴン(つまり、面積がゼロのポリゴン)を返してはなりません。

手順SimpleRandomSampleは、多角形内にランダムに分布するポイントのリストを効率的に取得します。これは、のラッパーでありSRS、各ピースが十分にコンパクトになり、効率的にサンプリングされるまで、ポリゴンを小さなピースに分割します。これを行うには、事前に計算された乱数のリストを使用して、各ピースに割り当てるポイント数を決定します。

パラメーターを変更することで、SRSを「調整」できますt。これは、許容できる最大の境界ボックス:ポリゴン面積比です。小さく(ただし1より大きい)すると、ほとんどのポリゴンが多数の断片に分割されます。大きくすると、いくつかのポリゴンで多くの試行ポイントが拒否される可能性があります(連続、スライバー、または穴がいっぱい)。これにより、元のポリゴンをサンプリングする最大時間が予測可能になります。

Procedure SimpleRandomSample(P:Polygon, N:Integer) {
    U = Sorted list of N independent uniform values between 0 and 1
    Return SRS(P, BoundingBox(P), U)
}

次のプロシージャは、必要に応じて自分自身を再帰的に呼び出します。不思議な表現t*N + 5*Sqrt(t*N)は、必要なポイント数の上限を控えめに見積もっており、偶然の変動を考慮しています。これが失敗する可能性は、100万プロシージャコールあたり0.3だけです。必要に応じて、この可能性を減らすために5から6または7まで増やします。

Procedure SRS(P:Polygon, B:Rectangle, U:List) {
    N = Length(U)
    If (N == 0) {Return empty list}
    aP = Area(P)
    If (aP <= 0) {
        Error("Cannot sample degenerate polygons.")
        Return empty list
    }
    t = 2
    If (aP*t < Area(B)) {
        # Cut P into pieces
        If (Width(B) > Height(B)) {
            B1 = Left(B); B2 = Right(B)
        } Else {
            B1 = Bottom(B); B2 = Top(B)
        }
        P1 = Clip(P, B1); P2 = Clip(P, B2)
        K = Search(U, Area(P1) / aP)
        V = Concatenate( SRS(P1, B1, U[1::K]), SRS(P2, B2, U[K+1::N]) )
    } Else {
        # Sample P
        V = empty list
        maxIter = t*N + 5*Sqrt(t*N)
        While(Length(V) < N and maxIter > 0) {
            Decrement maxIter
            Q = RandomPoint(B)
            If (Q In P) {Append Q to V}
        }
       If (Length(V) < N) {
            Error("Too many iterations.")
       }
    }
    Return V
}

2

多角形が凸であり、すべての頂点を知っている場合、頂点の「ランダムな」凸重み付けを行って、凸包(この場合は多角形)内にあることが保証される新しいポイントをサンプリングすることを検討できます。

たとえば、頂点を持つN面の凸ポリゴンがあるとします

V_i, i={1,..,N}

次に、ランダムにN個の凸重みを生成します

 w_1,w_2,..,w_N such that  w_i = 1; w_i>=0

ランダムにサンプリングされたポイントは、次のように与えられます

Y=  w_i*V_i

N個の凸の重みをサンプリングする別の方法があります。

  • 範囲内で一様にランダムにN-1個の数字を選択し(置換なし)、それらをソートし、それらの間のN個の間隔を正規化して重みを取得します。
  • また、多項分布の前に共役としてよく使用されるディリクレ分布からサンプリングすることもできます。これは、あなたの場合の凸の重みに似ています。

多角形がそれほど非凸状でない場合は、まず凸包に変換することを検討してください。これにより、少なくともポリゴンの外側にあるポイントの数を大幅に制限する必要があります。


2

タスクはv.randomを使用してGRASS GIS(1つのコマンド)で非常に簡単に解決できます。

マニュアルページから選択したポリゴン(ここではノースカロライナ州ローリー市の郵便番号エリア)に3つのランダムポイントを追加する方法の例を以下に示します。SQLの「where」ステートメントを変更することにより、ポリゴンを選択できます。

選択したポリゴンでランダムポイントを生成する


1
郵便番号は多角形ではなく線であることに注意してください。
リチャード

詳しく説明してもらえますか?私もここでそれはエリアを指します:en.wikipedia.org/wiki/ZIP_Code#Primary_state_prefixes
markusN

確かに、郵便番号は特定の郵便局とその郵便配達ルートを参照します。その結果、郵便番号はポリゴンではなく線になります。それらは互いに重なり合い、穴を含むことができ、必ずしも米国全体または特定の州をカバーするとは限りません。このため、それらを使用してエリアを分割することは危険です。国勢調査の単位(ブロックグループなど)の方が適しています。参照してください:このこれを
リチャード

1
ありがとう!おそらく国にも依存します。たとえば、en.wikipedia.org / wiki / Postal_codes_in_Germanyを参照してください。ただし、郵便番号は私の中心的なトピックではありません。ここでOTである郵便番号の定義について話し合う:
markusN

1
両方のカウントに注意してください。次回はもっと簡潔に言うことができるように、おそらく少しブログエントリを作成する必要があります:
リチャード

1

回答リンク

https://gis.stackexchange.com/a/307204/103524

異なるアプローチを使用する3つのアルゴリズム。

Gitリポジトリリンク

  1. 以下は、x方向とy方向からの座標の実際の距離を使用した、シンプルで最適なアプローチです。内部アルゴリズムはWGS 1984(4326)と挿入されたSRIDへの結果変換を使用します。

機能================================================ ==================

CREATE OR REPLACE FUNCTION public.I_Grid_Point_Distance(geom public.geometry, x_side decimal, y_side decimal)
RETURNS public.geometry AS $BODY$
DECLARE
x_min decimal;
x_max decimal;
y_max decimal;
x decimal;
y decimal;
returnGeom public.geometry[];
i integer := -1;
srid integer := 4326;
input_srid integer;
BEGIN
CASE st_srid(geom) WHEN 0 THEN
    geom := ST_SetSRID(geom, srid);
        ----RAISE NOTICE 'No SRID Found.';
    ELSE
        ----RAISE NOTICE 'SRID Found.';
END CASE;
    input_srid:=st_srid(geom);
    geom := st_transform(geom, srid);
    x_min := ST_XMin(geom);
    x_max := ST_XMax(geom);
    y_max := ST_YMax(geom);
    y := ST_YMin(geom);
    x := x_min;
    i := i + 1;
    returnGeom[i] := st_setsrid(ST_MakePoint(x, y), srid);
<<yloop>>
LOOP
IF (y > y_max) THEN
    EXIT;
END IF;

CASE i WHEN 0 THEN 
    y := ST_Y(returnGeom[0]);
ELSE 
    y := ST_Y(ST_Project(st_setsrid(ST_MakePoint(x, y), srid), y_side, radians(0))::geometry);
END CASE;

x := x_min;
<<xloop>>
LOOP
  IF (x > x_max) THEN
      EXIT;
  END IF;
    i := i + 1;
    returnGeom[i] := st_setsrid(ST_MakePoint(x, y), srid);
    x := ST_X(ST_Project(st_setsrid(ST_MakePoint(x, y), srid), x_side, radians(90))::geometry);
END LOOP xloop;
END LOOP yloop;
RETURN
ST_CollectionExtract(st_transform(ST_Intersection(st_collect(returnGeom), geom), input_srid), 1);
END;
$BODY$ LANGUAGE plpgsql IMMUTABLE;

単純なクエリで関数を使用します。ジオメトリは有効で、ポリゴン、マルチポリゴン、またはエンベロープである必要があります

SELECT I_Grid_Point_Distance(geom, 50, 61) from polygons limit 1;

結果================================================ ====================== ここに画像の説明を入力してください

  1. NicklasAvénアルゴリズムに基づく2番目の関数。SRIDを処理しようとしました。

    アルゴリズムに次の変更を適用しました。

    1. ピクセルサイズのx方向とy方向の変数、
    2. 回転楕円体または楕円体の距離を計算するための新しい変数。
    3. SRIDを入力し、Geomを回転楕円体または楕円体データムの作業環境に関数変換し、各側に距離を適用し、結果を取得し、入力SRIDに変換します。

機能================================================ ==================

CREATE OR REPLACE FUNCTION I_Grid_Point(geom geometry, x_side decimal, y_side decimal, spheroid boolean default false)
RETURNS SETOF geometry AS $BODY$ 
DECLARE
x_max decimal; 
y_max decimal;
x_min decimal;
y_min decimal;
srid integer := 4326;
input_srid integer; 
BEGIN
CASE st_srid(geom) WHEN 0 THEN
  geom := ST_SetSRID(geom, srid);
  RAISE NOTICE 'SRID Not Found.';
    ELSE
        RAISE NOTICE 'SRID Found.';
    END CASE;

    CASE spheroid WHEN false THEN
        RAISE NOTICE 'Spheroid False';
        srid := 4326;
        x_side := x_side / 100000;
        y_side := y_side / 100000;
    else
        srid := 900913;
        RAISE NOTICE 'Spheroid True';
    END CASE;
    input_srid:=st_srid(geom);
    geom := st_transform(geom, srid);
    x_max := ST_XMax(geom);
    y_max := ST_YMax(geom);
    x_min := ST_XMin(geom);
    y_min := ST_YMin(geom);
RETURN QUERY
WITH res as (SELECT ST_SetSRID(ST_MakePoint(x, y), srid) point FROM
generate_series(x_min, x_max, x_side) as x,
generate_series(y_min, y_max, y_side) as y
WHERE st_intersects(geom, ST_SetSRID(ST_MakePoint(x, y), srid))
) select ST_TRANSFORM(ST_COLLECT(point), input_srid) from res;
END;
$BODY$ LANGUAGE plpgsql IMMUTABLE STRICT;

単純なクエリで使用します。

SELECT I_Grid_Point(geom, 22, 15, false) from polygons;

結果================================================ ==================ここに画像の説明を入力してください

  1. シリーズジェネレーターに基づく機能。

機能================================================ =================

CREATE OR REPLACE FUNCTION I_Grid_Point_Series(geom geometry, x_side decimal, y_side decimal, spheroid boolean default false)
RETURNS SETOF geometry AS $BODY$
DECLARE
x_max decimal;
y_max decimal;
x_min decimal;
y_min decimal;
srid integer := 4326;
input_srid integer;
x_series DECIMAL;
y_series DECIMAL;
BEGIN
CASE st_srid(geom) WHEN 0 THEN
  geom := ST_SetSRID(geom, srid);
  RAISE NOTICE 'SRID Not Found.';
    ELSE
        RAISE NOTICE 'SRID Found.';
    END CASE;

    CASE spheroid WHEN false THEN
        RAISE NOTICE 'Spheroid False';
    else
        srid := 900913;
        RAISE NOTICE 'Spheroid True';
    END CASE;
    input_srid:=st_srid(geom);
    geom := st_transform(geom, srid);
    x_max := ST_XMax(geom);
    y_max := ST_YMax(geom);
    x_min := ST_XMin(geom);
    y_min := ST_YMin(geom);

    x_series := CEIL ( @( x_max - x_min ) / x_side);
    y_series := CEIL ( @( y_max - y_min ) / y_side );
RETURN QUERY
SELECT st_collect(st_setsrid(ST_MakePoint(x * x_side + x_min, y*y_side + y_min), srid)) FROM
generate_series(0, x_series) as x,
generate_series(0, y_series) as y
WHERE st_intersects(st_setsrid(ST_MakePoint(x*x_side + x_min, y*y_side + y_min), srid), geom);
END;
$BODY$ LANGUAGE plpgsql IMMUTABLE STRICT;

単純なクエリで使用します。

SELECT I_Grid_Point_Series(geom, 22, 15, false) from polygons; 結果================================================ =========================

ここに画像の説明を入力してください

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.