多角形を決定し、ポイントが多角形の内側か外側かをチェックするアルゴリズムを実装したいと思います。
同様のアルゴリズムの利用可能な例があるかどうか誰もが知っていますか?
ポリゴンのポイント(PIP)問題 をご覧ください。
C#コード
bool IsPointInPolygon(List<Loc> poly, Loc point)
{
int i, j;
bool c = false;
for (i = 0, j = poly.Count - 1; i < poly.Count; j = i++)
{
if ((((poly[i].Lt <= point.Lt) && (point.Lt < poly[j].Lt))
|| ((poly[j].Lt <= point.Lt) && (point.Lt < poly[i].Lt)))
&& (point.Lg < (poly[j].Lg - poly[i].Lg) * (point.Lt - poly[i].Lt)
/ (poly[j].Lt - poly[i].Lt) + poly[i].Lg))
{
c = !c;
}
}
return c;
}
ロケーションクラス
public class Loc
{
private double lt;
private double lg;
public double Lg
{
get { return lg; }
set { lg = value; }
}
public double Lt
{
get { return lt; }
set { lt = value; }
}
public Loc(double lt, double lg)
{
this.lt = lt;
this.lg = lg;
}
}
Webを検索してさまざまな実装を試し、C++からC#に移植した後、ついにコードがまっすぐになりました。
public static bool PointInPolygon(LatLong p, List<LatLong> poly)
{
int n = poly.Count();
poly.Add(new LatLong { Lat = poly[0].Lat, Lon = poly[0].Lon });
LatLong[] v = poly.ToArray();
int wn = 0; // the winding number counter
// loop through all edges of the polygon
for (int i = 0; i < n; i++)
{ // Edge from V[i] to V[i+1]
if (v[i].Lat <= p.Lat)
{ // start y <= P.y
if (v[i + 1].Lat > p.Lat) // an upward crossing
if (isLeft(v[i], v[i + 1], p) > 0) // P left of Edge
++wn; // have a valid up intersect
}
else
{ // start y > P.y (no test needed)
if (v[i + 1].Lat <= p.Lat) // a downward crossing
if (isLeft(v[i], v[i + 1], p) < 0) // P right of Edge
--wn; // have a valid down intersect
}
}
if (wn != 0)
return true;
else
return false;
}
private static int isLeft(LatLong P0, LatLong P1, LatLong P2)
{
double calc = ((P1.Lon - P0.Lon) * (P2.Lat - P0.Lat)
- (P2.Lon - P0.Lon) * (P1.Lat - P0.Lat));
if (calc > 0)
return 1;
else if (calc < 0)
return -1;
else
return 0;
}
IsLeft関数は丸めの問題を引き起こしていたため、変換が間違っていることに気付かずに何時間も費やしていました。
ところで、これは元のコードと記事です: http://softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm
もっとシンプルで効率的なソリューションがあると思います。
C++のコードは次のとおりです。 C#に簡単に変換できるはずです。
int pnpoly(int npol, float *xp, float *yp, float x, float y)
{
int i, j, c = 0;
for (i = 0, j = npol-1; i < npol; j = i++) {
if ((((yp[i] <= y) && (y < yp[j])) ||
((yp[j] <= y) && (y < yp[i]))) &&
(x < (xp[j] - xp[i]) * (y - yp[i]) / (yp[j] - yp[i]) + xp[i]))
c = !c;
}
return c;
}
最良の説明と実装は、 多角形のワインディングナンバーインクルージョンのポイント にあります。
十分に説明された記事の最後に、C++実装さえあります。このサイトには、他のジオメトリベースの問題に対する優れたアルゴリズム/ソリューションも含まれています。
C++実装を変更して使用し、C#実装も作成しました。エッジ交差アルゴリズムよりも正確で、非常に高速であるため、ワインディングナンバーアルゴリズムを使用することは間違いありません。
(コメントすることはできませんので回答を使用して)ちょうどいいのですが、ジオフェンスにポイントインポリゴンを使用する場合は、球面座標で動作するようにアルゴリズムを変更する必要があります。 -180経度は180経度と同じであり、そのような状況ではポイントインポリゴンが壊れます。
Asp.Net C#の完全なソリューション、ここで完全な詳細を見ることができ、緯度と経度を使用してポリゴンの内側または外側にあるポイント(lat、lon)を見つける方法を見ることができますか? 記事参照リンク
private static bool checkPointExistsInGeofencePolygon(string latlnglist、string lat、string lng){
List<Loc> objList = new List<Loc>();
// sample string should be like this strlatlng = "39.11495,-76.873259|39.114588,-76.872808|39.112921,-76.870373|";
string[] arr = latlnglist.Split('|');
for (int i = 0; i <= arr.Length - 1; i++)
{
string latlng = arr[i];
string[] arrlatlng = latlng.Split(',');
Loc er = new Loc(Convert.ToDouble(arrlatlng[0]), Convert.ToDouble(arrlatlng[1]));
objList.Add(er);
}
Loc pt = new Loc(Convert.ToDouble(lat), Convert.ToDouble(lng));
if (IsPointInPolygon(objList, pt) == true)
{
return true;
}
else
{
return false;
}
}
private static bool IsPointInPolygon(List<Loc> poly, Loc point)
{
int i, j;
bool c = false;
for (i = 0, j = poly.Count - 1; i < poly.Count; j = i++)
{
if ((((poly[i].Lt <= point.Lt) && (point.Lt < poly[j].Lt)) |
((poly[j].Lt <= point.Lt) && (point.Lt < poly[i].Lt))) &&
(point.Lg < (poly[j].Lg - poly[i].Lg) * (point.Lt - poly[i].Lt) / (poly[j].Lt - poly[i].Lt) + poly[i].Lg))
c = !c;
}
return c;
}
ポイントがポリゴン内にあるかどうかを確認します-
頂点a1、a2、a3、a4、a5を持つ多角形を考えます。次の一連の手順は、ポイントPがポリゴンの内側にあるか外側にあるかを確認するのに役立ちます。
Edge a1-> a2とa2をPに、Pをa1に接続するベクトルによって形成される三角形のベクトル領域を計算します。同様に、多角形の辺として1つの辺を持ち、他の2つの辺がPをその辺に接続している可能性のある三角形のそれぞれのベクトル面積を計算します。
ポイントがポリゴンの内側にあるためには、各三角形に正の面積が必要です。三角形の1つが負の面積を持っている場合でも、ポイントPは多角形から突出しています。
3つのエッジを表すベクトルが与えられた三角形の面積を計算するには、 http://www.jtaylor1142001.net/calcjat/Solutions/VCrossProduct/VCPATriangle.htm を参照してください。
多角形が凸の場合、問題はより簡単です。その場合、各ラインに対して簡単なテストを実行して、ポイントがそのラインの内側または外側にあるかどうかを確認できます(両方向に無限に拡張)。それ以外の場合、凹型ポリゴンの場合、ポイントから無限遠まで(任意の方向に)架空の光線を描画します。境界線を何回横切るかを数えます。奇数は、ポイントが内側にあることを意味し、ポイントが外側にあることを意味します。
この最後のアルゴリズムは見た目よりも複雑です。架空の光線がポリゴンの頂点の1つに正確に当たるとどうなるかについて、非常に注意する必要があります。
架空の光線が-x方向に進む場合、y座標が厳密にポイントのy座標よりも小さいポイントを少なくとも1つ含む行のみをカウントするように選択できます。これは、ほとんどの奇妙なEdgeケースを正しく動作させる方法です。
地球の南に住む人々を助けるために、一つの詳細を追加します!!ブラジルにいる場合(私の場合)、GPS座標はすべてマイナスです。そして、これらすべてのアルゴリズムは間違った結果をもたらします。
すべてのポイントの緯度と経度の絶対値を使用する場合の最も簡単な方法。その場合、Jan Koberskyのアルゴリズムは完璧です。
ポリゴンは、ポイントペアA、B、C ...の連続リストとして定義されます。A。辺A-B、B-C ...は他の辺と交差しません
ボックスXmin、Xmax、Ymin、Ymaxを決定します
ケース1テストポイントPはボックスの外側にあります
ケース2では、テストポイントPはボックス内にあります。
ボックス{'Xmin、Ymin]-[Xmax、Ymax]}の「直径」Dを決定します(Dが横になっている可能性がある混乱を避けるために、少し余分に追加します)
すべての辺の勾配Mを決定する
すべての勾配Mと最も異なる勾配Mtを見つける
テストラインは、勾配Mtで距離DのPから延びています。
交差点の数をゼロに設定します
A-B、B-Cの各辺について、P-Dとその始点から終点までの辺との交点をテストします。必要に応じて、交差点の数を増やします。 Pから交差点までの距離がゼロであることは、Pが側面にあることを示していることに注意してください。
奇数カウントは、Pがポリゴン内にあることを示します
単純なポリゴン(線が交差しない)があり、穴がない場合は、ポリゴンを三角形分割することもできます。これは、おそらくGISアプリでTINを描画するために実行し、それぞれのポイントをテストします。三角形。ポリゴンへのエッジの数は少ないが、ポイントの数が多い場合、これは高速です。
三角形の興味深い点については link text をご覧ください
それ以外の場合は、エッジクロッシングではなくワインディングルールを明確に使用してください。エッジクロッシングにはエッジ上のポイントに関する実際の問題があります。
Janの答え は素晴らしい。
代わりにGeoCoordinateクラスを使用した同じコードを示します。
using System.Device.Location;
...
public static bool IsPointInPolygon(List<GeoCoordinate> poly, GeoCoordinate point)
{
int i, j;
bool c = false;
for (i = 0, j = poly.Count - 1; i < poly.Count; j = i++)
{
if ((((poly[i].Latitude <= point.Latitude) && (point.Latitude < poly[j].Latitude))
|| ((poly[j].Latitude <= point.Latitude) && (point.Latitude < poly[i].Latitude)))
&& (point.Longitude < (poly[j].Longitude - poly[i].Longitude) * (point.Latitude - poly[i].Latitude)
/ (poly[j].Latitude - poly[i].Latitude) + poly[i].Longitude))
c = !c;
}
return c;
}
PHPでc#メソッドを翻訳し、コードを理解するために多くのコメントを追加しました。
PolygonHelpsの説明:
ポイントがポリゴンの内側か外側かを確認します。この手順ではGPS座標を使用し、ポリゴンに小さな地理的領域がある場合に機能します。
入力:
$ poly:Pointの配列:多角形の頂点リスト。 [{Point}、{Point}、...];
$ point:チェックするポイント。ポイント:{"lat" => "x.xxx"、 "lng" => "y.yyy"}
$ cがfalseの場合、ポリゴンとの交点の数は偶数であるため、ポイントはポリゴンの外側にあります。
$ cがtrueの場合、ポリゴンとの交点の数は奇数であるため、ポイントはポリゴンの内側にあります。
$ nは、多角形の頂点の数です。
ポリゴン内の各頂点について、現在の頂点と前の頂点を通る線を計算し、2つの線に交点があるかどうかを確認します。
交差点が存在すると、$ cが変わります。
したがって、メソッドは、ポイントがポリゴン内にある場合はtrueを返し、そうでない場合はfalseを返します。
class PolygonHelps {
public static function isPointInPolygon(&$poly, $point){
$c = false;
$n = $j = count($poly);
for ($i = 0, $j = $n - 1; $i < $n; $j = $i++){
if ( ( ( ( $poly[$i]->lat <= $point->lat ) && ( $point->lat < $poly[$j]->lat ) )
|| ( ( $poly[$j]->lat <= $point->lat ) && ( $point->lat < $poly[$i]->lat ) ) )
&& ( $point->lng < ( $poly[$j]->lng - $poly[$i]->lng )
* ( $point->lat - $poly[$i]->lat )
/ ( $poly[$j]->lat - $poly[$i]->lat )
+ $poly[$i]->lng ) ){
$c = !$c;
}
}
return $c;
}
}