Geohash的原理介绍、Redis的Geo命令总结、Python使用Redis的Geo模块

11 篇文章 0 订阅
1 篇文章 0 订阅

Geohash

一.GeoHash编码介绍

参考:

https://hogwartsrico.github.io/2015/01/22/About-GeoHash/

Geohash原理 - 简书 (jianshu.com)

1.Geohash认识

GeoHash本质上是空间索引的一种方式,其基本原理是将地球理解为一个二维平面,将平面递归分解成更小的子块,每个子块在一定经纬度范围内拥有相同的编码。以GeoHash方式建立空间索引,可以提高对空间poi数据进行经纬度检索的效率。

GeoHash将二维的经纬度转换成字符串,比如下图展示了北京9个区域的GeoHash字符串,分别是WX4ER,WX4G2、WX4G3等等,每一个字符串代表了某一矩形区域。也就是说,这个矩形区域内所有的点(经纬度坐标)都共享相同的GeoHash字符串,这样既可以保护隐私(只表示大概区域位置而不是具体的点),又比较容易做缓存。

img

​ Geohash编码中,字符串相似的表示距离相近(特殊情况后文阐述),这样可以利用字符串的前缀匹配来查询附近的POI信息。如下两个图所示,一个在城区,一个在郊区,城区的GeoHash字符串之间比较相似,郊区的字符串之间也比较相似,而城区和郊区的GeoHash字符串相似程度要低些。此外,不同的编码长度,表示不同的范围区间,字符串越长,表示的范围越精确。

img

2.GeoHash算法

​ 以经纬度值:(116.389550, 39.928167)进行算法说明,对纬度39.928167进行逼近编码 (地球纬度区间是[-90,90]

a. 区间[-90,90]进行二分为[-90,0),[0,90],称为左右区间,可以确定39.928167属于右区间[0,90],给标记为1

b. 接着将区间[0,90]进行二分为 [0,45),[45,90],可以确定39.928167属于左区间 [0,45),给标记为0

c. 递归上述过程39.928167总是属于某个区间[a,b]。随着每次迭代区间[a,b]总在缩小,并越来越逼近39.928167

d. 如果给定的纬度x(39.928167)属于左区间,则记录0,如果属于右区间则记录1,序列的长度跟给定的区间划分次数有关,如下图

img

​ e. 同理,地球经度区间是[-180,180],可以对经度116.389550进行编码。通过上述计算, 经度产生的编码为1 1 0 1 0 0 1 0 1 1 0 0 0 1 0,纬度产生的编码为1 0 1 1 1 0 0 0 1 1 0 0 0 1 1

​ f. 合并:偶数位放经度(0,2,4,6,8),奇数位放纬度(1,3,5,7,9),把2串编码组合生成新串如下图(先放经度,后放纬度
img

​ g. 首先将11100 11101 00100 01111 0000 01101转成十进制,对应着28、29、4、15,0,13 十进制对应的base32编码就是wx4g0e,如下图.

img

Base32编码:

首先将二进制串划分每5位一组,不足5位补0。

然后将各组的5位二进制串转成十进制(5bits对应着10进制的数值为0-31)。

用0-9、b-z(去掉a、i、l、o)这32个字母进行Base32编码,即对照下标将其转换为字符串。

img

​ h. 同理,将编码转换成经纬度的解码算法与之相反。

3.GeoHash原理

​ Geohash其实就是将整个地图或者某个分割所得的区域进行一次划分,由于采用的是base32编码方式,即Geohash中的每一个字母或者数字(如wx4g0e中的w)都是由5bits组成(2^5 = 32,base32),这5bits可以有32中不同的组合(0~31),这样我们可以将整个地图区域分为32个区域,通过00000 ~ 11111来标识这32个区域。第一次对地图划分后的情况如下图所示(每个区域中的编号对应于该区域所对应的编码))。

img

​ Geohash的0、1串序列是经度0、1序列和纬度0、1序列中的数字交替进行排列的,偶数位对应的序列为经度序列,奇数位对应的序列为纬度序列,在进行第一次划分时,Geohash0、1序列中的前5个bits(11100),那么这5bits中有3bits是表示经度,2bits表示纬度,所以第一次划分时,是将经度划分成8个区段(2^3 = 8),将纬度划分为4个区段(2^2 = 4),这样就形成了32个区域。如下图

img

​ 同理,可以按照第一次划分所采用的方式对第一次划分所得的32个区域各自再次划分。

image-20220116205718209

​ 上文讲了GeoHash的计算步骤,仅仅说明是什么而没有说明为什么?为什么分别给经度和维度编码?为什么需要将经纬度两串编码交叉组合成一串编码?本节试图回答这一问题。

​ 如图所示,我们将二进制编码的结果填写到空间中,当将空间划分为四块时候,编码的顺序分别是左下角00,左上角01,右下脚10,右上角11,也就是类似于Z的曲线,当我们递归的将各个块分解成更小的子块时,编码的顺序是自相似的(分形),每一个子快也形成Z曲线,这种类型的曲线被称为Peano空间填充曲线

​ 这种类型的空间填充曲线的优点是将二维空间转换成一维曲线(事实上是分形维),对大部分而言,编码相似的距离也相近,但Peano空间填充曲线最大的缺点就是突变性,有些编码相邻但距离却相差很远,比如0111与1000,编码是相邻的,但距离相差很大。

img

​ 除Peano空间填充曲线外,还有很多空间填充曲线,如图所示,其中效果公认较好是Hilbert空间填充曲线,相较于Peano曲线而言,Hilbert曲线没有较大的突变。但是由于Peano曲线实现更加简单,在使用的时候配合一定的解决手段,可以很好的满足大部分需求,因此TD内部Geohash算法采用的是Peano空间填充曲线。

img

4.GeoHash优缺点

优点:

Geohash 的优点很明显,它利用 Z 阶曲线进行编码。而 Z 阶曲线可以将二维或者多维空间里的所有点都转换成一维曲线。在数学上成为分形维。并且 Z 阶曲线还具有局部保序性。

Z 阶曲线通过交织点的坐标值的二进制表示来简单地计算多维度中的点的z值。一旦将数据被加到该排序中,任何一维数据结构,例如二叉搜索树,B树,跳跃表或(具有低有效位被截断)哈希表 都可以用来处理数据。通过 Z 阶曲线所得到的顺序可以等同地被描述为从四叉树的深度优先遍历得到的顺序

这也是 Geohash 的另外一个优点,搜索查找邻近点比较快。

缺点:

Geohash 的缺点之一也来自 Z 阶曲线。

Z 阶曲线有一个比较严重的问题,虽然有局部保序性,但是它也有突变性。在每个 Z 字母的拐角,都有可能出现顺序的突变。

Geohash 的另外一个缺点是,如果选择不好合适的网格大小,判断邻近点可能会比较麻烦。

5.使用注意点

a. 由于GeoHash是将区域划分为一个个规则矩形,并对每个矩形进行编码,这样在查询附近POI信息时会导致以下问题,比如红色的点是我们的位置,绿色的两个点分别是附近的两个餐馆,但是在查询的时候会发现距离较远餐馆的GeoHash编码与我们一样(因为在同一个GeoHash区域块上),而较近餐馆的GeoHash编码与我们不一致。这个问题往往产生在边界处。

img

​ 解决的思路很简单,我们查询时,除了使用定位点的GeoHash编码进行匹配外,还使用周围8个区域的GeoHash编码,这样可以避免这个问题。

b. 我们已经知道现有的GeoHash算法使用的是Peano空间填充曲线,这种曲线会产生突变,造成了编码虽然相似但距离可能相差很大的问题,因此在查询附近餐馆时候,首先筛选GeoHash编码相似的POI点,然后进行实际距离计算。

c. GeoHash Base32编码长度与精度。可以看出,当geohash base32编码长度为8时,精度在19米左右,而当编码长度为9时,精度在2米左右,编码长度需要根据数据情况进行选择。

img

img

6.计算围栏内所有Geohash

理解了geohash算法的基本原理之后,本节将介绍一个实际应用中常见的场景:计算围栏范围内所有的Geohash编码。该场景封装为函数可以表示如下:输入组成围栏的点经纬度坐标集合和指定的geohash长度,输出一组geohash编码。

​ public static Set getHashByFence(List points, int length)

​ 具体算法步骤如下:

  1. 输入围栏点坐标集合List points和指定的geohash长度length

  2. 计算围栏的外包矩形的左上角和右下角坐标lat_min、lat_max、lng_min、lng_max

  3. 根据lat_min、lat_max、lng_min、lng_max,计算外包矩形对角定点的距离d

  4. 以外包矩形中心点为圆心,以d/2为半径做一个圆,计算圆覆盖范围内的geohash

4.1 获取圆的外包矩形左上角和右下角定点坐标经纬度,存储到double[] locs

4.2 根据geohash字符长度计算该长度geohash编码对应的经纬度间隔(latA,lngA)

4.3 根据latA和lngA,计算出locs组成的矩形的左上角和右下角定点的经纬度,在geohash划分的网格的索引(也就是第几个),分别记为lat_min,lat_max,lng_min,lng_max

4.4 计算lat_min,lat_max,lng_min,lng_max对应范围内左右geohash的二进制编码,然后将经纬度二进制编码uncode为geohash字符编码,保存为Set sets

  1. 剔除sets中geohash编码对应矩形的中心点不在points围栏范围内的geohash,得到最终的geohash结果集

二.Redis的GEO模块

1.精度问题

reids 在3.2版本以后增加了地理位置Geo模块

GeoHash 算法会继续对这个整数做一次base32编码(09,az,去掉a、i、l、o四个字母)变成一个字符串。在Redis里面,经纬度使用52位的整数进行编码,放进了zset里面,zset的value是元素的key,score是GeoHash的52位整数值。zset的score虽然是浮点数,但是对于52位的整数值,它可以无损存储。

double类型精度为52位;
geohash是以base32(5位一个编码)的方式编码,52bits最高可存储10位geohash值,对应地理区域大小为0.6*0.6米的格子。换句话说经Redis geo转换过的位置理论上会有约0.3 *1.414=0.424米的误差。
【来源:https://python.iitter.com/other/12028.html】

Redis的double类型:

​ Redis的分数类型采用的是double,64位双精度浮点数只有52位有效数字,它能精确表达的整数范围为-253到253,最高只能表示16位十进制整数(最大值为9007199254740992,其实连16位也不能完整表示)。

数据类型的范围与精度:

1)范围

float和double的范围是由指数的位数来决定的。
float的指数位有8位,而double的指数位有11位,分布如下:

  • float:(32位,4字节)
    1bit(符号位) 8bits(指数位) 23bits(尾数位)

  • double:(64位,8字节)
    1bit(符号位) 11bits(指数位) 52bits(尾数位)

    于是,float的指数范围为-127—+128,而double的指数范围为-1023----+1024,并且指数位是按补码的形式来划分的。
    其中负指数决定了浮点数所能表达的绝对值最小的非零数;而正指数决定了浮点数所能表达的绝对值最大的数,也即决定了浮点数的取值范围。
    float的范围为-2^128 ~ +2^128,也即-3.40E+38 ~ +3.40E+38;double的范围为-2^1024 ~ +2^1024,也即-1.79E+308 ~ +1.79E+308。

2)精度

float和double的精度是由尾数的位数来决定的。浮点数在内存中是按科学计数法来存储的,其整数部分始终是一个隐含着的“1”,由于它是不变的,故不能对精度造成影响。
float:2^23 = 8388608,一共七位,这意味着最多能有7位有效数字,但绝对能保证的为6位,也即float的精度为6~7位有效数字;
double:2^52 = 4503599627370496,一共16位,同理,double的精度为15~16位。

2.Redis的Geo指令

问题

我们常见的需求是查找 n米 范围内的点,那么 n米 与 GeoHash 码位数之间的映射如何实现呢?由于 GeoHash 码是由5位二进制码组成,每少一位,精度就会损失 2e(5/2)。

方法当然有的,我们将二进制GeoHash码直接索引就可以,但很长的索引长度会导致 B树 索引查询效率会迅速下降。

方案

于是我们接着寻找解决方案,既然使用 base32 转换为 32进制码 会不好控制精度,保持二进制又导致索引长度过长,那么进制位数和索引长度有没有一个平衡呢?

另外 Redis 的 sorted set 支持 64位 的 double 类型的 score,我们把二进制的 GeoHash 码转为十进制放入 Redis 的 sorted set 中,不是可以实现 log(n)的查询效率了么。

3.Redis 的 Geo 指令基本使用

Redis 提供的 Geo 指令只有 6 个,读者们瞬间就可以掌握。使用时,读者务必再次想起,它只是一个普通的 zset 结构。

geoadd      用来增加地理位置的坐标,可以批量添加地理位置 
geodist     用来获取两个地理位置的距离
geopos      可以获取地理位置的坐标,可以批量获取多个地理位置的坐标
geohash     可以获取元素的经纬度编码字符串
georadius   可以根据给定地理位置坐标获取指定范围内的地理位置集合
georadiusbymember  可以根据给定地理位置获取指定范围内的地理位置集合

增加

geoadd 指令携带集合名称以及多个经纬度名称三元组,注意这里可以加入多个三元组

127.0.0.1:6379> geoadd company 116.48105 39.996794 juejin
(integer) 1
127.0.0.1:6379> geoadd company 116.514203 39.905409 ireader
(integer) 1
127.0.0.1:6379> geoadd company 116.489033 40.007669 meituan
(integer) 1
127.0.0.1:6379> geoadd company 116.562108 39.787602 jd 116.334255 40.027400 xiaomi
(integer) 2

距离

geodist 指令可以用来计算两个元素之间的距离,携带集合名称、2 个名称和距离单位。

127.0.0.1:6379> geodist company juejin ireader km
"10.5501"
127.0.0.1:6379> geodist company juejin meituan km
"1.3878"
127.0.0.1:6379> geodist company juejin jd km
"24.2739"
127.0.0.1:6379> geodist company juejin xiaomi km
"12.9606"
127.0.0.1:6379> geodist company juejin juejin km
"0.0000"

我们可以看到掘金离美团最近,因为它们都在望京。距离单位可以是 m、km、ml、ft,分别代表米、千米、英里和尺。

获取元素位置

geopos 指令可以获取集合中任意元素的经纬度坐标,可以一次获取多个。

127.0.0.1:6379> geopos company juejin
1) 1) "116.48104995489120483"
 2) "39.99679348858259686"
127.0.0.1:6379> geopos company ireader
1) 1) "116.5142020583152771"
 2) "39.90540918662494363"
127.0.0.1:6379> geopos company juejin ireader
1) 1) "116.48104995489120483"
 2) "39.99679348858259686"
2) 1) "116.5142020583152771"
 2) "39.90540918662494363"

我们观察到获取的经纬度坐标和 geoadd 进去的坐标有轻微的误差,原因是 geohash 对二维坐标进行的一维映射是有损的,通过映射再还原回来的值会出现较小的差别。对于「附近的人」这种功能来说,这点误差根本不是事。

获取元素的 hash 值

geohash 可以获取元素的经纬度编码字符串,上面已经提到,它是 base32 编码。 你可以使用这个编码值去 http://geohash.org/${hash}中进行直接定位,它是 geohash 的标准编码值。

127.0.0.1:6379> geohash company ireader
1) "wx4g52e1ce0"
127.0.0.1:6379> geohash company juejin
1) "wx4gd94yjn0"
附近的公司

georadiusbymember 指令是最为关键的指令,它可以用来查询指定元素附近的其它元
素,它的参数非常复杂。

# 范围 20 公里以内最多 3 个元素按距离正排,它不会排除自身
127.0.0.1:6379> georadiusbymember company ireader 20 km count 3 asc
1) "ireader"
2) "juejin"
3) "meituan"
# 范围 20 公里以内最多 3 个元素按距离倒排
127.0.0.1:6379> georadiusbymember company ireader 20 km count 3 desc
1) "jd"
2) "meituan"
3) "juejin"
# 三个可选参数 withcoord withdist withhash 用来携带附加参数
# withdist 很有用,它可以用来显示距离
127.0.0.1:6379> georadiusbymember company ireader 20 km withcoord withdist withhash count 3 asc
1) 1) "ireader"
 2) "0.0000"
 3) (integer) 4069886008361398
 4) 1) "116.5142020583152771"
 2) "39.90540918662494363"
2) 1) "juejin"
 2) "10.5501"
 3) (integer) 4069887154388167
 4) 1) "116.48104995489120483"
 2) "39.99679348858259686"
3) 1) "meituan"
 2) "11.5748"
 3) (integer) 4069887179083478
 4) 1) "116.48903220891952515"
 2) "40.00766997707732031"

除了 georadiusbymember 指令根据元素查询附近的元素,Redis 还提供了根据坐标值来查询附近的元素,这个指令更加有用,它可以根据用户的定位来计算「附近的车」,「附近的餐馆」等。它的参数和 georadiusbymember 基本一致,除了将目标元素改成经纬度坐标值。

127.0.0.1:6379> georadius company 116.514202 39.905409 20 km withdist count 3 asc
1) 1) "ireader"
 2) "0.0000"
2) 1) "juejin"
 2) "10.5501"
3) 1) "meituan"
 2) "11.5748"
小结 & 注意事项

在一个地图应用中,车的数据、餐馆的数据、人的数据可能会有百万千万条,如果使用Redis 的 Geo 数据结构,它们将全部放在一个 zset 集合中。在 Redis 的集群环境中,集合可能会从一个节点迁移到另一个节点,如果单个 key 的数据过大,会对集群的迁移工作造成较大的影响,在集群环境中单个 key 对应的数据量不宜超过 1M,否则会导致集群迁移出现卡顿现象,影响线上服务的正常运行。

所以,这里建议 Geo 的数据使用单独的 Redis 实例部署,不使用集群环境。

如果数据量过亿甚至更大,就需要对 Geo 数据进行拆分,按国家拆分、按省拆分,按市拆分,在人口特大城市甚至可以按区拆分。这样就可以显著降低单个 zset 集合的大小。

三、python使用Redis的Geo模块

#redis.Redis.******

pool = redis.ConnectionPool(host='10.3.*.*', port=6379,password='123456', decode_responses=True)   # 连接redis
redis_connect = redis.Redis(connection_pool=pool)
redis_connect.geoadd()
 Methods inherited from redis.commands.core.GeoCommands:
    
  ### 1.添加元素,geohash编码   将指定的地理空间项添加到由“`name``参数标识的指定键。地理空间项目作为``values``参数的 有序 成员提供,每个项目或位置由空间坐标轴经度、纬度和名称组成。values是有序的。-------# values的参数依次是经度、元素名、纬度
  geoadd(self, name, values, nx=False, xx=False, ch=False)
        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 by
        the triad longitude, latitude and name.
        
        # 提示:可以使用ZREM删除元素,其本质是使用zset存储
        Note: You can use ZREM to remove elements.
        
        # nx=True时只创建新元素,不会更新现有元素的值。正常默认为false,会更新现有元素的值
        ``nx`` forces ZADD to only create new elements and not to update
        scores for elements that already exist.
        
        # xx=true时强制只会更新现有元素的score,不会创建新的元素。正常默认为true,可以正常创建元素
        ``xx`` forces ZADD to only update scores of elements that already
        exist. New elements will not be added.
        
        # ch=true时会将返回值修改为更改的元素数量。更改的元素包括添加的新元素和score更改的元素。
        ``ch`` modifies the return value to be the numbers of elements changed.
        Changed elements include new elements that were added and elements
        whose scores changed.
        
        For more information check https://redis.io/commands/geoadd
 
 # 2.返回``name``键的``place1````place2``成员之间的距离。unit必须是以下单位之一: m, km mi, ft。默认情况下使用m。
    geodist(self, name, place1, place2, unit=None)
        Return the distance between ``place1`` and ``place2`` members of the
        ``name`` key.
        The units must be one of the following : m, km mi, ft. By default
        meters are used.
        
        For more information check https://redis.io/commands/geodist


# 3.返回由``name``参数标识的指定键的``values``成员的每个项的geohash字符串。
    geohash(self, name, *values)
        Return the geo hash string for each item of ``values`` members of
        the specified key identified by the ``name`` argument.
        
        For more information check https://redis.io/commands/geohash
  
# 4.返回每个``values``项的位置,作为``name``参数标识的指定键的成员。每个位置由lon和lat对表示。  
    geopos(self, name, *values)
        Return the positions of each item of ``values`` as members of
        the specified key identified by the ``name`` argument. Each position
        is represented by the pairs lon and lat.
        
        For more information check https://redis.io/commands/geopos

### 5.返回由``name``参数标识的指定键的成员,这些成员位于由``纬度````经度``位置指定的区域的边界内,并且距离由``半径``值指定的中心的最大距离。-----返回在以(lon,lat)为中心的,以radis为半径的范围内的value元素
    georadius(self, name, longitude, latitude, radius, unit=None, withdist=False, withcoord=False, withhash=False, count=None, sort=None, store=None, store_dist=None, any=False)
        Return the members 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 maximum
        distance from the center specified by the ``radius`` value.
        
        # 单位可以是m, km mi, ft,默认是m
        The units must be one of the following : m, km mi, ft. By default
        
        # withdist=true表示返回每个位置的距离,默认为false,不返回。
        ``withdist`` indicates to return the distances of each place.
        
        # withcoord=true表示返回每个地方的纬度和经度,默认为false,不返回。
        ``withcoord`` indicates to return the latitude and longitude of
        each place.
        
        # withhash=true表示返回每个位置的geohash字符串,默认为false,不返回。
        ``withhash`` indicates to return the geohash string of each place.
        
        # count=N表示返回不超过N的元素数,默认不限制个数
        ``count`` indicates to return the number of elements up to N.
       
       ### sort表示按排序方式返回位置,sort=ASC表示最近到最远,sort=DESC表示最远到最近。
        ``sort`` indicates to return the places in a sorted way, ASC for
        nearest to fairest and DESC for fairest to nearest.
        
        # store表示要将地名保存在以特定键命名的排序集中,目标排序集中的每个元素都将填充从原始地理排序集中获得的score。
        ``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表示将地名(value)元素保存在用特定键命名的排序集中,而不是``store``排序集,目的地分数是用距离设置的。
        ``store_dist`` indicates to save the places names in a sorted set
        named with a specific key, instead of ``store`` the sorted set
        destination score is set with the distance.
        
        For more information check https://redis.io/commands/georadius

### 6.此命令与“georadius”完全相同,唯一的区别是,它不以经度和纬度值作为要查询的区域的中心,而是采用已排序集表示的地理空间索引中已存在的成员的名称。---以value元素作为中心
    georadiusbymember(self, name, member, radius, unit=None, withdist=False, withcoord=False, withhash=False, count=None, sort=None, store=None, store_dist=None, any=False)
        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.
        
        For more information check https://redis.io/commands/georadiusbymember

###### 7. 返回由``name``参数标识的指定键的成员,这些成员位于给定形状指定区域的边界内。此命令扩展了GEORADIUS命令,因此除了在圆形区域内搜索外,它还支持在矩形区域内搜索。应使用此命令代替不推荐使用的GEORADIUS和GEORADIUSBYMEMBER命令。--------------支持在矩形区域内搜索
    geosearch(self, name, member=None, longitude=None, latitude=None, unit='m', radius=None, width=None, height=None, sort=None, count=None, any=False, withcoord=False, withdist=False, withhash=False)
        Return the members of specified key identified by the
        ``name`` argument, which are within the borders of the
        area specified by a given shape. This command extends the
        GEORADIUS command, so in addition to searching within circular
        areas, it supports searching within rectangular areas.
        This command should be used in place of the deprecated
        GEORADIUS and GEORADIUSBYMEMBER commands.
        
        # member使用集合中现存元素的位置??作为中心,不能与``longitude`` and ``latitude`` 参数同时使用
        ``member`` Use the position of the given existing
         member in the sorted set. Can't be given with ``longitude``
         and ``latitude``.
         
        # ``longitude`` and ``latitude``指定经纬度作为中心 ,不能与member参数同时使用
        ``longitude`` and ``latitude`` Use the position given by
        this coordinates. Can't be given with ``member``
        
        # radius指定圆形区域的半径,不能与``height`` and ``width``参数同时使用
        ``radius`` Similar to GEORADIUS, search inside circular
        area according the given radius. Can't be given with
        ``height`` and ``width``.
        
        # ``height`` and ``width``在轴对齐的矩形内搜索,由给定的高度和宽度确定。不能与radius参数同时使用
        ``height`` and ``width`` Search inside an axis-aligned
        rectangle, determined by the given height and width.
        Can't be given with ``radius``
        
        # unit单位必须是一下m, km, mi, ft.
        ``unit`` must be one of the following : m, km, mi, ft.
        `m` for meters (the default value), `km` for kilometers,
        `mi` for miles and `ft` for feet.
        
        # sort表示以一种排序的方式返回元素,sort=ASC代表从最近到最远,sort=DESC代表从最远到最近
        ``sort`` indicates to return the places in a sorted way,
        ASC for nearest to farest and DESC for farest to nearest.
        
        # `count``将结果限制为匹配项的第一个计数。 
        ``count`` limit the results to the first count matching items.
        
        # ``如果将any``设置为True,则只要找到足够的匹配项(count计数),命令就会返回。没有count参数就无法使用
        ``any`` is set to True, the command will return as soon as
        enough matches are found. Can't be provided without ``count``
        
        # ``withdist=true``表示返回每个位置的距离。
        ``withdist`` indicates to return the distances of each place.
        
        # ``withcoord=true``指示返回每个地方的纬度和经度。
        ``withcoord`` indicates to return the latitude and longitude of
        each place.
        
        # ``withhash`` 表示返回每个地方的hash编码
        ``withhash`` indicates to return the geohash string of each place.
        
        For more information check https://redis.io/commands/geosearch

# 8. 此命令类似于GEOSEARCH,但将结果存储在“dest”中。默认情况下,它将结果与其地理空间信息一起存储在目标排序集中。如果``store_dist``设置为True,则该命令将存储已排序集合中的项目,以浮点数的形式填充到圆或方框中心的距离。----将排序结果存储在dest集合中
    geosearchstore(self, dest, name, member=None, longitude=None, latitude=None, unit='m', radius=None, width=None, height=None, sort=None, count=None, any=False, storedist=False)
        This command is like GEOSEARCH, but stores the result in
        ``dest``. By default, it stores the results in the destination
        sorted set with their geospatial information.
        if ``store_dist`` set to True, the command will stores the
        items in a sorted set populated with their distance from the
        center of the circle or box, as a floating-point number.
        
        For more information check https://redis.io/commands/geosearchstore
    

四、Python的geohash编码包

安装:

方式一:不推荐!!Geohash-1.0
(odc) [mca@clu01 ~]$ pip install geohash

# 存在问题
(odc) [mca@clu01 ~]$ python
Python 3.8.12   packaged by conda-forge   (default, Oct 12 2021, 21:59:51) 
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import geohash
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'geohash'

解决问题:

(odc) [mca@clu01 miniconda3]$ find . -name "Geohash"
(odc) [mca@clu01 miniconda3]$ cd envs/odc/lib/python3.8/site-packages/Geohash
(odc) [mca@clu01 Geohash]$ ll
total 8
-rw-rw-r--. 1 mca mca 4021 Jan 17 21:14 geohash.py
-rw-rw-r--. 1 mca mca  839 Jan 17 21:14 __init__.py
drwxrwxr-x. 2 mca mca   67 Jan 17 21:14 __pycache__
(odc) [mca@clu01 Geohash]$ cd ..
(odc) [mca@clu01 site-packages]$ mv Geohash geohash
(odc) [mca@clu01 site-packages]$ cd geohash/
(odc) [mca@clu01 geohash]$ vim __init__.py 

# 把from geohash import ****修改为 from .geohash

Python 3.5 安装geohash库后import geohash失败 - 海盗Ora - 博客园 (cnblogs.com)

vinsci/geohash: Python module to decode/encode Geohashes to/from latitude and longitude. See http://en.wikipedia.org/wiki/Geohash (github.com)

方式二:python-geohash

可以避免方案一种存在的bug

(odc) [mca@clu01 ~]$ pip install python-geohash

方法:

FUNCTIONS
# 1.解码,将geohash编码解码成经纬度
    decode(geohash)
        Decode geohash, returning two strings with latitude and longitude
        containing only relevant digits and with trailing zeroes removed.
 
# 2.精确解码,将geohash解码为其精确值,包括结果的误差范围。返回四个浮点值:纬度、经度、纬度的正负误差(正数)和经度的正负误差(正数)。
    decode_exactly(geohash)
        Decode the geohash to its exact values, including the error
        margins of the result.  Returns four float values: latitude,
        longitude, the plus/minus error for latitude (as a positive
        number) and the plus/minus error for longitude (as a positive
        number).

# 3.编码,将经纬度编码成geohash编码,可以指定编码位数(与精度有关)
    encode(latitude, longitude, precision=12)
        Encode a position given in float arguments latitude, longitude to
        a geohash which will have the character count precision.

# 返回x的以10为底的对数。
    log10(x, /)
        Return the base 10 logarithm of x.
方式三:安装mzgeohash
(odc) [mca@clu01 ~]$ pip install mz2geohash

遇到问题的解决方式如下:

(41条消息) Python 安装mzgeohash包失败,包名冲突_Nougats的博客-CSDN博客

方式四:安装mz2geohash
(odc) [mca@clu01 ~]$ pip install mz2geohash

方法:

FUNCTIONS
# 1.返回给定方向的相邻geohash
    adjacent(geohash, direction)
        Return the adjacent geohash for a given direction.
  
# 2.解码geohash。返回(lon,lat)对。
    decode(value)
        Decode a geohash. Returns a (lon,lat) pair.
  
# 3.将(lon,lat)对编码为GeoHash。
    encode(lonlat, length=12)
        Encode a (lon,lat) pair to a GeoHash.
  
# 4.返回所有的邻居geohash编码
    neighbors(geohash)
        Return all neighboring geohashes.
 
 #   
    neighborsfit(centroid, points)

实例:

>>> import mz2geohash
>>> mz2geohash.decode('xn76urwe1g9y')
(139.76608408614993, 35.681382017210126)
>>> mz2geohash.encode((139.76608408614993, 35.681382017210126))
'xn76urwe1g9y'
>>> mz2geohash.neighbors('xn76urwe1g9y')
{'c': 'xn76urwe1g9y',
 'e': 'xn76urwe1gdn',
 'n': 'xn76urwe1g9z',
 'ne': 'xn76urwe1gdp',
 'nw': 'xn76urwe1g9x',
 's': 'xn76urwe1g9v',
 'se': 'xn76urwe1gdj',
 'sw': 'xn76urwe1g9t',
 'w': 'xn76urwe1g9w'}

https://github.com/jason-h-35/mapzen-geohash

  • 1
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值