目前實驗中有個需求,即給定一個經緯度,判斷其是否處於某個城市內部。本來是想使用Google Earth的接口,然而谷歌一直不通過我的開發者賬號申請,遂自己寫了一個程序來實現該功能。
首先一個城市的輪廓必然是多邊形的,那麼得先獲取該多邊形的每個點的座標,即城市多邊形邊界的表示。
怎麼找城市邊界的座標呢?如下圖所示,下圖來自於:Getting polygon boundaries of City in JSON from Google Maps API?
打開圖中鏈接:https://nominatim.openstreetmap.org/
然後按照圖中指示的操作即可獲取所需地區的邊界,有txt和json兩種格式的數據。
下面以紐約爲例。
獲取到的簡化版的地圖邊界信息如下圖所示(當然裏面也有原始的邊界信息,只是轉折點比較多而已):
數據的表示形式如下圖所示:
這裏我將無關信息去掉,只保留座標信息,下圖爲另一個區域的一個簡單的表示。可以看出第一個和最後一個節點是同一個,即這裏的點是沿順時針順序進行的。
數據獲取與預處理完成,該開始寫核心算法了。
下面的核心問題就是:判斷一個點是否在多邊形區域內部
隨便百度一下,就會知道有一個行之有效的方法叫“射線法”,但是關於射線法的使用詳細情況卻往往未做說明,這其中還是有很多坑的。這篇文章將射線法中所要遇到的問題進行了全面的分析與解釋,也提出瞭解決辦法,因此我不多贅述。判斷一個座標點是否在不規則多邊形內部的算法
但是該文章並未給出代碼實現,因此本文在最後基於上述所說的應用場景給出一個python實現版本的例子。
# region1是一個list,若以上圖爲例,則其格式爲:[[-74.035, 40.695],[-74.036, 40.694]...]
# 該函數就是判斷一個給定點是否在region1這個list所圍成的多邊形內
# 需要注意:region1中第一個元素代表經度,第二個代表緯度,而函數實際輸入中第一個是緯度,第二個是經度
def isInRegion_1(lat, lon):
count = 0
lat = float(lat)
lon = float(lon)
for i in range(len(region_1)):
# 如果是最後一個元素,那麼必然是和第一個一樣的,就啥也不幹
if i+1 == len(region_1):
break
# 如果不是最後一個元素,那麼需要和後一個元素一起判斷給定點是否在區域內
la_1, lo_1 = region_1[i]
la_2, lo_2 = region_1[i+1]
la_1, lo_1, la_2, lo_2 = float(la_1), float(lo_1), float(la_2), float(lo_2)
# 以緯度確定位置,沿緯度向右作射線,看交點個數
if lat < min(la_1, la_2):
continue
if lat > max(la_1, la_2):
continue
# 如果和某一個共點那麼直接返回true
if (lat, lon) == (la_1, lo_1) or (lat, lon) == (la_2, lo_2):
return True
# 如果和兩點共線
if lat == la_1 == la_2:
if lon < min(lo_1, lo_2) or lon > max(lo_1, lo_2):
return False
else:
return True
# 接下來只需考慮射線穿越的情況,該情況下的特殊情況是射線穿越頂點
# 求交點的精度
cross_lon = (lat - la_1) * (lo_2 - lo_1) / (la_2 - la_1) + lo_1
# 因爲需要考慮特殊情況的射線穿越頂點,因此只判斷是不是穿越左邊這個頂點
if cross_lon == la_1:
count += 1
# 其他情況
elif cross_lon > lon:
count += 1
if count%2 == 0:
return False
return True