113 lines
3.8 KiB
Python
113 lines
3.8 KiB
Python
BASE32 = "0123456789bcdefghjkmnpqrstuvwxyz"
|
||
|
||
# 邻居查找表(纬度和经度交替变化)
|
||
NEIGHBORS = {
|
||
"even": {
|
||
"N": "p0r21436x8zb9dcf5h7kjnmqesgutwvy",
|
||
"S": "14365h7k9dcfesgujnmqp0r2twvyx8zb",
|
||
"E": "bc01fg45238967deuvhjyznpkmstqrwx",
|
||
"W": "238967debc01fg45kmstqrwxuvhjyznp"
|
||
},
|
||
"odd": {
|
||
"N": "bc01fg45238967deuvhjyznpkmstqrwx",
|
||
"S": "238967debc01fg45kmstqrwxuvhjyznp",
|
||
"E": "p0r21436x8zb9dcf5h7kjnmqesgutwvy",
|
||
"W": "14365h7k9dcfesgujnmqp0r2twvyx8zb"
|
||
}
|
||
}
|
||
|
||
# 边界字符(用于计算超出边界的情况)
|
||
BORDERS = {
|
||
"even": {"N": "prxz", "S": "028b", "E": "bcfguvyz", "W": "0145hjnp"},
|
||
"odd": {"N": "bcfguvyz", "S": "0145hjnp", "E": "prxz", "W": "028b"}
|
||
}
|
||
|
||
def encode_geohash(lat, lng, precision=7):
|
||
"""手动实现 GeoHash 编码"""
|
||
lat_range = [-90.0, 90.0]
|
||
lng_range = [-180.0, 180.0]
|
||
|
||
binary = ""
|
||
is_even = True # 纬度和经度交替编码
|
||
|
||
while len(binary) < precision * 5:
|
||
mid = (lng_range[0] + lng_range[1]) / 2 if is_even else (lat_range[0] + lat_range[1]) / 2
|
||
if (lng if is_even else lat) >= mid:
|
||
binary += "1"
|
||
if is_even:
|
||
lng_range[0] = mid
|
||
else:
|
||
lat_range[0] = mid
|
||
else:
|
||
binary += "0"
|
||
if is_even:
|
||
lng_range[1] = mid
|
||
else:
|
||
lat_range[1] = mid
|
||
is_even = not is_even
|
||
|
||
# 5 位二进制转 Base32
|
||
return "".join(BASE32[int(binary[i:i+5], 2)] for i in range(0, len(binary), 5))
|
||
|
||
def decode_geohash(geohash):
|
||
"""解码 GeoHash 到 (lat, lng)"""
|
||
lat_range = [-90.0, 90.0]
|
||
lng_range = [-180.0, 180.0]
|
||
|
||
binary = "".join(f"{BASE32.index(c):05b}" for c in geohash)
|
||
|
||
is_even = True
|
||
for bit in binary:
|
||
if is_even:
|
||
mid = (lng_range[0] + lng_range[1]) / 2
|
||
lng_range = [mid, lng_range[1]] if bit == "1" else [lng_range[0], mid]
|
||
else:
|
||
mid = (lat_range[0] + lat_range[1]) / 2
|
||
lat_range = [mid, lat_range[1]] if bit == "1" else [lat_range[0], mid]
|
||
is_even = not is_even
|
||
|
||
return (sum(lat_range) / 2, sum(lng_range) / 2)
|
||
|
||
def get_neighbor(geohash, direction):
|
||
"""计算 GeoHash 的单个方向邻居"""
|
||
if not geohash:
|
||
return None
|
||
|
||
precision = len(geohash)
|
||
last_char = geohash[-1] # 取最后一个字符
|
||
geohash_prefix = geohash[:-1] # 除去最后一个字符
|
||
|
||
is_even = (precision % 2) == 0 # 判断奇偶精度
|
||
type_key = "even" if is_even else "odd"
|
||
|
||
# 如果当前 GeoHash 位于边界,需要递归调整前缀
|
||
if last_char in BORDERS[type_key][direction] and geohash_prefix:
|
||
geohash_prefix = get_neighbor(geohash_prefix, direction)
|
||
|
||
# 计算相邻 GeoHash
|
||
new_last_char = BASE32[NEIGHBORS[type_key][direction].index(last_char)]
|
||
return geohash_prefix + new_last_char
|
||
|
||
def get_all_neighbors(geohash):
|
||
"""计算 GeoHash 的 8 个相邻格子"""
|
||
return {
|
||
"N": get_neighbor(geohash, "N"),
|
||
"S": get_neighbor(geohash, "S"),
|
||
"E": get_neighbor(geohash, "E"),
|
||
"W": get_neighbor(geohash, "W"),
|
||
"NE": get_neighbor(get_neighbor(geohash, "N"), "E"),
|
||
"NW": get_neighbor(get_neighbor(geohash, "N"), "W"),
|
||
"SE": get_neighbor(get_neighbor(geohash, "S"), "E"),
|
||
"SW": get_neighbor(get_neighbor(geohash, "S"), "W")
|
||
}
|
||
|
||
if __name__ == '__main__':
|
||
# 示例:计算北京天安门广场(39.9087, 116.3975)的邻居
|
||
gh = encode_geohash(39.9087, 116.3975, precision=7)
|
||
neighbors = get_all_neighbors(gh)
|
||
|
||
print(f"中心 GeoHash: {gh}, {decode_geohash(gh)}")
|
||
print("相邻 GeoHash:")
|
||
for direction, neighbor in neighbors.items():
|
||
print(f"{direction}: {neighbor}, {decode_geohash(neighbor)}")
|