简介

  • redis3.2版本提供了GEO(地理信息定位)功能,支持存储地理位置信息。
  • 可以用来实现比如附近位置,摇一摇这类依附于地理位置的功能
  • GEO功能是 Redis 的另一位作者Matt Stancliff 借鉴 NoSQL 数据库 Ardb 实现的,Ardb 的作者来自中国,它提供了优秀的GEO功能。

命令

geoadd :添加/更新地理位置的坐标

作用

  • geoadd 用于存储指定的地理空间位置
  • 可以将一个或多个经度(longitude)、纬度(latitude)、位置名称(member)添加到指定的 key 中。
  • 这些数据会以有序集合的形式被储存在键里面, 从而使得像 GEORADIUSGEORADIUSBYMEMBER 这样的命令可以在之后通过位置查询取得这些元素。

注意:

  • GEOADD 命令以标准的 x,y 格式接受参数, 所以用户必须先输入经度, 然后再输入纬度
  • GEOADD 能够记录的坐标是有限的: 非常接近两极的区域是无法被索引的
  • 有效的经度从-180°到180°
  • 有效的纬度从-85.05112878度到85.05112878度。

当用户尝试输入一个超出范围的经度或者纬度时, GEOADD 命令将返回一个错误

语法

GEOADD key longitude latitude member [longitude latitude member ...]

longitude、latitude、member分别是该地理位置的经度、纬度、成员

返回值

  • 新添加到键里面的空间元素数量,
  • 不包括那些已经存在但是被更新的元素。

实例

127.0.0.1:6379> geoadd cities:locations 116.28 39.55 beijing
(integer) 1
127.0.0.1:6379> geoadd cities:locations 116.28 39.55 beijing
(integer) 0

geoadd可以同时添加多个地理位置信息。

127.0.0.1:6379> geoadd cities:locations 117.12 39.08 tianjin 114.29 38.02 shijiazhuang 118.01 39.38 tangshan 115.29 38.51 baoding
(integer) 4

geopos:获取地理位置信息

作用

  • 从键里面返回所有给定位置元素的位置(经度和纬度)。

语法

返回值

  • GEOPOS 命令返回一个数组, 数组中的每个项都由两个元素组成:

    • 第一个元素为给定位置元素的经度
    • 第二个元素则为给定位置元素的纬度。
  • 当给定的位置元素不存在时, 对应的数组项为空值。

实例

redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
(integer) 2redis> GEOPOS Sicily Palermo Catania NonExisting
1) 1) "13.361389338970184"2) "38.115556395496299"
2) 1) "15.087267458438873"---  2) "37.50266842333162"
3-) (nil)
1-27.0.0.1:6379> geopos cities:locations tianjin
1) 1) "117.12000042200088501"-  2) "39.0800000535766543"

geodist:获取两个地理位置的距离

作用

返回两个给定位置之间的距离。

如果两个位置之间的其中一个不存在, 那么命令返回空值。

指定单位的参数 unit 必须是以下单位的其中一个:

  • m 表示单位为米。
  • km 表示单位为千米。
  • mi 表示单位为英里。
  • ft 表示单位为英尺。

如果用户没有显式地指定单位参数, 那么 GEODIST 默认使用米作为单位。

GEODIST 命令在计算距离时会假设地球为完美的球形, 在极限情况下, 这一假设最大会造成 0.5% 的误差。

语法

GEODIST key member1 member2 [m|km|ft|mi]

最后一个距离单位参数说明:

  • m :米,默认单位。
  • km :千米。
  • mi :英里。
  • ft :英尺。

返回值

  • 计算出的距离会以双精度浮点数的形式被返回。
  • 如果给定的位置元素不存在, 那么命令返回空值。

实例

127.0.0.1:6379> geodist cities:locations beijing tianjin km
"89.2061"
redis> GEODIST Sicily Foo Bar
(nil)

georadius :获取指定位置范围内的地理信息位置集合

georadius 以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。

语法

GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [COUNT count]
  • withcoord:返回结果中包含经纬度;
  • withdist:返回结果中包含离中心节点位置的距离;
  • withhash:返回结果中包含geohash;
  • COUNT count:指定返回结果的数量;
  • asc|desc:指定结果按照离中心节点的距离做升序或者降序;
  • store key:将返回结果的地理位置信息保存到指定键;
  • storedist key:将返回结果离中心离中心节点的距离保存到指定键。

作用

以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。

范围可以使用以下其中一个单位:

  • m 表示单位为米。
  • km 表示单位为千米。
  • mi 表示单位为英里。
  • ft 表示单位为英尺。

在给定以下可选项时, 命令会返回额外的信息:

  • WITHDIST :

    • 在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回。
    • 距离的单位和用户给定的范围单位保持一致。
  • WITHCOORD : 将位置元素的经度和维度也一并返回。
  • WITHHASH :
    • 以 52 位有符号整数的形式, 返回位置元素经过原始 geohash 编码的有序集合分值。
    • 这个选项主要用于底层应用或者调试, 实际中的作用并不大。

命令默认返回未排序的位置元素。 通过以下两个参数, 用户可以指定被返回位置元素的排序方式:

  • ASC : 根据中心的位置, 按照从近到远的方式返回位置元素。
  • DESC : 根据中心的位置, 按照从远到近的方式返回位置元素。

在默认情况下, GEORADIUS 命令会返回所有匹配的位置元素。 虽然用户可以使用 COUNT <count>选项去获取前 N 个匹配元素, 但是因为命令在内部可能会需要对所有被匹配的元素进行处理, 所以在对一个非常大的区域进行搜索时, 即使只使用 COUNT 选项去获取少量元素, 命令的执行速度也可能会非常慢。 但是从另一方面来说, 使用 COUNT 选项去减少需要返回的元素数量, 对于减少带宽来说仍然是非常有用的。

返回值

GEORADIUS 命令返回一个数组, 具体来说:

  • 在没有给定任何 WITH 选项的情况下, 命令只会返回一个像 [“New York”,“Milan”,“Paris”] 这样的线性(linear)列表。
  • 在指定了WITHCOORD 、 WITHDIST 、 WITHHASH等选项的情况下, 命令返回一个二层嵌套数组, 内层的每个子数组就表示一个元素。

在返回嵌套数组时, 子数组的第一个元素总是位置元素的名字。 至于额外的信息, 则会作为子数组的后续元素, 按照以下顺序被返回:

  • 以浮点数格式返回的中心与位置元素之间的距离, 单位与用户指定范围时的单位一致。
  • geohash 整数。

由两个元素组成的坐标,分别为经度和纬度。

举个例子,GEORADIUS Sicily 15 37 200 km withcoord withdist这样的命令返回的每个子数组都是类似以下格式的:

实例

redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
(integer) 2redis> GEORADIUS Sicily 15 37 200 km WITHDIST
1) 1) "Palermo"2) "190.4424"
2) 1) "Catania"2) "56.4413"redis> GEORADIUS Sicily 15 37 200 km WITHCOORD
1) 1) "Palermo"2) 1) "13.361389338970184"2) "38.115556395496299"
2) 1) "Catania"2) 1) "15.087267458438873"2) "37.50266842333162"redis> GEORADIUS Sicily 15 37 200 km WITHDIST WITHCOORD
1) 1) "Palermo"2) "190.4424"3) 1) "13.361389338970184"2) "38.115556395496299"
2) 1) "Catania"2) "56.4413"3) 1) "15.087267458438873"2) "37.50266842333162"

georadiusbymember:获取指定位置范围内的地理信息位置集合

语法

GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [COUNT count]

作用

  • 这个命令和 GEORADIUS 命令一样, 都可以找出位于指定范围内的元素,
  • 但是 GEORADIUSBYMEMBER 的中心点是由给定的位置元素决定的, 而不是像 GEORADIUS 那样, 使用输入的经度和纬度来决定中心点。

返回值

一个数组, 数组中的每个项表示一个范围之内的位置元素。

实例

127.0.0.1:6379> georadiusbymember cities:locations beijing 100 km  #距离北京100km内的城市
1) "beijing"
2) "tianjin"

geohash:将二维经纬度转换为一维字符串

语法

geohash key member [member ... ]

作用

Redis使用geohash将二维经纬度转换为一维字符串

geohash有如下特点:

  • GEO的数据类型为zset,redis将所有地理位置信息的geohash存放在zset中
  • 字符串越长,表示的位置更精确
  • 两个字符串越相似,他们之间的距离越近,Redis利用字符串前缀匹配算法实现相关命令
  • geohash编码和经纬度是可以相互转换的。

Redis正是使用有序集合并结合geohash的特性实现了GEO的若干命令。

返回值

  • 一个数组, 数组的每个项都是一个 geohash 。
  • 命令返回的 geohash 的位置与用户给定的位置元素的位置一一对应。

实例

redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
(integer) 2redis> GEOHASH Sicily Palermo Catania
1) "sqc8b49rny0"
2) "sqdtr74hyu0"

zrem:删除地理位置信息

zrem key member

GEO没有提供删除成员的命令,但是因为GEO的底层实现是zset,所有可以借用zrem实现对地理位置信息的删除。

127.0.0.1:6379> zrem cities:locations tangshan
(integer) 1

GEO的底层结构

存储:sorted set

一般来说,在设计一个数据类型的底层结构时,我们首先要知道,要处理的数据有什么访问特定。所以,我们要先搞清楚位置信息是怎么被存取的。

以叫车服务为例,来分析下 LBS 应用中经纬度的存取特点。

  • 每一辆网约车都有一个编号(例如 33),网约车需要将自己的经度信息(例如116.034579)和纬度信息(例如 39.000452 )发给叫车应用。
  • 用户在叫车的时候,叫车应用会根据用户的经纬度位置(例如经度 116.054579,纬度39.030452),查找用户的附近车辆,并进行匹配。
  • 等把位置相近的用户和车辆匹配上以后,叫车应用就会根据车辆的编号,获取车辆的信息,并返回给用户。

可以看到,一辆车(或一个用户)对应一组经纬度,并且随着车(或用户)的位置移动,相应的经纬度也会变化。

这种数据记录模式属于一个 key(例如车 ID)对应一个 value(一组经纬度)。当有很多车辆信息要保存时,就需要有一个集合来保存一系列的 key 和 value。 Hash集合类型可以快速存取一系列的key和value,正好可以用来记录一系列车辆ID和经纬度的对应关系。所以,我们可以把不同车辆的ID和它们对应的经纬度信息存在Hash集合中。如下图


但是,除了记录经纬度信息,我们还需要根据用户的经纬度信息在车辆的Hash集合中进行范围查询。一旦设计到范围查询,就意味着集合中的元素需要有序,但是Hash类型的元素是无需的,所以不能用hash

那能不能用sorted set呢? Sorted Set 类型也支持一个 key 对应一个 value 的记录模式,其中,key 就是 SortedSet 中的元素,而 value 则是元素的权重分数。更重要的是,Sorted Set 可以根据元素的权重分数排序,支持范围查询。这就能满足 LBS 服务中查找相邻位置的需求了。

实际上,GEO类型的底层数据结构就是用sorted Set来实现的。用 Sorted Set 来保存车辆的经纬度信息时,Sorted Set 的元素是车辆 ID,元素的权重分数是经纬度信息,如下图所示:

这时问题来了,Sorted Set 元素的权重分数是一个浮点数(float 类型),而一组经纬度包含的是经度和纬度两个值,是没法直接保存为一个浮点数的,那具体该怎么进行保存呢?

这就要用到 GEO 类型中的 GeoHash 编码了。

编码方法:GeoHash

GeoHash 编码方法的基本原理是“二分区间,区间编码”

当我们要对一组经纬度进行GeoHash编码时,我们先对经度和维度分别编码,然后再把经纬度各自的编码组成一个最终编码。

首先,我们来看下经度和纬度的单独编码过程。

  • 对于一个地理位置信息来说,它的经度范围是[-180,190]。GeoHash编码对把一个经纬度编码成一个N位的二进制值,我们来对经度范围[-180,180]做 N 次的二分区操作,其中 N可以自定义。

  • 在进行第一次二分区时,经度范围[-180,180]会被分成两个子区间:[-180,0) 和[0,180](左、右分区)。此时,我们可以查看一下要编码的经度值落在了左分区还是右分区。如果是落在左分区,我们就用 0 表示;如果落在右分区,就用 1 表示。这样一来,每做完一次二分区,我们就可以得到 1 位编码值。

  • 然后,我们再对经度值所属的分区再做一次二分区,同时再次查看经度值落在了二分区后的左分区还是右分区,按照刚才的规则再做 1 位编码。当做完 N 次的二分区后,经度值就可以用一个 N bit 的数来表示了。

举个例子,假设我们要编码的经度值是 116.37,我们用 5 位编码值(也就是 N=5,做 5次分区)。

  • 我们先做第一次二分区操作,把经度区间[-180,180]分成了左分区[-180,0) 和右分区[0,180],此时,经度值 116.37 是属于右分区[0,180],所以,我们用 1 表示第一次二分区后的编码值。
  • 接下来,我们做第二次二分区:把经度值 116.37 所属的[0,180]区间,分成[0,90) 和[90,180]。此时,经度值 116.37 还是属于右分区[90,180],所以,第二次分区后的编码值仍然为 1。等到第三次对[90,180]进行二分区,经度值 116.37 落在了分区后的左分区[90, 135)中,所以,第三次分区后的编码值就是 0。
  • 按照这种方法,做完 5 次分区后,我们把经度值 116.37 定位在[112.5, 123.75]这个区间,并且得到了经度值的 5 位编码值,即 11010。这个编码过程如下表所示:

    对纬度的编码方式,和对经度的一样,只是纬度的范围是[-90,90],下面这张表显示了对纬度值 39.86 的编码过程。


当一组经纬度值都编完码后,我们再把它们的各自编码值组合在一起,组合的规则是:最终编码值的偶数位上依次是经度的编码值,奇数位上依次是纬度的编码值,其中,偶数位从 0 开始,奇数位从 1 开始。

我们刚刚计算的经纬度(116.37,39.86)的各自编码值是 11010 和 10111,组合之后,第 0 位是经度的第 0 位 1,第 1 位是纬度的第 0 位 1,第 2 位是经度的第 1 位 1,第 3位是纬度的第 1 位 0,以此类推,就能得到最终编码值 1110011101,如下图所示:

用了 GeoHash 编码后,原来无法用一个权重分数表示的一组经纬度(116.37,39.86)就可以用 1110011101 这一个值来表示,就可以保存为 Sorted Set 的权重分数了。

当然,使用 GeoHash 编码后,我们相当于把整个地理空间划分成了一个个方格,每个方格对应了 GeoHash 中的一个分区。

举个例子。我们把经度区间[-180,180]做一次二分区,把纬度区间[-90,90]做一次二分区,就会得到 4 个分区。我们来看下它们的经度和纬度范围以及对应的 GeoHash 组合编码。

  • 分区一:[-180,0) 和[-90,0),编码 00;
  • 分区二:[-180,0) 和[0,90],编码 01;
  • 分区三:[0,180]和[-90,0),编码 10;
  • 分区四:[0,180]和[0,90],编码 11。

这 4 个分区对应了 4 个方格,每个方格覆盖了一定范围内的经纬度值,分区越多,每个方格能覆盖到的地理空间就越小,也就越精准。我们把所有方格的编码值映射到一维空间时,相邻方格的 GeoHash 编码值基本也是接近的,如下图所示:


所以,我们使用 Sorted Set 范围查询得到的相近编码值,在实际的地理空间上,也是相邻的方格,这就可以实现 LBS 应用“搜索附近的人或物”的功能了。

不过,有的编码值虽然在大小上接近,但实际对应的方格却距离比较远。例如,我们用 4 位来做 GeoHash 编码,把经度区间[-180,180]和纬度区间[-90,90]各分成了 4 个分区,一共 16 个分区,对应了 16 个方格。编码值为 0111 和 1000 的两个方格就离得比较远,如下图所示:


所以,为了避免查询不准确问题,我们可以同时查询给定经纬度所在的方格周围的 4 个或8 个方格。

总结:GEO 类型是把经纬度所在的区间编码作为 Sorted Set 中
元素的权重分数,把和经纬度相关的车辆 ID 作为 Sorted Set 中元素本身的值保存下来,这样相邻经纬度的查询就可以通过编码值的大小范围查询来实现了。

redis:地理位置信息geo相关推荐

  1. Redis之GEO存储地理位置信息

    在外卖软件中的附近的美食店铺.外卖小哥的距离,打车软件附近的车辆,交友软件中附近的小姐姐.我们都可以利用redis的GEO地理位置计算得出. 1.Redis 的 Geo 是在 3.2 版本才有的 2. ...

  2. Redis GEO地理位置信息的应用

    Redis GEO地理位置信息的应用 Redis GEO 概述 应用场景 Redis GEO命令 GEO命令演示 Redis GEO实现附近人的功能 基础类 API接口 接口实现 执行测试 Redis ...

  3. Geo Location 地理位置信息小结

    周末调研了一下Geo Location 地理位置信息方面的内容,自己小结一下. 一.通过 IP 地址获得用户的地理位置信息 也就是根据用户的IP,通过IP数据库查询获得信息.一般IP数据库中, 每条记 ...

  4. Redis学习笔记-GEO经纬度编码原理地理划分

    文章目录 Redis学习笔记-GEO经纬度编码原理&地理划分 1.笔记图 2.GEO 应用场景 3.GEO 数据特点举例 4.GeoHash 的编码方法(二分区间,区间编码) 5.GEO 经纬 ...

  5. 获取用户精准地理位置信息(百度地图)

    获取用户精准地理位置信息步骤: 1.通过 navigator.geolocation.getCurrentPosition(showPosition, showError); 方法获取经纬度: 2.使 ...

  6. ES地理范围查询第一讲:Java操作地理位置信息(geo_point)

    - 简要介绍 我们知道es支持的数据类型是多种多样的,除了我们常见的几种基本数据类型,它也支持记录位置信息的的数据类型.在es中,记录地理位置信息的数据类型有两种,分别为geo_shap和geo_po ...

  7. H5地理位置信息、微信摇一摇

    geolocation window.navigator.geolocation 1.getCurrentPosition() // 获取当前的位置信息 2.watchPosition() // 监视 ...

  8. html5实现获取地理位置信息并定位

    html5实现获取地理位置信息并定位 一.总结 一句话总结:获取地理位置是分两步:获取经纬度和根据经纬度获取地址信息 a.HTML5的Geolocation(地理位置功能(Geolocation AP ...

  9. Android通过百度地图API用Service和Alarm在后台定时获取地理位置信息

    本文主要介绍了Android项目集成百度地图API,使用AlarmManager定时调用Service,在Service中请求坐标更新,并通过坐标得到省.市和县三级地理位置信息的方法. 程序结构很简单 ...

最新文章

  1. python小工具myqr生成动态二维码
  2. 服务器系统装软路由,服务器系统设置软路由
  3. 常用公有云接入——谷歌
  4. 景区门票系统上云 低成本、安全性高
  5. Python小白的数学建模课-22.插值方法
  6. Play 2.0 用户指南 - 表单提交和验证 --针对Scala开发者
  7. reg 正则表达式^
  8. 通过u盘启动计算机使用ghost安装系统步骤,ghost怎么重装系统 使用ghost重装系统步骤...
  9. Centos7完全卸载MySQL 安装 启动
  10. 产品画的Axure原型图打不开解决办法
  11. 金融学习之十一——久期
  12. 余吉磊——湖南高尔夫旅游职业学院游戏爱好者
  13. 机器学习项目实战——08浅层网络之葡萄酒分类
  14. Emacs,最强编辑器,没有之一
  15. 服务器技术的发展方向
  16. mac访达中显示隐藏的文件夹和文件
  17. 新浪微博--分享到微博的简单使用
  18. Ubuntu 20.04 boot repair(镜像引导目录修复)
  19. Windows Service 介绍
  20. Mac壁纸精灵Wallpaper Wizard如何保存我喜欢的图片?

热门文章

  1. Springboot 中文文档 —— Actuator
  2. Win软件 - 这可能是首款能在电脑上控制 iPhone 的工具
  3. 1、Anaconda安装TensorFlow(一)
  4. PlayMaker之开发案例
  5. ubuntu系统配置中文输入法以及安装ros2,docker等开发环境
  6. java 动物声音模拟器_动物声音模拟器软件
  7. Keil中配置GCC编译器开发STM32
  8. 西门子博途v16系统要求_【技成周报30期】西门子系列常见问题答疑汇总
  9. APP图标制作-PS如何倒70圆角
  10. 邮箱前缀是什么?该怎么设置?