def isPointinPolygon(point, rangelist): #[1,0.8] [[0,0],[1,1],[0,1],[0,0]]
# 判断是否在多邊形内,如果不在, 返回false
Xlist = []
Ylist = []
Px = point[0]
Py = point[1]
# 初步判定法(是否在矩形框內)
for i in range(len(rangelist)):
Xlist.append(rangelist[i][0]) #抓X
Ylist.append(rangelist[i][1]) #抓Y
maxX = max(Xlist)
minX = min(Xlist)
maxY = max(Ylist)
minY = min(Ylist)
print( "(%1.1f,%1.1f) -> (%1.1f,%1.1f)" % (minX, minY, maxX, maxY) )
if (Px > maxX or Px < minX or #判斷X
Py > maxY or Py < minY): #判斷Y
return False
# 射線法(以左射線共奇數個交點為例)
count = 0
point1 = rangelist[0] #假設第一個點
for i in range(1, len(rangelist)):
point2 = rangelist[i]
print("i=%d, p1 = (%1.1f,%1.1f), p2 = (%1.1f,%1.1f)" % (i,point1[0],point1[1],point2[0],point2[1]) )
# 在頂點上
if (Px == point1[0] and Py == point1[1]) or (Px == point2[0] and Py == point2[1]):
print("在某個頂點上")
return False
# 水平射線穿過兩頂點之一,忽略不重新計算(若沒這步會使得這個點被認為是2個交點)
elif ( Py == min ( point1[1],point2[1] )):
print("pass")
point1 = point2
continue
# 判斷水平射線是否會穿過兩點之間,若有則繼續算
if (point1[1] < Py and point2[1] >= Py) or (point1[1] >= Py and point2[1] < Py):
# 求水平射線跟 p1,p2 連線 之交點的 x
# x = p2.x - d
# d = a*b/c
# a = p2.y-Py
# b = p2.x - p1.x
# c = p2.y - p1.y
x = point2[0] - (point2[1] - Py) * (point2[0] - point1[0])/(point2[1] - point1[1])
print("Intersection : (%d,%d)" % (x,Py) )
if ( x < Px): # 若左水平射線與p1,p2連線的交點,在待測點左側,則 count = count +1
print("count++")
count +=1
elif ( x > Px):
print("another side")
# 點在多邊形上
if ( x == Px):
print("點在多邊形上")
return False
point1 = point2
print("Number of intersection point = %d"%count)
if count%2 == 0:
return False
else:
return True
if __name__ == '__main__':
print(isPointinPolygon([4,4], [ [0,0],[1,0],[2,1],[3,0],[4,-1],[5,0],[5,5],[4.5,6],[4,5],[3,4],[2.5,6],[2,5],[0,5],[0,0] ] ))
首先,我們先畫出一個任意多邊形
圖(1)
我們的任務是給一個待測點座標 (Px,Py) ,判斷這個點是否在這個多邊形內,主要採用的演算法為"射線法"
射線法指出,當某一個待測點往左(或往右)射出一條無限長的水平射線,若這個射線與整個多邊形具有 "奇數" 個交點,則待測點在多邊形內;若為 "偶數" 個交點(包含0個) 則待測點在多邊形外部。
我們可以藉由將任意 N 邊形分解成為 N 個線段,去計算每一個線段跟待測點的交點(也有可能不存在交點),最後加總起來就能算出待測點跟這個任意 N 邊形有幾個交點(只計算待測點水平向左射線)
圖(2)
程式碼中 18~20 行判斷了待測點是否在這個初步判定矩形窗內,若否則回傳 False (也就是待測點不在多邊形內)
程式碼第 26 行以一個 for 迴圈為主體,將 N 邊形的每個線段(由兩個座標點 P1 、 P2組成) 抓來獨立計算。
程式碼第 31~34 行很好理解,我們判斷待測點 (Px,Py) 與 P1、P2 是否相等,若相等則代表待測點在頂點上。
程式碼第 37~41 行為一個特別的 Case 。,其向左水平射線這好穿過某個 "頂點"
圖(3) 可以想像成 圖(2) 的 (4,5) 到 (3,4) 的線段
圖(4) 可以想像成 圖(2) 的 (3,4) 到 (2.5,6) 的線段
例如在 圖(3)、圖(4) 我們發現待測點的向左水平射線都會穿過線段的頂點,若以 圖(2) 來說就是都穿過 (3,4)
在後續的演算法(44行後)我們將去判斷水平射線與線段的交點,但在此我們就先做一個前置判斷。若情況如 圖(3) 或圖 (4) 的情況,我們將使用 continue 來略過後續 for 迴圈的計算(也就是不計算交點了)
而如果情況是 圖(5) 及 圖(6)
圖(5) 可以想像成圖 (2) 的 (1,0) 到 (2,1) 的線段
圖(6) 可以想像成圖 (2) 的 (2,1) 到 (4,-1) 的線段
則會繼續後續的求交點演算法(44行後),得到交點數量 = 2
但因為射線法是判定交點數量是否是奇數,所以不會出現誤判的狀況
hint : 奇數+偶數(2) = 奇數 ; 偶數 + 偶數(2) = 偶數
程式碼第 44~52 行就是求交點的算法,首先在 44 行進行一個判斷式,該判斷式判斷了水平射線是否會穿過某個線段如圖(7) ,若為 圖(8) 或 (9) 的狀況,就不會進行 51~62 行的交點計算。
圖(7)
圖(8)
圖(9)
第 51 行的地方是求出向左水平射線與線段的交點,使用到一個幾何概念如 圖(10)
圖(10)
第 53 行判斷交點 x 是否在 Px 的左側,是的話 count = count +1
第 60 行判斷點是否在多邊形上(在多邊形上也被認為不在多邊形內部)
最後,在第 68~71 行判斷交點的數量,若為奇數則回傳 True (代表點在多邊形内)
執行結果 :
待測點為(2,0)
待測點為(3,1)
待測點為(5,-1)
待測點為(4.5,5)
待測點為(4,4)
待測點為(3.5,5)
補充 :
實際上我這篇文的目的是要幫助我篩選資料的,如下圖展示,效果很不錯
原始資料
判斷後
參考連結
判斷一個座標點是否在不規則多邊形內部的算法
判斷點是否在多邊形內 (附C實現代碼)
python 实现 射线法 判断一个点在图形区域内外
python3射线法判断点是否在多边形内
判断点是否在多边形内的算法