Redis的GEO

GEO简介:
移动互联网时代LBS应用越来越多,交友软件中附近的小姐姐、外卖软件中附近的美食店铺、打车软件附近的车辆等等,那这种附近各种形形色色的XXX地址位置选择是如何实现的?

地球上的地理位置是使用二维的经纬度表示,经度范围 (-180, 180],纬度范围 (-90, 90],只要我们确定一个点的经纬度就可以名曲他在地球的位置。
例如滴滴打车,最直观的操作就是实时记录更新各个车的位置,
然后当我们要找车时,在数据库中查找距离我们(坐标x0,y0)附近r公里范围内部的车辆

使用如下SQL即可:
select taxi from position where x0-r < x < x0 + r and y0-r < y < y0+r

但是这样会有什么问题呢?
1.查询性能问题,如果并发高,数据量大这种查询是要搞垮数据库的
2.这个查询的是一个矩形访问,而不是以我为中心r公里为半径的圆形访问。
3.精准度的问题,我们知道地球不是平面坐标系,而是一个圆球,这种矩形计算在长距离计算时会有很大误差

Redis在3.2版本以后增加了地理位置的处理

原理:
核心思想就是将球体转换为平面,区块转换为一点

主要分为三步
将三维的地球变为二维的坐标
在将二维的坐标转换为一维的点块
最后将一维的点块转换为二进制再通过base32编码
难点:
GeoHash核心原理解析:https://www.cnblogs.com/LBSer/p/3310455.html
地理知识说明:
经纬度:
https://baike.baidu.com/item/%E7%BB%8F%E7%BA%AC%E7%BA%BF/5596978?fr=aladdin
查询:
https://jingweidu.bmcx.com/

命令:
GEOADD 多个经度(longitude)、纬度(latitude)、位置名称(member)添加到指定的 key 中
GEOPOS 从键里面返回所有给定位置元素的位置(经度和纬度)
GEODIST 返回两个给定位置之间的距离。
GEORADIUS 以给定的经纬度为中心, 返回与中心的距离不超过给定最大距离的所有位置元素。
GEORADIUSBYMEMBER 跟GEORADIUS类似
GEOHASH返回一个或多个位置元素的 Geohash 表示

命令实操:
如何获得某个地址的经纬度:
http://api.map.baidu.com/lbsapi/getpoint/
https://jingweidu.bmcx.com/

GEOADD添加经纬度坐标:
在这里插入图片描述
在这里插入图片描述

命令如下:
GEOADD city 116.403963 39.915119 “天安门” 116.403414 39.924091 “故宫” 116.024067 40.362639 “长城”

中文乱码如何处理:
在这里插入图片描述

GEOPOS返回经纬度:
在这里插入图片描述
在这里插入图片描述
GEOHASH返回坐标的geohash表示:
在这里插入图片描述
在这里插入图片描述
geohash算法生成的base32编码值,3维变2维变1维

GEODIST 两个位置之间距离:
在这里插入图片描述
在这里插入图片描述
后面参数是距离单位:
m 米
km 千米
ft 英尺
mi 英里

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

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

当前位置(116.418017 39.914402),在王府井
在这里插入图片描述
以半径为中心,查找附近的XXX

GEORADIUSBYMEMBER

在这里插入图片描述
在这里插入图片描述
美团地图位置附近的酒店推送?

需求分析:
微信附近的人或者一公里以内的各种营业厅、加油站、理发店、超市…
找个单车
附近的酒店

架构设计:
Redis的新类型GEO http://www.redis.cn/commands/geoadd.html

编码实现:关键点
在这里插入图片描述
code:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
import org.springframework.data.geo.Circle;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
public class GeoController
{
    public  static final String CITY ="city";

    @Autowired
    private RedisTemplate redisTemplate;

    @RequestMapping("/geoadd")
    public String geoAdd()
    {
        Map<String, Point> map= new HashMap<>();
        map.put("天安门",new Point(116.403963,39.915119));
        map.put("故宫",new Point(116.403414 ,39.924091));
        map.put("长城" ,new Point(116.024067,40.362639));

        redisTemplate.opsForGeo().add(CITY,map);

        return map.toString();
    }

    @GetMapping(value = "/geopos")
    public Point position(String member) {
        //获取经纬度坐标
        List<Point> list= this.redisTemplate.opsForGeo().position(CITY,member);
        return list.get(0);
    }

    @GetMapping(value = "/geohash")
    public String hash(String member) {
        //geohash算法生成的base32编码值
        List<String> list= this.redisTemplate.opsForGeo().hash(CITY,member);
        return list.get(0);
    }

    @GetMapping(value = "/geodist")
    public Distance distance(String member1, String member2) {
        Distance distance= this.redisTemplate.opsForGeo().distance(CITY,member1,member2, RedisGeoCommands.DistanceUnit.KILOMETERS);
        return distance;
    }

    /**
     * 通过经度,纬度查找附近的
     * 北京王府井位置116.418017,39.914402
     */
    @GetMapping(value = "/georadius")
    public GeoResults radiusByxy() {
        //这个坐标是北京王府井位置
        Circle circle = new Circle(116.418017, 39.914402, Metrics.KILOMETERS.getMultiplier());
        //返回50条
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates().sortAscending().limit(50);
        GeoResults<RedisGeoCommands.GeoLocation<String>> geoResults= this.redisTemplate.opsForGeo().radius(CITY,circle, args);
        return geoResults;
    }

    /**
     * 通过地方查找附近
     */
    @GetMapping(value = "/georadiusByMember")
    public GeoResults radiusByMember() {
        String member="天安门";
        //返回50条
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates().sortAscending().limit(50);
        //半径10公里内
        Distance distance=new Distance(10, Metrics.KILOMETERS);
        GeoResults<RedisGeoCommands.GeoLocation<String>> geoResults= this.redisTemplate.opsForGeo().radius(CITY,member, distance,args);
        return geoResults;
    }
}
 
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值