判斷一個點是否在多邊形區域內部 / 判斷一個給定位置是否位於某個城市內部

目前實驗中有個需求,即給定一個經緯度,判斷其是否處於某個城市內部。本來是想使用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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章