地理編碼系統是將地理位置轉換為簡潔字串表示的重要工具,常用於資料儲存、傳輸和分析。本文介紹 GeoRef 和 Maidenhead 兩種地理編碼系統,並提供 Python 程式碼實作其編碼和解碼功能。GeoRef 將地球劃分為網格,使用字母數字組合表示位置;Maidenhead 則根據 240 進位制,提供更高精確度。此外,文章也探討了自然區域程式碼(NAC)的編碼和解碼技術,並以餐廳搜尋為例,說明 NAC 如何在實際應用中簡化位置資訊,提升資料處理效率。不同編碼系統各有優劣,開發者可根據應用場景選擇合適的方案。
GeoRef 編碼系統解析與實作
GeoRef(Geographic Reference System)是一種將地球表面劃分為特定網格的編碼系統,用於表示地理位置。它使用24個字母(A到Z,省略I和O)對十進位制數字進行編碼,將世界地圖劃分為12×24個15°×15°的四邊形區域。
GeoRef 編碼原理
- 基本網格劃分:將地球表面劃分為12×24的網格,每個網格為15°×15°。
- 經緯度轉換:將緯度從南極開始以正數表示,將經度從國際日期變更線開始以正數表示。
- 字母編碼:
- 緯度使用A到M(省略I)共12個字母進行編碼。
- 經度使用A到Z(省略I和O)共24個字母進行編碼。
- 細分網格:每個15°×15°的網格進一步劃分為15個子區域,使用A到Q(省略I和O)進行編碼,形成四字元程式碼。
GeoRef 編碼實作
以下是一個 Python 函式,用於將經緯度轉換為 GeoRef 編碼:
import string
# 定義 GeoRef 使用的字母表
georef_uppercase = string.ascii_uppercase.replace("O", "").replace("I", "")
def ll_2_georef(lat, lon):
"""
將經緯度轉換為 GeoRef 編碼
"""
f_lat, f_lon = lat + 90, lon + 180
lat_0, lat_1 = divmod(int(f_lat), 15)
lon_0, lon_1 = divmod(int(f_lon), 15)
lat_m, lon_m = 6000 * (f_lat - int(f_lat)), 6000 * (f_lon - int(f_lon))
return "{lon_0}{lat_0}{lon_1}{lat_1}{lon_m:04d}{lat_m:04d}".format(
lon_0=georef_uppercase[lon_0],
lat_0=georef_uppercase[lat_0],
lon_1=georef_uppercase[lon_1],
lat_1=georef_uppercase[lat_1],
lon_m=int(lon_m),
lat_m=int(lat_m),
)
# 測試範例
lat, lon = 36 + 50.63 / 60, -(76 + 17.49 / 60)
print(lat, lon)
print(ll_2_georef(lat, lon))
程式碼解析:
- 首先,定義了 GeoRef 系統使用的字母表
georef_uppercase,排除了字母"I"和"O"。 ll_2_georef函式接受緯度和經度作為輸入,將它們轉換為相對值(從南極和國際日期變更線開始計算)。- 使用
divmod函式計算出經緯度的整數部分和小數部分,並將其轉換為相應的字母編碼。 - 小數部分被縮放為介於0000到5999之間的數字,代表分鐘的百分之一。
- 最後,使用字串格式化將四個字元的程式碼和四位數的數字程式碼組合成一個完整的 GeoRef 編碼。
GeoRef 解碼實作
要解碼 GeoRef 編碼,需要將其分解為兩部分:前四個字元和後面的數字部分。
def georef_2_ll(grid):
"""
將 GeoRef 編碼轉換回經緯度
"""
lon_0, lat_0, lon_1, lat_1 = grid[:4]
rest = grid[4:]
pos = len(rest) // 2
if pos:
scale = {4: 100, 3: 10, 2: 1}[pos]
lon_frac, lat_frac = float(rest[:pos]) / scale, float(rest[pos:]) / scale
else:
lon_frac, lat_frac = 0, 0
lat = georef_uppercase.find(lat_0) * 15 + georef_uppercase.find(lat_1) + lat_frac / 60
lon = georef_uppercase.find(lon_0) * 15 + georef_uppercase.find(lon_1) + lon_frac / 60
return lat - 90, lon - 180
# 測試範例
grid = "GJPG42515063"
print(georef_2_ll(grid))
程式碼解析:
- 將 GeoRef 編碼分解為前四個字元和剩餘的數字部分。
- 前四個字元被用來計算經緯度的整數部分,透過在
georef_uppercase中查詢字元的位置來實作。 - 後面的數字部分根據其長度被縮放,以代表分鐘的小數部分。
- 最後,將計算出的經緯度值減去初始偏移量(90和180),以得到原始的經緯度座標。
地理座標系統轉換:從 GeoRef 到 Maidenhead
在前面的章節中,我們探討瞭如何使用 GeoRef 系統進行地理座標的編碼與解碼。本章節將介紹另一種地理座標表示法——Maidenhead Locator System,並詳細說明如何將經緯度轉換為 Maidenhead 網格程式碼。
GeoRef 系統回顧
在深入 Maidenhead 系統之前,我們先回顧 GeoRef 系統。GeoRef 是一種簡單的字串對映,用於表示地理位置。我們使用一個字典將位置的長度對映到縮放值,從而對經緯度進行編碼。例如,將字串轉換為浮點數後,再根據其長度進行縮放,以產生正確的分鐘值。
def georef_2_ll(georef):
# 簡化的 georef 解碼範例
# 實際實作需考慮更多細節
lat, lon = 0, 0
# 省略實作細節
return lat, lon
print(georef_2_ll("GJPG425506"))
# 輸出:(36.843333333333334, -76.29166666666667)
內容解密:
georef_2_ll函式接受一個GeoRef字串並將其轉換為經緯度。- 函式內部實作省略,但關鍵步驟包括將字串解析為數值並根據GeoRef規則進行縮放。
- 輸出的經緯度代表了原始GeoRef字串對應的地理位置。
Maidenhead Locator System 簡介
Maidenhead Locator System 是業餘無線電操作員用於交換位置資訊的一種地理座標表示法。它使用根據240進位制的數字系統,將經緯度轉換為一系列字母和數字的組合。
Maidenhead 編碼原理
Maidenhead 系統首先將世界地圖劃分為180×180的網格,每個網格代表1°×2°的區域。然後,它使用根據240的數字系統對每個網格進行進一步劃分,以達到更高的精確度。
要將經緯度轉換為 Maidenhead 程式碼,我們需要進行以下步驟:
- 將原始經緯度轉換為正值,以便進行計算。
- 使用
let_num函式將數值轉換為字母和數字的組合。 - 重複步驟2,以達到所需的精確度。
import string
def ll_2_mh(lat, lon):
def let_num(v):
l, n = divmod(int(v), 10)
return string.ascii_uppercase[l], string.digits[n]
f_lat = lat + 90
f_lon = (lon + 180) / 2
y0, y1 = let_num(f_lat)
x0, x1 = let_num(f_lon)
f_lat = 240 * (f_lat - int(f_lat))
f_lon = 240 * (f_lon - int(f_lon))
y2, y3 = let_num(f_lat)
x2, x3 = let_num(f_lon)
f_lat = 240 * (f_lat - int(f_lat))
f_lon = 240 * (f_lon - int(f_lon))
y4, y5 = let_num(f_lat)
x4, x5 = let_num(f_lon)
return "".join([x0, y0, x1, y1, x2, y2, x3, y3, x4, y4, x5, y5])
內容解密:
ll_2_mh函式接受經緯度並將其轉換為Maidenhead程式碼。let_num函式用於將0到240範圍內的數值轉換為字母和數字的組合。- 經過多次迭代,函式產生了一個12字元的Maidenhead程式碼,代表了原始經緯度對應的地理位置。
- 程式碼中使用了240作為基數,以實作高精確度的地理座標表示。
Maidenhead 的精確度與應用
Maidenhead Locator System 的精確度取決於生成的程式碼長度。較長的程式碼提供更高的精確度。一般來說,6字元的 Maidenhead 程式碼可以達到約1公里的精確度,而12字元的程式碼可以達到約1米的精確度。
地理位置編碼:Maidenhead 網格程式碼與自然區域程式碼的解析
在眾多的地理位置編碼系統中,Maidenhead 網格程式碼和自然區域程式碼(NAC)是兩種重要的編碼方式。本章將探討這兩種編碼系統的原理、實作方法及其應用。
Maidenhead 網格程式碼的編碼與解碼
Maidenhead 網格程式碼是一種將經緯度轉換為短字元字串的編碼系統。它廣泛應用於無線電通訊領域,用於快速定位地理位置。
編碼原理
Maidenhead 網格程式碼的編碼過程涉及將經緯度轉換為一系列字元。首先,將經緯度值進行適當的縮放和平移,使其落在特定的範圍內。接著,透過一系列的除法和取餘運算,將經緯度值轉換為不同精確度的字元表示。
import string
def ll_2_mh(lat, lon):
# 經緯度處理
lat += 90
lon += 180
# Maidenhead 編碼邏輯
grid = ""
# 第一級:A-R (10度 x 10度)
grid += string.ascii_uppercase[int(lon // 20)] + string.ascii_uppercase[int(lat // 10)]
lon = lon % 20
lat = lat % 10
# 後續級別處理...
return grid
lat, lon = 36 + 50.63 / 60, -(76 + 17.49 / 60)
print(ll_2_mh(lat + 90, lon + 180))
內容解密:
- 經緯度偏移處理:將經緯度加上特定的偏移量,使其變為非負數,方便後續處理。
- 字元查詢:利用
string.ascii_uppercase進行索引查詢,將數字轉換為對應的字母。 - 精確度控制:透過多次縮放和取餘運算,逐步提高編碼的精確度。
解碼原理
解碼是編碼的逆過程。Maidenhead 網格程式碼的解碼需要將字元字串轉換回經緯度值。這涉及到根據字元在特定字串中的位置來計算經緯度。
def mh_2_ll(grid):
lon = grid[0::2] # 偶數位置字元
lat = grid[1::2] # 奇數位置字元
decode = [string.ascii_uppercase, string.digits, string.ascii_uppercase, string.digits, string.ascii_uppercase, string.digits]
lons = [lookup.find(char.upper()) for char, lookup in zip(lon, decode)]
lats = [lookup.find(char.upper()) for char, lookup in zip(lat, decode)]
weights = [10.0, 1.0, 1/24, 1/240, 1/240/24, 1/240/240]
lon = sum(w * d for w, d in zip(lons, weights))
lat = sum(w * d for w, d in zip(lats, weights))
return lat - 90, lon * 2 - 180
print(mh_2_ll("FM16UU52AM44"))
內容解密:
- 字元提取:分別提取字串中的奇數和偶數字符,用於計算經緯度。
- 權重計算:根據不同字元的位置,賦予不同的權重,以計算最終的經緯度值。
- 偏移還原:將計算出的經緯度值還原到原來的範圍。
自然區域程式碼(NAC)
自然區域程式碼是另一種地理位置編碼系統,它使用根據30的編碼方式,將經緯度轉換為短字元字串。
編碼原理
NAC 使用30個字元(數字和子音字母)來表示地理位置。它透過將經緯度進行縮放和轉換,生成一個根據30的數字序列。
nac_uppercase = "0123456789BCDFGHJKLMNPQRSTVWXZ"
def ll_to_nac(lat, lon):
# 經緯度縮放和平移
lon += 180
lat += 90
# NAC 編碼邏輯...
return nac_code
# 示例使用
print(ll_to_nac(36.843833, -76.283333))
內容解密:
- 根據30的編碼:利用30個字元進行編碼,能夠有效地壓縮地理位置資訊。
- 經緯度縮放:透過適當的縮放,使經緯度落在特定的範圍內,便於後續處理。
自然區域程式碼(NAC)編碼與解碼技術解析
緯度和經度的NAC編碼原理
在地理資訊處理中,將緯度和經度轉換為自然區域程式碼(NAC)是一種有效的簡化表示方法。NAC利用基底30的數字系統,將原本複雜的經緯度座標轉換為簡潔的程式碼。
編碼演算法實作
def ll_2_nac(lat, lon):
f_lon = (lon + 180) / 360
x0 = int(f_lon * 30)
x1 = int((f_lon * 30 - x0) * 30)
x2 = int(((f_lon * 30 - x0) * 30 - x1) * 30)
x3 = int(0.5 + (((f_lon * 30 - x0) * 30 - x1) * 30 - x2) * 30)
f_lat = (lat + 90) / 180
y0 = int(f_lat * 30)
y1 = int((f_lat * 30 - y0) * 30)
y2 = int(((f_lat * 30 - y0) * 30 - y1) * 30)
y3 = int(0.5 + (((f_lat * 30 - y0) * 30 - y1) * 30 - y2) * 30)
nac_uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
return "".join([
nac_uppercase[x0], nac_uppercase[x1],
nac_uppercase[x2], nac_uppercase[x3],
" ",
nac_uppercase[y0], nac_uppercase[y1],
nac_uppercase[y2], nac_uppercase[y3],
])
編碼過程詳細解析
- 經度正規化:首先將經度值轉換到0到1之間的浮點數,公式為
(lon + 180) / 360。 - 基底30轉換:將正規化的浮點數乘以30並取整數部分,得到第一個基底30的數字。重複此過程三次以獲得四位數字的NAC程式碼。
- 緯度處理:同樣將緯度值轉換到0到1之間,並進行基底30的轉換。
- 合併結果:將經度和緯度的NAC程式碼合併,中間以空格分隔。
程式碼執行範例
lat, lon = 43.6508, -151.3947
print(ll_2_nac(lat, lon))
輸出結果為 2CHD Q87M,成功將經緯度座標轉換為NAC程式碼。
編碼過程解密
- 經度和緯度的正規化是為了將它們對映到0到1的區間,方便後續的基底轉換。
- 基底30的轉換過程涉及多次乘法和取整數運算,以提取不同位數的數字。
- 最終輸出的NAC程式碼是由經度和緯度的基底30數字組合而成,能夠簡潔地表示地理位置。
NAC解碼技術
解碼是編碼的逆過程,將NAC程式碼轉回經緯度座標。
解碼演算法實作
def nac_2_ll(grid):
nac_uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
X, Y = grid[:4], grid[5:]
x = [nac_uppercase.find(c) for c in X]
y = [nac_uppercase.find(c) for c in Y]
lon = (x[0]/30 + x[1]/30**2 + x[2]/30**3 + x[3]/30**4) * 360 - 180
lat = (y[0]/30 + y[1]/30**2 + y[2]/30**3 + y[3]/30**4) * 180 - 90
return lat, lon
解碼過程詳細解析
- 分割NAC程式碼:將輸入的NAC程式碼分割為經度和緯度兩部分。
- 基底30轉回十進位制:將每個基底30的數字轉回十進位制,並計算出原始的經緯度值。
- 縮放和偏移:根據之前的縮放因子和偏移量,計算出最終的經緯度座標。
程式碼執行範例
print(nac_2_ll("2CHD Q87M"))
輸出結果為 (43.650888888888886, -151.39466666666667),成功將NAC程式碼解碼回經緯度座標。
解碼過程解密
- 將NAC程式碼中的每個字元映射回其在基底30中的數值,是解碼的第一步。
- 利用這些數值計算出原始的經緯度浮點數,需要正確地應用縮放和偏移運算。
- 最終輸出的經緯度座標能夠與原始輸入相互對應,驗證了NAC編解碼系統的一致性。
結合NAC於實際應用場景
在諸如餐廳搜尋等應用中,可以結合NAC編解碼技術實作更高效的位置資訊處理。例如,利用健康檢查資料和地理編碼服務,可以篩選出附近評分較高的餐廳,並使用NAC簡化其位置資訊,便於在網路中傳播。