算法詳解——5種方法怎么判斷一個點是否在多邊形區域內


目錄

方法1:向量叉乘判別法

方法2:面積和判別法

具體做法:

方法3:夾角和判別法

方法4:引射線法

具體做法:

注意點:

算法圖解: 

參考代碼:

一個多邊形(polygon)的內部實現:

特殊情況:

計算一個多邊形的面積:

方法5:PNPoly算法

引入介紹:

核心算法部分:


方法1:向量叉乘判別法

(非凹多邊形,凹多邊形需要切割為凸多邊形)

設多邊形的頂點依次為A1,A2…An,要判斷的點為P,那么分別計算向量PA1叉乘向量PA2,向量PA2叉乘向量PA3,…,向量PA(n-1)叉乘向量PAn,向量PAn叉乘向量PA1,如果這些叉乘的結果都同向的話,那么這個點就在多邊形的內部。


方法2:面積和判別法

判斷目標點與多邊形的每條邊組成的三角形面積和是否等於該多邊形,相等則在多邊形內部。

具體做法:

設要檢測的點為P點。用P點連接多邊形各頂點,假如P點在多邊形以內,則頂點與P組成的三角形正好填充此多邊形。反之,則不能。此時使用多邊形面積計算公式,使P點作為參考點(a,b,c,.......,n為多邊形定點)則有:

   S = 0.5 * ( (a.x-p.x)*(a.y-p.y)-(b.x-p.x)*(b.y-p.y) +(b.x-p.x)*(b.y-p.y)-(c.x-p.x)*(c.y-p.y) +......... + (n.x-p.x)*(n.y-p.y)-(a.x-p.x)*(a.y-p.y) )

   如果P點不在多邊形以內,則每個三角形的相對面積符號不會一致。由於0.5並不會影響我們的運算結果   所以只需要取每個三角形面積的符號(a.x-p.x)*(a.y-p.y)-(b.x-p.x)*(b.y-p.y)  如果一致則在多邊形以內,反之則不在多邊形以內。


方法3:夾角和判別法

(非凹多邊形,凹多邊形需要切割為凸多邊形)

判斷目標點與所有邊的夾角和是否為360度,為360度則在多邊形內部。

 凹多邊形判別

     在幾何學中,對凸邊形的定義是對於任意一邊,不在這個邊上的頂點都在邊的一側。

凹多邊形分割為多個凸多邊形

參考:判斷點是否在一個任意多邊形內幾種方法_IceFlame-CSDN博客


方法4:引射線法

從目標點出發引一條射線,看這條射線和多邊形所有邊的交點數目。如果有奇數個交點,則說明在內部,如果有偶數個交點,則說明在外部。

具體做法:

將測試點的Y坐標與多邊形的每一個點進行比較,會得到一個測試點所在的行與多邊形邊的交點的列表。在下圖的這個例子中有8條邊與測試點所在的行相交,而有6條邊沒有相交。如果測試點的兩邊點的個數都是奇數個則該測試點在多邊形內,否則在多邊形外。在這個例子中測試點的左邊有5個交點,右邊有三個交點,它們都是奇數,所以點在多邊形內。

注意點:

如果測量點處於多邊形邊緣或頂點或下右3圖的情況下時,結果將不准確,不過我們可以根據不同的情況作特殊處理來保證結果的正確性。

算法圖解: 

關於這個算法的具體的更多圖形例子:http://alienryderflex.com/polygon/

參考代碼:

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;
}

一個多邊形(polygon)的內部實現:

public bool IsInside(PointLatLng p)
      {
         int count = Points.Count;

         if(count < 3)
         {
            return false;
         }

         bool result = false;

         for(int i = 0, j = count - 1; i < count; i++)
         {
            var p1 = Points[i];
            var p2 = Points[j];

            if(p1.Lat < p.Lat && p2.Lat >= p.Lat || p2.Lat < p.Lat && p1.Lat >= p.Lat)
            {
               if(p1.Lng + (p.Lat - p1.Lat) / (p2.Lat - p1.Lat) * (p2.Lng - p1.Lng) < p.Lng)
               {
                  result = !result;
               }
            }
            j = i;
         }
         return result;
      }

特殊情況:

要檢測的點在多變形的一條邊上,射線法判斷的結果是不確定的,需要特殊處理(If the test point is on the border of the polygon, this algorithm will deliver unpredictable results)。

計算一個多邊形的面積:

private static double SignedPolygonArea(List<PointLatLng> points)
        {
            // Add the first point to the end.
            int pointsCount = points.Count;
            PointLatLng[] pts = new PointLatLng[pointsCount + 1];
            points.CopyTo(pts, 0);
            pts[pointsCount] = points[0];

            for (int i = 0; i < pointsCount + 1; ++i)
            {
                pts[i].Lat = pts[i].Lat * (System.Math.PI * 6378137 / 180);
                pts[i].Lng = pts[i].Lng * (System.Math.PI * 6378137 / 180);
            }

            // Get the areas.
            double area = 0;
            for (int i = 0; i < pointsCount; i++)
            {
                area += (pts[i + 1].Lat - pts[i].Lat) * (pts[i + 1].Lng + pts[i].Lng) / 2;
            }

            // Return the result.
            return area;
        }

        /// <summary>
        /// Get the area of a polygon
        /// </summary>
        /// <param name="points"></param>
        /// <returns></returns>
        public static double GetPolygonArea(List<PointLatLng> points)
        {
            // Return the absolute value of the signed area.
            // The signed area is negative if the polygon is oriented clockwise.
            return Math.Abs(SignedPolygonArea(points));
        }

參考判斷點是否在多邊形內部_game_shader-CSDN博客


方法5:PNPoly算法

引入介紹:

在GIS(地理信息管理系統)中,判斷一個坐標是否在多邊形內部是個經常要遇到的問題。乍聽起來還挺復雜。根據W. Randolph Franklin 提出的PNPoly算法,只需區區幾行代碼就解決了這個問題。

假設多邊形的坐標存放在一個數組里,首先我們需要取得該數組在橫坐標和縱坐標的最大值和最小值,根據這四個點算出一個四邊型,首先判斷目標坐標點是否在這個四邊型之內,如果在這個四邊型之外,那可以跳過后面較為復雜的計算,直接返回false。

if (p.x < minX || p.x > maxX || p.y < minY || p.y > maxY) {
    // 這個測試都過不了。。。直接返回false;
}

核心算法部分:

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 代表多邊形有幾個點。浮點數testx, testy代表待測試點的橫坐標和縱坐標,*vertx,*verty分別指向儲存多邊形橫縱坐標數組的首地址。

我們注意到,每次計算都涉及到相鄰的兩個點和待測試點,然后考慮兩個問題:

  1. 被測試點的縱坐標testy是否在本次循環所測試的兩個相鄰點縱坐標范圍之內?即
    verty[i] < testy < verty[j]

    verty[j] < testy < verty[i]

  2. 待測點test是否在i,j兩點之間的連線之下?看不懂后半短if statement的朋友請自行在紙上寫下i,j兩點間的斜率公式,要用到一點初中解析幾何和不等式的知識范疇,對廣大碼農來說小菜一碟。

然后每次這兩個條件同時滿足的時候我們把返回的布爾量取反。

這個表達式的意思是說,隨便畫個多邊形,隨便定一個點,然后通過這個點水平划一條線,先數數看這條橫線和多邊形的邊相交幾次,(或者說先排除那些不相交的邊,第一個判斷條件),然后再數這條橫線穿越多邊形的次數是否為奇數,如果是奇數,那么該點在多邊形內,如果是偶數,則在多邊形外。詳細的數學證明這里就不做了,需要自行畫多邊形進行驗證。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM