GEODIST:计算两个位置之间的直线距离
在使用 GEOADD 命令将位置及其坐标存储到位置集合中之后,可以使用 GEODIST 命令计算两个给定位置之间的直线距离:
GEODIST location_set name1 name2
在默认情况下,GEODIST 命令将以米为单位,返回两个给定位置之间的直线距离。
举个例子,通过执行以下代码,我们可以计算出清远和广州这两座城市之间的直线距离:
redis> GEODIST Guangdong-cities Qingyuan Guangzhou
"52094.433840356309" -- 清远和广州之间的直线距离约为52094m
而通过执行以下命令,我们可以计算出清远和深圳之间的直线距离:
redis> GEODIST Guangdong-cities Qingyuan Shenzhen
"144220.75758239598" -- 清远和深圳之间的直线距离约为144220m
指定距离的单位
GEODIST 命令在默认情况下将以米为单位返回两个给定位置之间的直线距离,用户也可以在有需要的情况下,通过可选的 unit 参数来指定自己想要使用的单位:
GEODIST location_set name1 name2 [unit]
unit 参数的值可以是以下单位中的任意一个:
-
m——以米为单位,为默认单位。
-
km——以千米为单位。
-
mi——以英里为单位。
-
ft——以英尺为单位。
前面在计算两座城市之间的距离时,使用了默认的米作为单位,因为两座城市之间的距离较远,所以 GEODIST 命令给出的计算结果看上去不够简洁。为此,我们可以改用千米为单位,再次调用 GEODIST 命令计算清远和广州以及清远和深圳之间的直线距离:
redis> GEODIST Guangdong-cities Qingyuan Guangzhou km
"52.094433840356309" -- 清远和广州之间直线距离约为52km
redis> GEODIST Guangdong-cities Qingyuan Shenzhen km
"144.22075758239598" -- 清远和深圳之间直线距离约为144km
改用千米为单位之后,GEODIST 命令给出的计算结果就简洁多了。
处理不存在的位置
在调用 GEODIST 命令时,如果用户给定的某个位置并不存在于位置集合中,那么命令将返回空值,表示计算失败。
比如在以下示例中,因为 Guangdong-cities 位置集合中并未存储肇庆市的坐标,所以尝试使用 GEODIST 命令去计算清远和肇庆之间的直线距离将得到一个空值:
redis> GEODIST Guangdong-cities Qingyuan Zhaoqing
(nil)
示例:具有基本功能的用户地理位置程序
很多社交网站都提供了与地理位置相关的功能,比如记录用户的位置、获取指定用户的位置,或者查找指定范围内的其他用户等。
在学习了 GEOADD、GEOPOS 和 GEODIST 这 3 个命令之后,我们同样可以构建出一个具有基本功能的用户地理位置程序,该程序能够记录用户所在的位置、获取指定用户所在的位置以及计算两个用户之间的直线距离,具体实现如代码清单9-1所示。
import random
USER_LOCATION_KEY = "user_locations"
class Location:
def __init__(self, client):
self.client = client
self.key = USER_LOCATION_KEY
def pin(self, user, longitude, latitude):
"""
记录指定用户的坐标。
"""
self.client.geoadd(self.key, longitude, latitude, user)
def get(self, user):
"""
获取指定用户的坐标。
"""
position_list = self.client.geopos(self.key, user)
# geopos() 允许用户输入多个位置,然后以列表形式返回各个位置的坐标。
# 因为我们这里只传入了一个位置,所以只需要取出列表的第一个元素即可。
if len(position_list) != 0:
return position_list[0]
def calculate_distance(self, user_a, user_b):
"""
以公里为单位,计算两个用户之间的直线距离。
"""
return self.client.geodist(self.key, user_a, user_b, unit="km")
def find_nearby(self, user, radius=1):
"""
以公里为单位,寻找并返回 user 指定半径范围内的所有其他用户。
"""
all_nearby_users = self.client.georadiusbymember(self.key, user, radius, unit="km")
# 因为 georadiusbymember() 方法会把 user 本身也包含在结果里面,
# 但由于我们并不需要这个用户,所以使用 remove() 方法移除他
all_nearby_users.remove(user)
return all_nearby_users
def find_random_nearby(self, user, radius=1):
"""
以公里为单位,随机地返回一个位于 user 指定半径范围内的其他用户。
"""
# random.choice() 方法用于从列表中随机地选择并返回一个项
return random.choice(self.find_nearby(user, radius))
这个程序所做的就是使用 GEOADD 命令把用户的 ID 以及用户所在的经纬度关联起来,然后使用 GEOPOS 命令去获取用户所在的经纬度,或者使用 GEODIST 命令去计算两个用户之间的直线距离。
通过执行以下代码,我们可以创建出相应的用户位置对象,并使用它记录 peter、jack 和 tom 这 3 个用户的地理位置:
>>> from redis import Redis
>>> from location import Location
>>> client = Redis(decode_responses=True)
>>> location = Location(client)
>>> location.pin("peter", 113.20996731519699, 23.593675019671288)
>>> location.pin("jack", 113.22784155607224, 23.125598202060807)
>>> location.pin("tom", 113.40603142976761, 22.511156445825442)
然后通过执行以下代码,获取 peter 和 jack 的坐标:
>>> location.get("peter")
(113.20996731519699, 23.593675019671288)
>>> location.get("jack")
(113.22784155607224, 23.125598202060807)
最后通过执行以下代码,计算 peter 和 jack 之间的直线距离以及 peter 和 tom 之间的直线距离:
>>> location.calculate_distance("peter", "jack")
52.0944
>>> location.calculate_distance("peter", "tom")
122.0651