2Dポイントがポリゴン内にあるかどうかを判断するにはどうすればよいですか?


497

ヒットテストで使用するために、ポリゴンアルゴリズム内に高速の 2Dポイントを作成しようとしています(例Polygon.contains(p:Point))効果的なテクニックの提案をいただければ幸いです。


あなたは右利きまたは左利きの問題についてのあなたの認識について私たちに話すのを忘れていました-これは「内側」対「外側」として解釈することもできます-RT
Richard T

13
はい、質問で多くの詳細が未指定のままになっていることに気付きましたが、現時点では、さまざまな応答を確認することに少し興味があります。
Scott Evernden、2008年

4
90面のポリゴンはエニアコンタゴンと呼ばれ、10,000面のポリゴンはミリゴンと呼ばれます。

「最もエレガント」は、「仕事がまったくない」アルゴリズムを見つけるのに苦労したため、対象外です。私は自分自身をそれを把握する必要があります。stackoverflow.com/questions/14818567/...
davidkonrad

回答:


732

グラフィックについては、整数を好むのではなく、多くのシステムはUIの描画に整数を使用しますが(ピクセルは結局intです)、たとえばmacOSはすべてに浮動小数点を使用します。macOSはポイントのみを認識し、ポイントは1ピクセルに変換できますが、モニターの解像度によっては、別のピクセルに変換される場合があります。網膜スクリーンでは、半分のポイント(0.5 / 0.5)がピクセルです。それでも、macOSのUIが他のUIよりも大幅に遅いことに気づきませんでした。結局のところ、3D API(OpenGLまたはDirect3D)もフロートで動作し、最新のグラフィックライブラリはGPUアクセラレーションを利用することがよくあります。

さて、速度が主な関心事だとおっしゃいましたが、さあ、速度を上げましょう。高度なアルゴリズムを実行する前に、まず簡単なテストを行ってください。軸合わせの境界ボックスを作成するポリゴンの周囲に。これは非常に簡単で高速で、すでに多くの計算を安全にすることができます。それはどのように機能しますか?ポリゴンのすべてのポイントを反復処理し、XとYの最小値/最大値を見つけます。

例えばあなたはポイントを持っています(9/1), (4/3), (2/7), (8/2), (3/6)。これは、Xminが2、Xmaxが9、Yminが1、Ymaxが7であることを意味します。2つのエッジ(2/1)と(9/7)を持つ長方形の外側のポイントは、ポリゴン内に存在できません。

// p is your point, p.x is the x coord, p.y is the y coord
if (p.x < Xmin || p.x > Xmax || p.y < Ymin || p.y > Ymax) {
    // Definitely not within the polygon!
}

これは、任意の時点で実行する最初のテストです。ご覧のとおり、このテストは超高速ですが、非常に粗いです。外接する四角形内のポイントを処理するには、より高度なアルゴリズムが必要です。これを計算する方法はいくつかあります。どちらの方法が機能するかは、ポリゴンに穴を開けることができるか、または常にソリッドであるかによっても異なります。以下はソリッドの例です(1つは凸、1つは凹)。

Polygon without hole

そしてここに穴のあるものがあります:

Polygon with hole

緑は真ん中に穴があいています!

上記の3つすべてのケースを処理でき、それでもかなり高速な最も簡単なアルゴリズムは、レイキャスティングと呼ばれます。アルゴリズムの考え方は非常に単純です。ポリゴンの外側からポイントまで仮想光線を描画し、ポリゴンの側面に当たる頻度を数えます。ヒット数が偶数の場合はポリゴンの外側、奇数の場合は内側です。

Demonstrating how the ray cuts through a polygon

巻き数のアルゴリズムは、代替となり、それは非常に近いポリゴンラインにあることのポイントのために、より正確であるが、それはまた、はるかに遅いです。レイキャスティングは、浮動小数点の精度と丸めの問題のため、ポリゴンの側面に近すぎるポイントでは失敗する可能性がありますが、実際にはほとんど問題になりません。ビューアがすでに内側か外側かを認識するビューア。

あなたはまだ上記の境界ボックスを持っています、覚えていますか?境界ボックスの外側の点を選択して、光線の開始点として使用してください。たとえば、ポイント(Xmin - e/p.y)は確かにポリゴンの外にあります。

しかし、何ですか eですか?まあ、e(実際にはイプシロン)バウンディングボックスにいくつかのパディングを与えます。先ほど述べたように、多角形のラインに近づきすぎると、レイトレーシングが失敗します。バウンディングボックスはポリゴンと等しい場合があるため(ポリゴンが軸に揃えられた長方形の場合、バウンディングボックスはポリゴン自体と同じです)、これを安全にするためにいくつかのパディングが必要です。どのくらいの大きさを選ぶべきですかe?大きすぎません。描画に使用する座標系のスケールによって異なります。ピクセルステップ幅が1.0の場合は、1.0を選択します(ただし、0.1も機能します)。

これで、開始座標と終了座標を持つ光線が得られたので、問題は「ポリゴン内の点です」から「光線がポリゴンの側面と交差する頻度」に変わります。したがって、以前のようにポリゴンポイントを操作するだけではなく、実際の辺が必要になります。辺は常に2つの点で定義されます。

side 1: (X1/Y1)-(X2/Y2)
side 2: (X2/Y2)-(X3/Y3)
side 3: (X3/Y3)-(X4/Y4)
:

すべての面に対して光線をテストする必要があります。光線をベクトル、すべての辺をベクトルと見なします。光線は、各面を正確に1回だけヒットするか、まったくヒットしない必要があります。同じ側​​を2回攻撃することはできません。2D空間の2本の線は、平行でない限り、常に1回だけ交差します。平行である場合は、交差しません。ただし、ベクトルの長さには制限があるため、2つのベクトルは短すぎて互いに出会うことができないため、平行でなくても交差しない場合があります。

// Test the ray against all sides
int intersections = 0;
for (side = 0; side < numberOfSides; side++) {
    // Test if current side intersects with ray.
    // If yes, intersections++;
}
if ((intersections & 1) == 1) {
    // Inside of polygon
} else {
    // Outside of polygon
}

これまでのところ、2つのベクトルが交差するかどうかをどのようにテストしますか?これはいくつかのCコード(テストされていません)です。

#define NO 0
#define YES 1
#define COLLINEAR 2

int areIntersecting(
    float v1x1, float v1y1, float v1x2, float v1y2,
    float v2x1, float v2y1, float v2x2, float v2y2
) {
    float d1, d2;
    float a1, a2, b1, b2, c1, c2;

    // Convert vector 1 to a line (line 1) of infinite length.
    // We want the line in linear equation standard form: A*x + B*y + C = 0
    // See: http://en.wikipedia.org/wiki/Linear_equation
    a1 = v1y2 - v1y1;
    b1 = v1x1 - v1x2;
    c1 = (v1x2 * v1y1) - (v1x1 * v1y2);

    // Every point (x,y), that solves the equation above, is on the line,
    // every point that does not solve it, is not. The equation will have a
    // positive result if it is on one side of the line and a negative one 
    // if is on the other side of it. We insert (x1,y1) and (x2,y2) of vector
    // 2 into the equation above.
    d1 = (a1 * v2x1) + (b1 * v2y1) + c1;
    d2 = (a1 * v2x2) + (b1 * v2y2) + c1;

    // If d1 and d2 both have the same sign, they are both on the same side
    // of our line 1 and in that case no intersection is possible. Careful, 
    // 0 is a special case, that's why we don't test ">=" and "<=", 
    // but "<" and ">".
    if (d1 > 0 && d2 > 0) return NO;
    if (d1 < 0 && d2 < 0) return NO;

    // The fact that vector 2 intersected the infinite line 1 above doesn't 
    // mean it also intersects the vector 1. Vector 1 is only a subset of that
    // infinite line 1, so it may have intersected that line before the vector
    // started or after it ended. To know for sure, we have to repeat the
    // the same test the other way round. We start by calculating the 
    // infinite line 2 in linear equation standard form.
    a2 = v2y2 - v2y1;
    b2 = v2x1 - v2x2;
    c2 = (v2x2 * v2y1) - (v2x1 * v2y2);

    // Calculate d1 and d2 again, this time using points of vector 1.
    d1 = (a2 * v1x1) + (b2 * v1y1) + c2;
    d2 = (a2 * v1x2) + (b2 * v1y2) + c2;

    // Again, if both have the same sign (and neither one is 0),
    // no intersection is possible.
    if (d1 > 0 && d2 > 0) return NO;
    if (d1 < 0 && d2 < 0) return NO;

    // If we get here, only two possibilities are left. Either the two
    // vectors intersect in exactly one point or they are collinear, which
    // means they intersect in any number of points from zero to infinite.
    if ((a1 * b2) - (a2 * b1) == 0.0f) return COLLINEAR;

    // If they are not collinear, they must intersect in exactly one point.
    return YES;
}

入力値は、ベクトル1(および)およびベクトル2(および)の2 つの端点です。したがって、2つのベクトル、4つのポイント、8つの座標があります。そして明確です。交差を増やし、何もしません。v1x1/v1y1v1x2/v1y2v2x1/v2y1v2x2/v2y2YESNOYESNO

COLLINEARはどうですか?これは、両方のベクトルが同じ無限線上にあり、位置と長さに応じて、まったく交差しないか、無限の数の点で交差することを意味します。私はこのケースをどのように処理するか絶対にわかりません、私はどちらの方法でも交差として数えません。とにかく、浮動小数点の丸め誤差のため、実際にはこのケースはかなりまれです。より良いコードはおそらくテストしないでしょう== 0.0fが、代わりにのようなもの< epsilonで、イプシロンはかなり小さい数です。

より多くのポイントをテストする必要がある場合は、多角形の辺の線形方程式の標準形式をメモリに保持することで、全体を少し高速化できるため、毎回これらを再計算する必要はありません。これにより、ポリゴンサイドごとに3つの浮動小数点値をメモリに格納する代わりに、すべてのテストで2つの浮動小数点乗算と3つの浮動小数点減算を節約できます。これは、典型的なメモリと計算時間のトレードオフです。

最後に重要なことですが、問題を解決するために3Dハードウェアを使用する場合、興味深い代替手段があります。GPUにすべての作業を任せてください。画面外のペイントサーフェスを作成します。黒で完全に塗ります。次に、OpenGLまたはDirect3Dでポリゴン(または、ポイントがそれらのいずれかにあるかどうかをテストしたい場合はすべてのポリゴンをペイントしますが、どのポリゴンでもかまいません)をペイントして、ポリゴンを別のポリゴンで塗りつぶします色、例えば白。ポイントがポリゴン内にあるかどうかを確認するには、描画面からこのポイントの色を取得します。これは単なるO(1)メモリフェッチです。

もちろん、この方法は、描画面を大きくする必要がない場合にのみ使用できます。GPUメモリに収まらない場合、この方法はCPUで行うよりも遅くなります。巨大である必要があり、GPUが最新のシェーダーをサポートしている場合でも、上記のレイキャスティングをGPUシェーダーとして実装することでGPUを使用できますが、これは絶対に可能です。より多くのポリゴンまたは多数のポイントをテストする場合、これは報われます。一部のGPUは64〜256ポイントを並行してテストできることを考慮してください。ただし、CPUからGPUへのデータ転送とその逆の転送には常にコストがかかるため、ポイントまたはポリゴンのいずれかが動的で頻繁に変更されるいくつかの単純なポリゴンに対していくつかのポイントをテストするだけの場合、GPUアプローチはほとんど効果がありませんオフ。


26
+1素晴らしい答え。ハードウェアを使用してそれを行う際に、グラフィックカードからデータを取得する必要があるために遅くなる可能性があることを他の場所で読んだことがあります。ただし、CPUの負荷を大幅に軽減するという原則が気に入っています。これがOpenGLでどのように行われるかについて、良い参考資料はありますか?
ギャビン

3
+1なので、これはとても簡単です。主な問題は、ポリゴンとテストポイントがグリッド上に並ぶ場合(珍しいことではありません)は、たとえば、ポリゴンポイントをまっすぐに通過するなど、「重複した」交差に対処する必要があることです。(1ではなく2のパリティを生成します)。この奇妙な領域に入る:stackoverflow.com/questions/2255842/…。コンピュータグラフィックスは、これらの特殊なケースでいっぱいです。理論的には単純で、実際には毛深いです。
Jared Updike、2010年

7
@RMorrisey:なぜそう思いますか?凹面のポリゴンではどのように失敗するのかわかりません。多角形が凹型の場合、光線は多角形を何度も出て再び入る可能性がありますが、結局、凹型多角形の場合も、ポイントが内側でも外側でも、ヒットカウンターは奇数になります。
メッキ

6
softsurfer.com/Archive/algorithm_0103/algorithm_0103.htmで説明されている「高速巻線数アルゴリズム」はかなり高速に動作します...
SP

10
/を使用してx座標とy座標を分離するのは混乱を招き、xをyで割ったものとして読み取られます。x、y(つまり、xコンマy)を使用する方がはるかに明確です。全体として、有用な答えです。
Ash

583

私は次のコードが最良の解決策だと思います(ここから抜粋):

int pnpoly(int nvert, float *vertx, float *verty, float testx, float testy)
{
  int i, j, c = 0;
  for (i = 0, j = nvert-1; i < nvert; j = i++) {
    if ( ((verty[i]>testy) != (verty[j]>testy)) &&
     (testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
       c = !c;
  }
  return c;
}

議論

  • nvert:ポリゴンの頂点の数。最後に最初の頂点を繰り返すかどうかは、上記の記事で説明されています。
  • vertx、verty:ポリゴンの頂点のx座標とy座標を含む配列。
  • testx、testy:テストポイントのX座標とY座標。

これは短くて効率的であり、凸型ポリゴンと凹型ポリゴンの両方で機能します。前に提案したように、最初に外接する四角形を確認し、ポリゴンの穴を個別に処理する必要があります。

この背後にある考え方は非常に簡単です。著者はそれを次のように説明しています:

テストポイントから半無限の光線を水平方向に(xを増やし、yを固定して)実行し、交差するエッジの数を数えます。各交差点で、光線は内側と外側を切り替えます。これは、ジョーダン曲線定理と呼ばれます。

変数cは、水平光線がエッジを横切るたびに、0から1および1から0に切り替わります。つまり、基本的には、交差するエッジの数が偶数か奇数かを追跡しています。0は偶数、1は奇数を意味します。


5
質問。私が渡す変数は正確には何ですか?彼らは何を表していますか?
tekknolagi

9
@Mickこれは、verty[i]とのverty[j]どちらかであるかどうかをチェックするtestyため、決して等しくなることはありません。
Peter Wood

4
このコードは堅牢ではなく、使用することはお勧めしません。ここではいくつかの詳細な分析を与えるのリンクは次のとおりです。 www-ma2.upc.es/geoc/Schirra-pointPolygon.pdf
Mikola

13
このアプローチには実際に制限があります(エッジケース):ポリゴンのポイント(15,20)をチェックすると[(10,10)、(10,20)、(20,20)、(20,10)]が返されますtrueではなくfalse。(10,20)または(20,15)と同じ。他のすべてのケースでは、アルゴリズムは完全に正常に機能し、エッジケースの偽陰性は私のアプリケーションでは問題ありません。
Alexander Pacha 2014年

10
@Alexander、これは実際には設計によるものです。2つの異なるポリゴンがエッジを共有する場合、左と下の境界を上と右の境界とは逆の方向で処理することにより、このエッジに沿ったポイントは1つだけのポリゴンに配置されます。..便利なプロパティ。
wardw

69

これは、このRPI教授からのnirgによる回答の C#バージョンです。そのRPIソースからのコードの使用には帰属が必要であることに注意してください。

境界ボックスチェックが上部に追加されました。ただし、James Brownが指摘するように、メインコードはバウンディングボックスチェック自体とほぼ同じ速度であるため、チェックしているポイントのほとんどがバウンディングボックス内にある場合、バウンディングボックスチェックは実際の操作全体を遅くする可能性があります。 。したがって、バウンディングボックスをチェックアウトしたままにすることもできますが、ポリゴンの形状があまり頻繁に変化しない場合は、ポリゴンのバウンディングボックスを事前に計算することもできます。

public bool IsPointInPolygon( Point p, Point[] polygon )
{
    double minX = polygon[ 0 ].X;
    double maxX = polygon[ 0 ].X;
    double minY = polygon[ 0 ].Y;
    double maxY = polygon[ 0 ].Y;
    for ( int i = 1 ; i < polygon.Length ; i++ )
    {
        Point q = polygon[ i ];
        minX = Math.Min( q.X, minX );
        maxX = Math.Max( q.X, maxX );
        minY = Math.Min( q.Y, minY );
        maxY = Math.Max( q.Y, maxY );
    }

    if ( p.X < minX || p.X > maxX || p.Y < minY || p.Y > maxY )
    {
        return false;
    }

    // https://wrf.ecse.rpi.edu/Research/Short_Notes/pnpoly.html
    bool inside = false;
    for ( int i = 0, j = polygon.Length - 1 ; i < polygon.Length ; j = i++ )
    {
        if ( ( polygon[ i ].Y > p.Y ) != ( polygon[ j ].Y > p.Y ) &&
             p.X < ( polygon[ j ].X - polygon[ i ].X ) * ( p.Y - polygon[ i ].Y ) / ( polygon[ j ].Y - polygon[ i ].Y ) + polygon[ i ].X )
        {
            inside = !inside;
        }
    }

    return inside;
}

5
うまくいきました。おかげで、JavaScriptに変換しました。stackoverflow.com/questions/217578/...
フィリップ・Lenssen

2
これはGraphicsPath.IsVisibleを使用するよりも1000倍以上高速です。境界ボックスのチェックにより、関数は約70%遅くなります。
James Brown、

GraphicsPath.IsVisible()が非常に遅いだけでなく、サイドが0.01fの範囲にある非常に小さなポリゴンでもうまく機能しない
NDependチームのPatrick

50

Nirgのアプローチに基づくM. Katzの回答のJavaScriptバリアントは次のとおりです。

function pointIsInPoly(p, polygon) {
    var isInside = false;
    var minX = polygon[0].x, maxX = polygon[0].x;
    var minY = polygon[0].y, maxY = polygon[0].y;
    for (var n = 1; n < polygon.length; n++) {
        var q = polygon[n];
        minX = Math.min(q.x, minX);
        maxX = Math.max(q.x, maxX);
        minY = Math.min(q.y, minY);
        maxY = Math.max(q.y, maxY);
    }

    if (p.x < minX || p.x > maxX || p.y < minY || p.y > maxY) {
        return false;
    }

    var i = 0, j = polygon.length - 1;
    for (i, j; i < polygon.length; j = i++) {
        if ( (polygon[i].y > p.y) != (polygon[j].y > p.y) &&
                p.x < (polygon[j].x - polygon[i].x) * (p.y - polygon[i].y) / (polygon[j].y - polygon[i].y) + polygon[i].x ) {
            isInside = !isInside;
        }
    }

    return isInside;
}

32

点pと各ポリゴン頂点の間の角度の方向付き合計を計算します。全方向角が360度の場合、ポイントは内側になります。合計が0の場合、ポイントは外側です。

この方法はより堅牢で、数値の精度にあまり依存しないため、この方法の方が好きです。

交差数の計算中に頂点を「ヒット」できるため、交差数の均一性を計算する方法は制限されています。

編集:ちなみに、この方法は凹面と凸面のポリゴンで機能します。

編集:私は最近、このトピックに関するウィキペディアの記事全体を見つけました。


1
いいえ、これは真実ではありません。これは、ポリゴンの凸性に関係なく機能します。
David Segonds、2008年

2
@DarenW:頂点ごとに1つのacosのみ。一方、このアルゴリズムは分岐がまったくないため、SIMDで実装するのが最も簡単です。
ジャスパーベッカーズ2009

1
@emilio、ポイントが三角形から離れている場合、ポイントと三角形の2つの頂点によって形成される角度が90度になる方法がわかりません。
David Segonds、2009年

2
最初に境界ボックスのチェックを使用して、「遠く離れている」場合を解決します。トリガーには、事前に生成されたテーブルを使用できます。
JOM

3
これはO(n)であり、最小限の計算で済むため、最適なソリューションです。すべてのポリゴンで機能します。私はこのソリューションを30年前に最初のIBMの仕事で研究しました。彼らはそれを承認し、現在でもGISテクノロジで使用しています。
Dominic Cerisano 16

24

この質問はとても興味深いです。この投稿に対する他の回答とは異なる別の実用的なアイデアがあります。アイデアは、角度の合計を使用して、ターゲットが内側か外側かを決定することです。巻数として知られていますます。

xをターゲットポイントとします。配列[0、1、.... n]をエリアのすべてのポイントとします。ターゲットポイントをすべての境界ポイントに線で接続します。ターゲットポイントがこの領域内にある場合。すべての角度の合計は360度になります。そうでない場合、角度は360度未満になります。

このイメージを参照して、アイデアの基本的な理解を深めてください。 ここに画像の説明を入力してください

私のアルゴリズムは、時計回りが正の方向であることを前提としています。これは潜在的な入力です:

[[-122.402015, 48.225216], [-117.032049, 48.999931], [-116.919132, 45.995175], [-124.079107, 46.267259], [-124.717175, 48.377557], [-122.92315, 47.047963], [-122.402015, 48.225216]]

以下は、アイデアを実装するPythonコードです。

def isInside(self, border, target):
degree = 0
for i in range(len(border) - 1):
    a = border[i]
    b = border[i + 1]

    # calculate distance of vector
    A = getDistance(a[0], a[1], b[0], b[1]);
    B = getDistance(target[0], target[1], a[0], a[1])
    C = getDistance(target[0], target[1], b[0], b[1])

    # calculate direction of vector
    ta_x = a[0] - target[0]
    ta_y = a[1] - target[1]
    tb_x = b[0] - target[0]
    tb_y = b[1] - target[1]

    cross = tb_y * ta_x - tb_x * ta_y
    clockwise = cross < 0

    # calculate sum of angles
    if(clockwise):
        degree = degree + math.degrees(math.acos((B * B + C * C - A * A) / (2.0 * B * C)))
    else:
        degree = degree - math.degrees(math.acos((B * B + C * C - A * A) / (2.0 * B * C)))

if(abs(round(degree) - 360) <= 3):
    return True
return False

21

エリックヘインズの記事 boboboboによって引用は本当に素晴らしいです。特に興味深いのは、アルゴリズムのパフォーマンスを比較する表です。角度の合計方法は、他の方法と比較して本当に悪いです。さらに興味深いのは、ルックアップグリッドを使用してポリゴンをさらに「イン」および「アウト」セクターに細分割するような最適化により、1000サイドを超えるポリゴンでもテストが非常に高速になることです。

とにかく、それは初期の段階ですが、私の投票は「クロッシング」法に向けられています。しかし、私はそれが最も簡潔に記述され、デビッド・バークによって成文化されたことを発見しました。実際の三角法が必要ないこと、そして凸面と凹面で機能し、辺の数が増えるにつれて適度に機能することを気に入っています。

ちなみに、ランダムポリゴンでテストした、エリックヘインズの記事のパフォーマンステーブルの1つを次に示します。

                       number of edges per polygon
                         3       4      10      100    1000
MacMartin               2.9     3.2     5.9     50.6    485
Crossings               3.1     3.4     6.8     60.0    624
Triangle Fan+edge sort  1.1     1.8     6.5     77.6    787
Triangle Fan            1.2     2.1     7.3     85.4    865
Barycentric             2.1     3.8    13.8    160.7   1665
Angle Summation        56.2    70.4   153.6   1403.8  14693

Grid (100x100)          1.5     1.5     1.6      2.1      9.8
Grid (20x20)            1.7     1.7     1.9      5.7     42.2
Bins (100)              1.8     1.9     2.7     15.1    117
Bins (20)               2.1     2.2     3.7     26.3    278

11

nirgによる回答の Swiftバージョン:

extension CGPoint {
    func isInsidePolygon(vertices: [CGPoint]) -> Bool {
        guard !vertices.isEmpty else { return false }
        var j = vertices.last!, c = false
        for i in vertices {
            let a = (i.y > y) != (j.y > y)
            let b = (x < (j.x - i.x) * (y - i.y) / (j.y - i.y) + i.x)
            if a && b { c = !c }
            j = i
        }
        return c
    }
}

これには、bの計算でゼロ除算の問題が発生する可能性があります。「a」の計算で交差する可能性があることが示された場合、「b」と「c」を含む次の行のみを計算する必要があります。両方のポイントが上、または両方のポイントが下の場合、可能性はありません。これは、「a」の計算で説明されます。
anorskdev

11

Nirgによって投稿され、boboboboによって編集されたソリューションが本当に好きです。私はそれをjavascriptフレンドリーにし、私の使用のためにもう少し読みやすくしました:

function insidePoly(poly, pointx, pointy) {
    var i, j;
    var inside = false;
    for (i = 0, j = poly.length - 1; i < poly.length; j = i++) {
        if(((poly[i].y > pointy) != (poly[j].y > pointy)) && (pointx < (poly[j].x-poly[i].x) * (pointy-poly[i].y) / (poly[j].y-poly[i].y) + poly[i].x) ) inside = !inside;
    }
    return inside;
}

8

私は下の研究者だったとき、私はこの背中にいくつかの作業をしたマイケル・ストーンブレーカーあなたが知っている、思い付いた教授- アングルPostgreSQLのなど。

最速の方法は、バウンディングボックスを最初に作成することです。これは、SUPERが高速だからです。境界ボックスの外側にある場合は、外側にあります。そうでなければ、あなたはもっと難しい仕事をします...

優れたアルゴリズムが必要な場合は、ジオワーク用のオープンソースプロジェクトのPostgreSQLソースコードを確認してください...

指摘したいのですが、右利きと左利きの洞察はありませんでした(「内側」と「外側」の問題としても表現できます...


更新

BKBのリンクは、数多くの合理的なアルゴリズムを提供しました。私は地球科学の問題に取り組んでいたため、緯度/経度で機能する解決策が必要でしたが、利き手に特有の問題があります。これは、狭い領域の内側の領域ですか、それとも大きな領域の領域ですか?答えは、頂点の「方向」が重要であることです。これは、左利きまたは右利きのどちらかであり、このようにして、任意の特定のポリゴンの「内側」としていずれかの領域を示すことができます。そのため、私の仕事では、そのページに列挙されているソリューション3を使用しました。

さらに、私の作業では、「オンライン」テストで個別の関数を使用しました。

...誰かが尋ねたので:頂点の数がいくつかの数を超えたときにバウンディングボックステストが最適であることがわかりました-必要に応じて、より長いテストを行う前に非常に迅速なテストを行ってください...バウンディングボックスは、最大のx、最小のx、最大のy、最小のyを組み合わせて、ボックスの4点を作成します...

次のヒント:グリッドスペースですべてのより洗練された「薄暗い」計算をすべて平面上の正の点で実行し、「実際の」経度/緯度に再投影して、考えられるエラーを回避しました。経度のライン180を交差したときと、極域を処理したときにラップアラウンドします。よくできました!


バウンディングボックスがない場合はどうなりますか?:)
スコットエバーデン2008年

8
バウンディングボックスは簡単に作成できます。最大で最小のx、最大で最小のyを使用するのは4つのポイントだけです。よりすぐ。
Richard T

「...経度の線180を横切ったとき、および極域を処理するときに、ラップアラウンドのエラーの可能性を回避します。」これについて詳しく説明できますか?ポリゴンが経度0と交差しないようにすべてを「上」に移動する方法は理解できると思いますが、いずれかの極を含むポリゴンの処理方法がわかりません...
tiritea

6

David Segondの答えは、ほぼ標準的な一般的な答えであり、Richard Tの答えが最も一般的な最適化ですが、他にもいくつかあります。その他の強力な最適化は、あまり一般的ではないソリューションに基づいています。たとえば、多数のポイントで同じポリゴンをチェックする場合、ポリゴンを三角形化すると、非常に高速なTIN検索アルゴリズムが多数あるため、処理速度が大幅に向上します。もう1つは、ポリゴンとポイントが低解像度の限られた平面上にある場合(画面表示など)、ポリゴンをメモリマップディスプレイバッファーに特定の色でペイントし、特定のピクセルの色をチェックして、それがあるかどうかを確認することです。ポリゴンで。

多くの最適化と同様に、これらは一般的なケースではなく特定のケースに基づいており、単一の使用ではなく償却時間に基づいて利益を生み出します。

この分野で働いているとき、私はJoeseph O'Rourkes 'Cの計算幾何学' ISBN 0-521-44034-3が非常に役立つことを発見しました。


4

簡単な解決策は、ポリゴンを三角形に分割し、ここで説明するように三角形をヒットテストすることです。

ポリゴンがCONVEXである場合は、より良いアプローチがあるかもしれません。ポリゴンを無限線のコレクションとして見てください。スペースを2つに分割する各行。どのポイントでも、ラインの片側にあるのか反対側にあるのかは簡単です。ポイントがすべてのラインの同じ側にある場合、それはポリゴンの内側にあります。


非常に高速で、より一般的な形状に適用できます。1990年頃、一部の側面が円弧である「カービゴン」がありました。それらの側面を円形のくさびと、くさびを原点(多角形の重心)に結合する1組の三角形に分析することにより、ヒットテストは高速かつ簡単でした。
DarenW 2009年

1
これらの曲線についての参照はありますか?
2009年

曲がりくねったものも参考にしたいと思います。
ジョエル、

凸多角形の場合の重要な解決策を見逃しました。点を対角線と比較することにより、頂点の数を半分にすることができます。そして、このプロセスを繰り返す、あなたは操作ではなく、N.ログイン(N)で、単一の三角形に減らす
イブス・ダアスト

4

これは古いことに気づきましたが、誰かが興味を持っている場合に備えて、Cocoaに実装されたレイキャスティングアルゴリズムを次に示します。それが物事を行う最も効率的な方法であるかどうかはわかりませんが、誰かを助けるかもしれません。

- (BOOL)shape:(NSBezierPath *)path containsPoint:(NSPoint)point
{
    NSBezierPath *currentPath = [path bezierPathByFlatteningPath];
    BOOL result;
    float aggregateX = 0; //I use these to calculate the centroid of the shape
    float aggregateY = 0;
    NSPoint firstPoint[1];
    [currentPath elementAtIndex:0 associatedPoints:firstPoint];
    float olderX = firstPoint[0].x;
    float olderY = firstPoint[0].y;
    NSPoint interPoint;
    int noOfIntersections = 0;

    for (int n = 0; n < [currentPath elementCount]; n++) {
        NSPoint points[1];
        [currentPath elementAtIndex:n associatedPoints:points];
        aggregateX += points[0].x;
        aggregateY += points[0].y;
    }

    for (int n = 0; n < [currentPath elementCount]; n++) {
        NSPoint points[1];

        [currentPath elementAtIndex:n associatedPoints:points];
        //line equations in Ax + By = C form
        float _A_FOO = (aggregateY/[currentPath elementCount]) - point.y;  
        float _B_FOO = point.x - (aggregateX/[currentPath elementCount]);
        float _C_FOO = (_A_FOO * point.x) + (_B_FOO * point.y);

        float _A_BAR = olderY - points[0].y;
        float _B_BAR = points[0].x - olderX;
        float _C_BAR = (_A_BAR * olderX) + (_B_BAR * olderY);

        float det = (_A_FOO * _B_BAR) - (_A_BAR * _B_FOO);
        if (det != 0) {
            //intersection points with the edges
            float xIntersectionPoint = ((_B_BAR * _C_FOO) - (_B_FOO * _C_BAR)) / det;
            float yIntersectionPoint = ((_A_FOO * _C_BAR) - (_A_BAR * _C_FOO)) / det;
            interPoint = NSMakePoint(xIntersectionPoint, yIntersectionPoint);
            if (olderX <= points[0].x) {
                //doesn't matter in which direction the ray goes, so I send it right-ward.
                if ((interPoint.x >= olderX && interPoint.x <= points[0].x) && (interPoint.x > point.x)) {  
                    noOfIntersections++;
                }
            } else {
                if ((interPoint.x >= points[0].x && interPoint.x <= olderX) && (interPoint.x > point.x)) {
                     noOfIntersections++;
                } 
            }
        }
        olderX = points[0].x;
        olderY = points[0].y;
    }
    if (noOfIntersections % 2 == 0) {
        result = FALSE;
    } else {
        result = TRUE;
    }
    return result;
}

5
Cocoaで実際に実行している場合は、[NSBezierPath containsPoint:]メソッドを使用できることに注意してください。
ThomasW 2012年

4

テストポイントのサンプルメソッドを使用したnirgの回答のObj-Cバージョン。Nirgの答えは私にはうまくいきました。

- (BOOL)isPointInPolygon:(NSArray *)vertices point:(CGPoint)test {
    NSUInteger nvert = [vertices count];
    NSInteger i, j, c = 0;
    CGPoint verti, vertj;

    for (i = 0, j = nvert-1; i < nvert; j = i++) {
        verti = [(NSValue *)[vertices objectAtIndex:i] CGPointValue];
        vertj = [(NSValue *)[vertices objectAtIndex:j] CGPointValue];
        if (( (verti.y > test.y) != (vertj.y > test.y) ) &&
        ( test.x < ( vertj.x - verti.x ) * ( test.y - verti.y ) / ( vertj.y - verti.y ) + verti.x) )
            c = !c;
    }

    return (c ? YES : NO);
}

- (void)testPoint {

    NSArray *polygonVertices = [NSArray arrayWithObjects:
        [NSValue valueWithCGPoint:CGPointMake(13.5, 41.5)],
        [NSValue valueWithCGPoint:CGPointMake(42.5, 56.5)],
        [NSValue valueWithCGPoint:CGPointMake(39.5, 69.5)],
        [NSValue valueWithCGPoint:CGPointMake(42.5, 84.5)],
        [NSValue valueWithCGPoint:CGPointMake(13.5, 100.0)],
        [NSValue valueWithCGPoint:CGPointMake(6.0, 70.5)],
        nil
    ];

    CGPoint tappedPoint = CGPointMake(23.0, 70.0);

    if ([self isPointInPolygon:polygonVertices point:tappedPoint]) {
        NSLog(@"YES");
    } else {
        NSLog(@"NO");
    }
}

サンプルポリゴン


2
もちろん、Objective-Cでは、CGPathContainsPoint()あなたの友達です。
Zev Eisenberg 2014年

@ZevEisenbergですが、それは簡単すぎるでしょう。メモありがとうございます。いつかそのプロジェクトを掘り下げて、カスタムソリューションを使用した理由を確認します。おそらく知らなかったようですCGPathContainsPoint()
Jon

4

問題の帰納的定義以上に美しいものはありません。ここで完全を期すために、プロローグのバージョンがあります。これは、レイキャスティングの背後にある考えも明確にするかもしれません。

の単純化アルゴリズムのシミュレーションに基づく http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html

いくつかのヘルパー述語:

exor(A,B):- \+A,B;A,\+B.
in_range(Coordinate,CA,CB) :- exor((CA>Coordinate),(CB>Coordinate)).

inside(false).
inside(_,[_|[]]).
inside(X:Y, [X1:Y1,X2:Y2|R]) :- in_range(Y,Y1,Y2), X > ( ((X2-X1)*(Y-Y1))/(Y2-Y1) +      X1),toggle_ray, inside(X:Y, [X2:Y2|R]); inside(X:Y, [X2:Y2|R]).

get_line(_,_,[]).
get_line([XA:YA,XB:YB],[X1:Y1,X2:Y2|R]):- [XA:YA,XB:YB]=[X1:Y1,X2:Y2]; get_line([XA:YA,XB:YB],[X2:Y2|R]).

2つの点AおよびB(Line(A、B))が与えられた直線の方程式は次のとおりです。

                    (YB-YA)
           Y - YA = ------- * (X - XA) 
                    (XB-YB) 

線の回転方向は、境界では時計回り、穴では反時計回りに設定することが重要です。ポイント(X、Y)、つまりテストされたポイントがラインの左半平面にあるかどうかを確認します(これは好みの問題です。右側でもかまいませんが、境界の方向でもかまいません)その場合、線を変更する必要があります)。これは、点から右(または左)に光線を投影し、線との交差を確認するためです。光線を水平方向に投影することを選択しました(これも好みの問題であり、同様の制限付きで垂直方向に行うこともできます)。

               (XB-XA)
           X < ------- * (Y - YA) + XA
               (YB-YA) 

ここで、ポイントが平面全体ではなく、線分セグメントの左側(または右側)にあるかどうかを知る必要があるため、検索をこのセグメントのみに制限する必要がありますが、これはセグメント内にあるので簡単です垂直軸のYよりも高いのは、ラインの1つのポイントのみです。これはより強い制限であるため、最初にチェックする必要があるため、この要件を満たす行のみを最初に取得し、その位置をチェックします。ジョーダンカーブの定理により、ポリゴンに投影される光線は偶数のラインで交差する必要があります。これで完了です。光線を右にスローし、線と交差するたびに状態を切り替えます。しかし、私たちの実装では、与えられた制限を満たすソリューションのバッグの長さをチェックし、それに対する内部関係を決定することに協力しています。ポリゴンの各ラインに対して、これを行う必要があります。

is_left_half_plane(_,[],[],_).
is_left_half_plane(X:Y,[XA:YA,XB:YB], [[X1:Y1,X2:Y2]|R], Test) :- [XA:YA, XB:YB] =  [X1:Y1, X2:Y2], call(Test, X , (((XB - XA) * (Y - YA)) / (YB - YA) + XA)); 
                                                        is_left_half_plane(X:Y, [XA:YA, XB:YB], R, Test).

in_y_range_at_poly(Y,[XA:YA,XB:YB],Polygon) :- get_line([XA:YA,XB:YB],Polygon),  in_range(Y,YA,YB).
all_in_range(Coordinate,Polygon,Lines) :- aggregate(bag(Line),    in_y_range_at_poly(Coordinate,Line,Polygon), Lines).

traverses_ray(X:Y, Lines, Count) :- aggregate(bag(Line), is_left_half_plane(X:Y, Line, Lines, <), IntersectingLines), length(IntersectingLines, Count).

% This is the entry point predicate
inside_poly(X:Y,Polygon,Answer) :- all_in_range(Y,Polygon,Lines), traverses_ray(X:Y, Lines, Count), (1 is mod(Count,2)->Answer=inside;Answer=outside).

3

nirgの回答のC#バージョンはこちらです。コードを共有します。それは誰かを時間を節約するかもしれません。

public static bool IsPointInPolygon(IList<Point> polygon, Point testPoint) {
            bool result = false;
            int j = polygon.Count() - 1;
            for (int i = 0; i < polygon.Count(); i++) {
                if (polygon[i].Y < testPoint.Y && polygon[j].Y >= testPoint.Y || polygon[j].Y < testPoint.Y && polygon[i].Y >= testPoint.Y) {
                    if (polygon[i].X + (testPoint.Y - polygon[i].Y) / (polygon[j].Y - polygon[i].Y) * (polygon[j].X - polygon[i].X) < testPoint.X) {
                        result = !result;
                    }
                }
                j = i;
            }
            return result;
        }

これはほとんどの場合に機能しますが、間違っており、常に適切に機能するとは限りません。正しいMカッツの解を使用
Lukas Hanacek 2013

3

Javaバージョン:

public class Geocode {
    private float latitude;
    private float longitude;

    public Geocode() {
    }

    public Geocode(float latitude, float longitude) {
        this.latitude = latitude;
        this.longitude = longitude;
    }

    public float getLatitude() {
        return latitude;
    }

    public void setLatitude(float latitude) {
        this.latitude = latitude;
    }

    public float getLongitude() {
        return longitude;
    }

    public void setLongitude(float longitude) {
        this.longitude = longitude;
    }
}

public class GeoPolygon {
    private ArrayList<Geocode> points;

    public GeoPolygon() {
        this.points = new ArrayList<Geocode>();
    }

    public GeoPolygon(ArrayList<Geocode> points) {
        this.points = points;
    }

    public GeoPolygon add(Geocode geo) {
        points.add(geo);
        return this;
    }

    public boolean inside(Geocode geo) {
        int i, j;
        boolean c = false;
        for (i = 0, j = points.size() - 1; i < points.size(); j = i++) {
            if (((points.get(i).getLongitude() > geo.getLongitude()) != (points.get(j).getLongitude() > geo.getLongitude())) &&
                    (geo.getLatitude() < (points.get(j).getLatitude() - points.get(i).getLatitude()) * (geo.getLongitude() - points.get(i).getLongitude()) / (points.get(j).getLongitude() - points.get(i).getLongitude()) + points.get(i).getLatitude()))
                c = !c;
        }
        return c;
    }

}

2

.Netポート:

    static void Main(string[] args)
    {

        Console.Write("Hola");
        List<double> vertx = new List<double>();
        List<double> verty = new List<double>();

        int i, j, c = 0;

        vertx.Add(1);
        vertx.Add(2);
        vertx.Add(1);
        vertx.Add(4);
        vertx.Add(4);
        vertx.Add(1);

        verty.Add(1);
        verty.Add(2);
        verty.Add(4);
        verty.Add(4);
        verty.Add(1);
        verty.Add(1);

        int nvert = 6;  //Vértices del poligono

        double testx = 2;
        double testy = 5;


        for (i = 0, j = nvert - 1; i < nvert; j = i++)
        {
            if (((verty[i] > testy) != (verty[j] > testy)) &&
             (testx < (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + vertx[i]))
                c = 1;
        }
    }

2

VBAバージョン:

注:ポリゴンがマップ内のエリアである場合、緯度/経度がX / Y(緯度= Y、経度= X)ではなくY / X値であることを覚えておいてください。経度は測定値ではありませんでした。

クラスモジュール:CPoint

Private pXValue As Double
Private pYValue As Double

'''''X Value Property'''''

Public Property Get X() As Double
    X = pXValue
End Property

Public Property Let X(Value As Double)
    pXValue = Value
End Property

'''''Y Value Property'''''

Public Property Get Y() As Double
    Y = pYValue
End Property

Public Property Let Y(Value As Double)
    pYValue = Value
End Property

モジュール:

Public Function isPointInPolygon(p As CPoint, polygon() As CPoint) As Boolean

    Dim i As Integer
    Dim j As Integer
    Dim q As Object
    Dim minX As Double
    Dim maxX As Double
    Dim minY As Double
    Dim maxY As Double
    minX = polygon(0).X
    maxX = polygon(0).X
    minY = polygon(0).Y
    maxY = polygon(0).Y

    For i = 1 To UBound(polygon)
        Set q = polygon(i)
        minX = vbMin(q.X, minX)
        maxX = vbMax(q.X, maxX)
        minY = vbMin(q.Y, minY)
        maxY = vbMax(q.Y, maxY)
    Next i

    If p.X < minX Or p.X > maxX Or p.Y < minY Or p.Y > maxY Then
        isPointInPolygon = False
        Exit Function
    End If


    ' SOURCE: http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html

    isPointInPolygon = False
    i = 0
    j = UBound(polygon)

    Do While i < UBound(polygon) + 1
        If (polygon(i).Y > p.Y) Then
            If (polygon(j).Y < p.Y) Then
                If p.X < (polygon(j).X - polygon(i).X) * (p.Y - polygon(i).Y) / (polygon(j).Y - polygon(i).Y) + polygon(i).X Then
                    isPointInPolygon = True
                    Exit Function
                End If
            End If
        ElseIf (polygon(i).Y < p.Y) Then
            If (polygon(j).Y > p.Y) Then
                If p.X < (polygon(j).X - polygon(i).X) * (p.Y - polygon(i).Y) / (polygon(j).Y - polygon(i).Y) + polygon(i).X Then
                    isPointInPolygon = True
                    Exit Function
                End If
            End If
        End If
        j = i
        i = i + 1
    Loop   
End Function

Function vbMax(n1, n2) As Double
    vbMax = IIf(n1 > n2, n1, n2)
End Function

Function vbMin(n1, n2) As Double
    vbMin = IIf(n1 > n2, n2, n1)
End Function


Sub TestPointInPolygon()

    Dim i As Integer
    Dim InPolygon As Boolean

'   MARKER Object
    Dim p As CPoint
    Set p = New CPoint
    p.X = <ENTER X VALUE HERE>
    p.Y = <ENTER Y VALUE HERE>

'   POLYGON OBJECT
    Dim polygon() As CPoint
    ReDim polygon(<ENTER VALUE HERE>) 'Amount of vertices in polygon - 1
    For i = 0 To <ENTER VALUE HERE> 'Same value as above
       Set polygon(i) = New CPoint
       polygon(i).X = <ASSIGN X VALUE HERE> 'Source a list of values that can be looped through
       polgyon(i).Y = <ASSIGN Y VALUE HERE> 'Source a list of values that can be looped through
    Next i

    InPolygon = isPointInPolygon(p, polygon)
    MsgBox InPolygon

End Sub

2

nirgの c ++ コード Python実装を作成しました

入力

  • bounding_points:ポリゴンを構成するノード。
  • bounding_box_positions:フィルタリングする候補点。(私の実装では、境界ボックスから作成されました。

    (入力はフォーマットのタプルのリストは以下のとおりです。[(xcord, ycord), ...]

戻り値

  • ポリゴンの内側にあるすべてのポイント。
def polygon_ray_casting(self, bounding_points, bounding_box_positions):
    # Arrays containing the x- and y-coordinates of the polygon's vertices.
    vertx = [point[0] for point in bounding_points]
    verty = [point[1] for point in bounding_points]
    # Number of vertices in the polygon
    nvert = len(bounding_points)
    # Points that are inside
    points_inside = []

    # For every candidate position within the bounding box
    for idx, pos in enumerate(bounding_box_positions):
        testx, testy = (pos[0], pos[1])
        c = 0
        for i in range(0, nvert):
            j = i - 1 if i != 0 else nvert - 1
            if( ((verty[i] > testy ) != (verty[j] > testy))   and
                    (testx < (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + vertx[i]) ):
                c += 1
        # If odd, that means that we are inside the polygon
        if c % 2 == 1: 
            points_inside.append(pos)


    return points_inside

ここでも、アイデアはここから取得されます


2

驚いたことに誰も早くこれを取り上げなかったが、データベースを必要とする実用主義者のために:MongoDBはこれを含むGeoクエリに対して優れたサポートを提供している。

あなたが探しているのは:

db.neighborhoods.findOne({ジオメトリ:{$ geoIntersects:{$ geometry:{タイプ: "ポイント"、座標:["経度"、 "緯度"]}}}})

Neighborhoods1つ以上のポリゴンを標準のGeoJson形式で格納するコレクションです。クエリがnullを返した場合、交差しません。交差していません。

ここに非常によく文書化されています:https//docs.mongodb.com/manual/tutorial/geospatial-tutorial/

330の不規則なポリゴングリッドに分類された6,000以上のポイントのパフォーマンスは1分未満で、最適化がまったく行われず、それぞれのポリゴンでドキュメントを更新する時間が含まれていました。


1

レイキャスティングを使用していない、Cでのポリゴンテストのポイントを次に示します。そして、それは重なり合う領域(自己交差)で機能しuse_holesます。引数を参照してください。

/* math lib (defined below) */
static float dot_v2v2(const float a[2], const float b[2]);
static float angle_signed_v2v2(const float v1[2], const float v2[2]);
static void copy_v2_v2(float r[2], const float a[2]);

/* intersection function */
bool isect_point_poly_v2(const float pt[2], const float verts[][2], const unsigned int nr,
                         const bool use_holes)
{
    /* we do the angle rule, define that all added angles should be about zero or (2 * PI) */
    float angletot = 0.0;
    float fp1[2], fp2[2];
    unsigned int i;
    const float *p1, *p2;

    p1 = verts[nr - 1];

    /* first vector */
    fp1[0] = p1[0] - pt[0];
    fp1[1] = p1[1] - pt[1];

    for (i = 0; i < nr; i++) {
        p2 = verts[i];

        /* second vector */
        fp2[0] = p2[0] - pt[0];
        fp2[1] = p2[1] - pt[1];

        /* dot and angle and cross */
        angletot += angle_signed_v2v2(fp1, fp2);

        /* circulate */
        copy_v2_v2(fp1, fp2);
        p1 = p2;
    }

    angletot = fabsf(angletot);
    if (use_holes) {
        const float nested = floorf((angletot / (float)(M_PI * 2.0)) + 0.00001f);
        angletot -= nested * (float)(M_PI * 2.0);
        return (angletot > 4.0f) != ((int)nested % 2);
    }
    else {
        return (angletot > 4.0f);
    }
}

/* math lib */

static float dot_v2v2(const float a[2], const float b[2])
{
    return a[0] * b[0] + a[1] * b[1];
}

static float angle_signed_v2v2(const float v1[2], const float v2[2])
{
    const float perp_dot = (v1[1] * v2[0]) - (v1[0] * v2[1]);
    return atan2f(perp_dot, dot_v2v2(v1, v2));
}

static void copy_v2_v2(float r[2], const float a[2])
{
    r[0] = a[0];
    r[1] = a[1];
}

注:これはatan2f、への呼び出しが多いため、あまり最適ではない方法の1つですが、このスレッドを読んでいる開発者にとっては興味深いかもしれません(私のテストでは、ライン交差法を使用した場合よりも23倍遅くなります)。


0

ポリゴンのヒットを検出するには、次の2つをテストする必要があります。

  1. ポイントがポリゴン領域内にある場合。(レイキャスティングアルゴリズムで実行可能)
  2. ポイントがポリゴンの境界線上にある場合(ポリライン(ライン)のポイント検出に使用されるのと同じアルゴリズムで実行できます)。

0

レイキャスティングアルゴリズムで以下の特殊なケースに対処するには:

  1. 光線はポリゴンの側面の1つと重なります。
  2. ポイントはポリゴンの内側にあり、光線はポリゴンの頂点を通過します。
  3. ポイントはポリゴンの外側にあり、光線はポリゴンの角度の1つに接しています。

ポイントが複雑なポリゴンの内側にあるかどうかを確認します。この記事はそれらを簡単に解決する方法を提供するので、上記の場合に特別な処理は必要ありません。


0

これを行うには、目的のポイントをポリゴンの頂点に接続して形成される領域が、ポリゴン自体の領域と一致するかどうかを確認します。

または、ポイントからチェックポイントまでの2つの連続するポリゴン頂点の各ペアの内角の合計が360になるかどうかをチェックすることもできますが、分割や計算が含まれないため、最初のオプションの方が速いと感じています三角関数の逆関数。

ポリゴンに穴が開いているとどうなるかわかりませんが、メインのアイデアはこの状況に適応できるようです

数学コミュニティに質問を投稿することもできます。百万通りのやり方があると思う


0

java-scriptライブラリを探している場合は、Polygonクラスにjavascript google maps v3拡張機能があり、そこにポイントが存在するかどうかを検出します。

var polygon = new google.maps.Polygon([], "#000000", 1, 1, "#336699", 0.3);
var isWithinPolygon = polygon.containsLatLng(40, -90);

GoogleエクステンションGithub



0

答えは、ポリゴンが単純か複雑かによって異なります。単純なポリゴンには、ラインセグメントの交差があってはなりません。したがって、穴を開けることはできますが、線を互いに交差させることはできません。複雑な領域は線の交点を持つことができます-したがって、それらは重なり合う領域、または1つの点だけで互いに接触する領域を持つことができます。

単純なポリゴンの場合、最適なアルゴリズムはレイキャスティング(交差数)アルゴリズムです。複雑なポリゴンの場合、このアルゴリズムはオーバーラップする領域内にあるポイントを検出しません。したがって、複雑なポリゴンの場合は、巻線数アルゴリズムを使用する必要があります。

以下は、両方のアルゴリズムをCで実装した優れた記事です。私はそれらを試しました、そして彼らはうまくいきます。

http://geomalgorithms.com/a03-_inclusion.html


0

nirgによるScalaバージョンのソリューション(外接する四角形の事前チェックが個別に行われると仮定):

def inside(p: Point, polygon: Array[Point], bounds: Bounds): Boolean = {

  val length = polygon.length

  @tailrec
  def oddIntersections(i: Int, j: Int, tracker: Boolean): Boolean = {
    if (i == length)
      tracker
    else {
      val intersects = (polygon(i).y > p.y) != (polygon(j).y > p.y) && p.x < (polygon(j).x - polygon(i).x) * (p.y - polygon(i).y) / (polygon(j).y - polygon(i).y) + polygon(i).x
      oddIntersections(i + 1, i, if (intersects) !tracker else tracker)
    }
  }

  oddIntersections(0, length - 1, tracker = false)
}

0

これは@nirg回答のgolangバージョンです(@@ m-katzによるC#コードに触発されました)

func isPointInPolygon(polygon []point, testp point) bool {
    minX := polygon[0].X
    maxX := polygon[0].X
    minY := polygon[0].Y
    maxY := polygon[0].Y

    for _, p := range polygon {
        minX = min(p.X, minX)
        maxX = max(p.X, maxX)
        minY = min(p.Y, minY)
        maxY = max(p.Y, maxY)
    }

    if testp.X < minX || testp.X > maxX || testp.Y < minY || testp.Y > maxY {
        return false
    }

    inside := false
    j := len(polygon) - 1
    for i := 0; i < len(polygon); i++ {
        if (polygon[i].Y > testp.Y) != (polygon[j].Y > testp.Y) && testp.X < (polygon[j].X-polygon[i].X)*(testp.Y-polygon[i].Y)/(polygon[j].Y-polygon[i].Y)+polygon[i].X {
            inside = !inside
        }
        j = i
    }

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