redis GEO地理位置命令介绍

GEOADD keylongitude latitude member [longitude latitude member ...]

Adds the specified geospatial items (latitude, longitude, name) to the specified key. Data is stored into the key as a sorted set, in a way that makes it possible to later retrieve items using a query by radius with the GEORADIUS or GEORADIUSBYMEMBER commands.

The command takes arguments in the standard format x,y so the longitude must be specified before the latitude. There are limits to the coordinates that can be indexed: areas very near to the poles are not indexable. The exact limits, as specified by EPSG:900913 / EPSG:3785 / OSGEO:41001 are the following:

  • Valid longitudes are from -180 to 180 degrees.
  • Valid latitudes are from -85.05112878 to 85.05112878 degrees.

The command will report an error when the user attempts to index coordinates outside the specified ranges.

Note: there is no GEODEL command because you can use ZREM in order to remove elements. The Geo index structure is just a sorted set.

How does it work?

The way the sorted set is populated is using a technique called Geohash. Latitude and Longitude bits are interleaved in order to form an unique 52 bit integer. We know that a sorted set double score can represent a 52 bit integer without losing precision.

This format allows for radius querying by checking the 1+8 areas needed to cover the whole radius, and discarding elements outside the radius. The areas are checked by calculating the range of the box covered removing enough bits from the less significant part of the sorted set score, and computing the score range to query in the sorted set for each area.

What Earth model does it use?

It just assumes that the Earth is a sphere, since the used distance formula is the Haversine formula. This formula is only an approximation when applied to the Earth, which is not a perfect sphere. The introduced errors are not an issue when used in the context of social network sites that need to query by radius and most other applications. However in the worst case the error may be up to 0.5%, so you may want to consider other systems for error-critical applications.

Return value

Integer reply, specifically:

  • The number of elements added to the sorted set, not including elements already existing for which the score was updated.

Examples

redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
(integer) 2
redis> GEODIST Sicily Palermo Catania
"166274.1516"
redis> GEORADIUS Sicily 15 37 100 km
1) "Catania"
redis> GEORADIUS Sicily 15 37 200 km
1) "Palermo"
2) "Catania"
redis> 

 翻译:

自3.2.0起可用。

每个添加项目的时间复杂度: O(log(N)),其中N是排序集中的元素数。

将指定的地理空间项目(纬度,经度,名称)添加到指定的键。数据作为排序集存储到密钥中,使得可以使用GEORADIUSGEORADIUSBYMEMBER命令使用半径查询稍后检索项目

该命令采用标准格式x,y的参数,因此必须在纬度之前指定经度。可以索引的坐标有限制:非常靠近极点的区域不可索引。如EPSG:900913 / EPSG:3785 / OSGEO:41001规定的具体限值如下:

  • 有效经度为-180至180度。
  • 有效纬度为-85.05112878至85.05112878度。

当用户尝试对指定范围之外的坐标进行索引时,该命令将报告错误。

注意:没有GEODEL命令,因为您可以使用ZREM来删除元素。地理索引结构只是一个排序集。

它是如何工作的?

排序集的填充方式是使用名为Geohash的技术 纬度和经度位被交织,以便形成唯一的52位整数。我们知道一个排序集合的双分可以代表一个52位整数,而不会失去精度。

该格式允许通过检查覆盖整个半径所需的1 + 8区域,并丢弃半径外的元素来进行半径查询。通过计算被覆盖的区域的范围来检查区域,从排序的设定分数的较不重要的部分中移除足够的位,并且计算每个区域的排序集合中的查询的得分范围。

它使用什么地球模型?

它只是假设地球是一个球体,因为使用的距离公式是Haversine公式。这个公式只适用于地球的近似值,这不是一个完美的球体。当在需要通过半径和大多数其他应用程序查询的社交网站的上下文中使用时,引入的错误不是问题。然而,在最坏的情况下,错误可能高达0.5%,因此您可能需要考虑其他系统的错误关键应用程序。

返回值

整数回复,具体来说:

  • 添加到排序集中的元素数量,不包括已更新分数的元素

geoadd

geoadd用来增加地理位置的坐标,可以批量添加地理位置,命令格式为:

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

key标识一个地理位置的集合。longitude latitude member标识了一个地理位置的坐标。longitude是地理位置的经度,latitude是地理位置的纬度。member是该地理位置的名称。GEOADD可以批量给集合添加一批地理位置。

geopos

geopos可以获取地理位置的坐标,可以批量获取多个地理位置的坐标,命令格式为:

GEOPOS key member [member ...]

geodist

geodist用来获取两个地理位置的距离,命令格式为:

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

单位可以指定为以下四种类型:

  • m:米,距离单位默认为米,不传递该参数则单位为米。
  • km:公里。
  • mi:英里。
  • ft:英尺。

georadius

georadius可以根据给定地理位置坐标获取指定范围内的地理位置集合。命令格式为:

GEORADIUS key longitude latitude radius [m|km|ft|mi] [WITHCOORD] [WITHDIST] [ASC|DESC] [WITHHASH] [COUNT count]

longitude latitude标识了地理位置的坐标,radius表示范围距离,距离单位可以为m|km|ft|mi,还有一些可选参数:

  • WITHCOORD:传入WITHCOORD参数,则返回结果会带上匹配位置的经纬度。
  • WITHDIST:传入WITHDIST参数,则返回结果会带上匹配位置与给定地理位置的距离。
  • ASC|DESC:默认结果是未排序的,传入ASC为从近到远排序,传入DESC为从远到近排序。
  • WITHHASH:传入WITHHASH参数,则返回结果会带上匹配位置的hash值。
  • COUNT count:传入COUNT参数,可以返回指定数量的结果。

georadiusbymember

georadiusbymember可以根据给定地理位置获取指定范围内的地理位置集合。georadius命令传递的是坐标,georadiusbymember传递的是地理位置。georadius更为灵活,可以获取任何坐标点范围内的地理位置。但是大多数时候,只是想获取某个地理位置附近的其他地理位置,使用georadiusbymember则更为方便。georadiusbymember命令格式为(命令可选参数与georadius含义一样):

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

geohash

geohash可以获取某个地理位置的geohash值。geohash是将二维的经纬度转换成字符串hash值的算法,后面会具体介绍geohash原理。可以批量获取多个地理位置的geohash值。命令格式为:

GEOHASH key member [member ...]

redis GEO实现

redis GEO实现主要包含了以下两项技术:

  • 1、使用geohash保存地理位置的坐标。
  • 2、使用有序集合(zset)保存地理位置的集合。

geohash

geohash的思想是将二维的经纬度转换成一维的字符串,geohash有以下三个特点:

  • 1、字符串越长,表示的范围越精确。编码长度为8时,精度在19米左右,而当编码长度为9时,精度在2米左右。
  • 2、字符串相似的表示距离相近,利用字符串的前缀匹配,可以查询附近的地理位置。这样就实现了快速查询某个坐标附近的地理位置。
  • 3、geohash计算的字符串,可以反向解码出原来的经纬度。

这三个特性让geohash特别适合表示二维hash值。这篇文章:GeoHash核心原理解析详细的介绍了geohash的原理,想要了解geohash实现的朋友可以参考这篇文章。

redis GEO命令实现

知道了redis使用有序集合(zset)保存地理位置数据(想了解redis有序集合的,可以参看这篇文章《有序集合对象》),以及geohash的特性,就很容易理解redis是如何实现redis GEO命令了。细心的读者可能发现,redis没有实现地理位置的删除命令。不过由于GEO数据保存在zset中,可以用zrem来删除某个地理位置。

  • geoadd命令增加地理位置的时候,会先计算地理位置坐标的geohash值,然后地理位置作为有序集合的member,geohash作为该member的score。然后使用zadd命令插入到有序集合。
  • geopos命令则先根据地理位置获取geohash值,然后decode得到地理位置的坐标。
  • geodist命令先根据两个地理位置各自得到坐标,然后计算两个坐标的距离。
  • georadius和georadiusbymember使用相同的实现,georadiusbymember多了一步把地理位置转换成对应的坐标。然后查找该坐标和周围对应8个坐标符合距离要求的地理位置。因为geohash得到的值其实是个格子,并不是点,这样通过计算周围对应8个坐标就能解决边缘问题。由于使用有序集合保存地理位置,在对地列位置基于范围查询,就相当于实现了zrange命令,内部的实现确实与zrange命令一致,只是geo有些特别的处理,比如获得的某个地理位置,还需要计算该地理位置是否符合给定的距离访问。
  • geohash则直接返回了地理位置的geohash值。

redis关于geohash使用了Ardb的geohash库geohash-int,redis使用的geohash编码长度为26位。可以精确到0.59m的精度。

总结

通过本文,拨开GEO身后的云雾,可以看出redis借助了有序集合(zset)和geohash,加上redis本身实现的命令框架,可以很容易的实现地理位置相关的命令。

参考文献:

 

posted @ 2017-10-05 17:07  开始战斗  阅读(6933)  评论(0编辑  收藏  举报