Redis 3.2 版本的其中一个重要更新就是提供了对地理位置(GEO)数据的支持, 这一特性允许用户将地理位置信息储存到 Redis 数据库中, 并对它们执行距离计算、范围查找等操作。
尽管 Redis 3.2 正式释出已经有一段时间了, 但是 Redis 最常用的 Python 库 redis-py 却一直没有添加对 GEO 特性的支持, 这给使用 Python 操作 Redis 的用户们带来了不少麻烦。
可喜的是, 今天笔者在逛 github 的时候, 发现 redis-py 的最新版本已经添加了对 GEO 特性的支持, 所以今天就让我们一起来看看如何在 redis-py 中处理地理位置数据。
下载并安装新版 redis-py
因为支持 GEO 命令的最新版 redis-py 仍处于开发阶段, 所以它无法通过 pypi 取得。 为此, 我们需要从 redis-py 的 github 页面手动克隆最新版本的 redis-py :
$ git clone git@github:andymccurdy/redis-py.git
在克隆操作执行完毕之后, 我们进入到 redis-py 文件夹中, 然后将这个新版本安装到系统中:
$cd redis-py
$sudo setup.py install
如果你的系统已经安装了其他版本的 redis-py , 那么记得在安装新版之前, 先将旧版本卸载掉。
在安装操作执行完毕之后, 我们在解释器中载入 redis-py 库:
>>> from redis import Redis
>>> r=Redis()
通过对 Redis() 对象的属性进行访问, 我们可以确认各个 GEO 命令在 redis-py 中都有了相应的方法:
>>> for i in dir(r):
... if i.startswith("geo"):
... print(i)
...
geoadd
geodist
geohash
geopos
georadius
georadiusbymember
接下来, 就让我们逐个试试这些方法。
添加地理位置
首先要测试的是 geoadd() 方法, 这个方法调用的是 Redis 的 GEOADD 命令, 它的文档如下:
geoadd(self, name, *values) method of redis.clientis instance
Add the specified geospatial items to the specified key identified
by the name
argument. The Geospatial items are given as ordered
members of the values
argument, each item or place is formed b
the triad latitude, longitude and name.
作为例子, 以下代码展示了如何使用 geoadd() 方法, 将清远、广州和佛山这三个城市的坐标添加到 "Guangdong" 这个键里面:
>>> r.geoadd("Guangdong", "113.2099647", "23.593675", "Qingyuan", 113.2278442, 23.1255978, "Guangzhou", 113.106308, 23.0088312, "Foshan")
3获取地理位置
在将地理位置储存到键里面之后, 我们就可以使用 GEOPOS 命令去获取已储存的地理位置信息。 在 redis-py 里面, GEOPOS 命令可以通过执行 geopos() 方法来调用, 以下是这一方法的文档:
geopos(self, name, *values) method of redis.clientis instance
Return the postitions of each item of values
as members of
the specified key identified by the name
argument. Each position
is represented by the pairs lat and lon.
比如说, 以下代码就展示了如何使用 geopos() 方法去从 "Guangdong" 键中获取清远和广州的地理位置:
>>> r.geopos("Guangdong", "Qingyuan", "Guangzhou")
[(113.20996731519699, 23.593675019671288), (113.22784155607224, 23.125598202160807)]计算两地间的距离
对于被储存的两个地理位置, 我们可以使用 GEODIST 命令去计算它们之间的距离, 而这个命令在 redis-py 中可以通过同名的 geodist() 方法去调用, 以下是该方法的文档说明:
geodist(self, name, place1, place2, unit=None) method of redis.clientis instance
Return the distance between place1
and place2
members of the
name
key.
The units must be one o fthe following : m, km mi, ft. By default
meters are used.
比如说, 要计算清远和广州之间的距离, 我们可以执行以下代码:
>>> r.geodist("Guangdong", "Qingyuan", "Guangzhou")
52094.4338
因为 GEODIST 命令默认使用米作为单位, 所以它返回了 52094.4338 米作为结果, 为了让这个结果更为直观一些, 我们可以将 GEODIST 命令的单位从米改为千米(公里):
>>> r.geodist("Guangdong", "Qingyuan", "Guangzhou", unit="km")
52.0944
现在, 我们可以更直观地看到清远和广州之间相距 52.0944 公里了。
范围查找
Redis 的 GEORADIUS 命令 和 GEORADIUSBYMEMBER 命令 可以让买二手手机号码用户基于指定的地理位置或者已有的地理位置进行范围查找, redis-py 也通过同名的 georadius() 方法和 georadiusbymember() 方法来执行这两个命令。
以下是 georadius() 方法的文档:
georadius(self, name, longitude, latitude, radius, unit=None, withdist=False, withcoord=False, withhash=False, count=None, sort=None, store=None, store_dist=None) method of redis.clientis instance
Return the members of the of the specified key identified by the
name
argument which are within the borders of the area specified
with the latitude
and longitude
location and the maxium
distnance from the center specified by the radius
value.
The units must be one o fthe following : m, km mi, ft. By default
withdist
indicates to return the distances of each place.
withcoord
indicates to return the latitude and longitude of
each place.
withhash
indicates to return the geohash string of each place.
count
indicates to return the number of elements up to N.
sort
indicates to return the places in a sorted way, ASC for
nearest to fairest and DESC for fairest to nearest.
store
indicates to save the places names in a sorted set named
with a specific key, each element of the destination sorted set is
populated with the score got from the original geo sorted set.
store_dist
indicates to save the places names in a sorted set
named with a sepcific key, instead of store
the sorted set
destination score is set with the distance.
而以下则是 georadiusbymember() 方法的文档:
georadiusbymember(self, name, member, radius, unit=None, withdist=False, withcoord=False, withhash=False, count=None, sort=None, store=None, store_dist=None) method of redis.clientis instance
This command is exactly like georadius
with the sole difference
that instead of taking, as the center of the area to query, a longitude
and latitude value, it takes the name of a member already existing
inside the geospatial index represented by the sorted set.
作为例子, 以下代码展示了如何通过给定深圳的地理坐标(114.0538788,22.5551603)来查找位于它指定范围之内的其他城市, 这一查找操作是通过 georadius() 方法来完成的:
>>> r.georadius("Guangdong", 114.0538788, 22.5551603, 100, unit="km", withdist=True)
[] # 没有城市在深圳的半径 100 公里之内
>>> r.georadius("Guangdong", 114.0538788, 22.5551603, 120, unit="km", withdist=True)
[['Foshan', 109.4922], ['Guangzhou', 105.8065]] # 佛山和广州都在深圳的半径 120 公里之内
>>> r.georadius("Guangdong", 114.0538788, 22.5551603, 150, unit="km", withdist=True)
[['Foshan', 109.4922], ['Guangzhou', 105.8065], ['Qingyuan', 144.2205]] # 佛山、广州和清远都在深圳的半径 150 公里之内
另一方面, 以下代码则展示了如何通过 georadiusbymember() 方法, 找出位于广州指定半径范围内的其他城市:
>>> r.georadiusbymember("Guangdong", "Guangzhou", 30, unit="km", withdist=True)
[['Guangzhou', 0.0], ['Foshan', 17.9819]] # 佛山在广州的半径 30 公里范围之内
>>> r.georadiusbymember("Guangdong", "Guangzhou", 100, unit="km", withdist=True)
[['Foshan', 17.9819], ['Guangzhou', 0.0], ['Qingyuan', 52.0944]] # 佛山和清远都在广州的半径 100 公里范围之内获取 geohash
最后, 用户可以通过 geohash() 方法调用 GEOHASH 命令, 以此来获得指定地理位置的 geohash 值, 以下是该方法的文档:
geohash(self, name, *values) method of redis.clientis instance
Return the geo hash string for each item of values
members of
the specified key identified by the name
argument.
作为例子, 以下代码展示了如何获取清远、广州和佛山的 geohash 值:
>>> r.geohash("Guangdong", "Qingyuan", "Guangzhou", "Foshan")
['ws0w0phgp70', 'ws0e89curg0', 'ws06vu9s0j0']结语
好的, 这次关于使用 redis-py 处理 GEO 数据的介绍就到此结束, 希望这篇文章能够帮助大家更好地了解 redis-py 对 GEO 数据的支持, 也希望这个新版本的 redis-py 能够尽早释出, 让大家能够尽快地在 redis-py 里面用上 GEO 命令。