通过经纬度计算距离实现附近、附近的人等功能

博客 分享
0 230
张三
张三 2022-01-28 16:54:40
悬赏:0 积分 收藏

通过经纬度计算距离实现附近、附近的人等功能

背景:附近功能在很多生活类的App或软件中经常出现?那他们是怎么实现的呢?如果数据量不是很大,且功能比较简单,基于MySQL就可以实现。然而很多时候数据量很大且功能复杂,那么我们就需要使用Elasticsearch这种数据库了,不仅功能丰富,而且性能强大,大数据量情况下性能不再是问题。


一、基于MySQL实现(8.0.28)
1、建表

DROP TABLE IF EXISTS `t_city`;CREATE TABLE `t_city` (`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',`city_code` varchar(20) NOT NULL DEFAULT '' COMMENT '城市编码',`city_name` varchar(20) NOT NULL DEFAULT '' COMMENT '城市名称',`addr` varchar(255) NOT NULL DEFAULT '' COMMENT '详细地址',`lat` decimal(12,8) DEFAULT NULL COMMENT '纬度',`lon` decimal(12,7) DEFAULT NULL COMMENT '经度',`efficiency_level` varchar(2) DEFAULT '' COMMENT '效率评级',`credit_level` varchar(2) DEFAULT NULL COMMENT '信用评级',PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=937 DEFAULT CHARSET=utf8mb4;

2、插入测试数据

INSERT INTO `hb-test`.`t_city` (`city_code`, `city_name`, `addr`, `lat`, `lon`, `efficiency_level`, `credit_level`) VALUES ('1', '北京', '北海公园', null, null, 'A', '1');INSERT INTO `hb-test`.`t_city` (`city_code`, `city_name`, `addr`, `lat`, `lon`, `efficiency_level`, `credit_level`) VALUES ('2', '西安', '西安火车站', null, null, 'A', '2');
INSERT INTO `hb-test`.`t_city` (`city_code`, `city_name`, `addr`, `lat`, `lon`, `efficiency_level`, `credit_level`) VALUES ('1', '北京', '北海公园', '39.93463300', '116.3927710', 'A', '1');INSERT INTO `hb-test`.`t_city` (`city_code`, `city_name`, `addr`, `lat`, `lon`, `efficiency_level`, `credit_level`) VALUES ('2', '西安', '西安火车站', '34.28405900', '108.9688710', 'A', '2');INSERT INTO `hb-test`.`t_city` (`city_code`, `city_name`, `addr`, `lat`, `lon`, `efficiency_level`, `credit_level`) VALUES ('2', '西安', '西安北站', '34.38171600', '108.9452810', 'B', '3');INSERT INTO `hb-test`.`t_city` (`city_code`, `city_name`, `addr`, `lat`, `lon`, `efficiency_level`, `credit_level`) VALUES ('2', '西安', '电子正街', '34.21045200', '108.9257140', 'C', '2');INSERT INTO `hb-test`.`t_city` (`city_code`, `city_name`, `addr`, `lat`, `lon`, `efficiency_level`, `credit_level`) VALUES ('2', '西安', '零壹广场', '34.23076900', '108.8875360', 'D', '1');INSERT INTO `hb-test`.`t_city` (`city_code`, `city_name`, `addr`, `lat`, `lon`, `efficiency_level`, `credit_level`) VALUES ('2', '西安', '软件新城', '34.21228800', '108.8411110', 'D', '2');

3、第一种查询sql:基于球面距离公式

纬度最大是90度,大于90度的一定是经度。百度地图上的坐标拾取。精度在前,和经纬度的字面意思一致
地球平均半径约:6371km 最大半径(赤道半径)约:6378km
#以西安北站(108.945281,34.381716)为原点,计算出有经纬度的城市距离当前远点的距离并正序排列
SELECT    id,    city_name,    addr,    lat,    lon,    efficiency_level,    credit_level,    ROUND(        6378 * 2 * ASIN(            SQRT(                POW(                    SIN(                        (                            34.381716 * PI() / 180 - lat * PI() / 180                        ) / 2                    ),                    2                ) + COS(34.381716 * PI() / 180) * COS(lat * PI() / 180) * POW(                    SIN(                        (                            108.945281 * PI() / 180 - lon * PI() / 180                        ) / 2                    ),                    2                )            )        ) * 1000    ) AS distanceFROM    t_cityWHERE lat IS NOT NULLORDER BY    distance ASC,    efficiency_level DESC,    credit_level ASCLIMIT 2000 

4、第二种查询sql:基于mysql自带函数st_distance

#以西安北站(108.945281,34.381716)为原点,计算出有经纬度的城市距离当前远点的距离并正序排列
SELECT    *, (        st_distance (            point (lon, lat),            point (108.945281, 34.381716)        ) * 6378*1000*PI()/180    ) AS distanceFROM    t_citywhere lon IS NOT NULLORDER BY    distance ASCLIMIT 2000

二、通过Elasticsearch(7.15.1)的geo_point实现
1、创建索引mapping

PUT t_city{  "settings": {    "number_of_shards": 1,    "number_of_replicas": 1  },  "mappings": {    "properties": {      "addr": {        "type": "keyword"      },      "city_code": {        "type": "keyword"      },      "city_name": {        "type": "keyword"      },      "id": {        "type": "keyword"      },      "location": {        "type": "geo_point"      },      "time": {        "type": "date"      }    }  }}

2、插入测试数据

POST /t_city/_bulk
{"index":{}}
{"city_code":"1","city_name":"北京","addr":"北海公园","time":"2022-01-23","location":{"lat":39.934633,"lon":39.934633}}
{"index":{}}
{"city_code":"2","city_name":"西安","addr":"西安火车站","time":"2022-01-23","location":{"lat":34.284059,"lon":108.96887}}
{"index":{}}
{"city_code":"2","city_name":"上海","addr":"上海虹桥机场","time":"2022-01-23","location":{"lat":31.203347,"lon":121.346817}}

3、geo_distance 询问:查找在中心点指定距离内的地理点(更多用法参考官方文档

#以西安北站(108.94528,34.381716)为原点,查询距离西安北站100km以内的10个点
GET /t_city/_search{  "size": 10,  "query": {    "bool": {      "filter": [        {          "geo_distance": {            "distance": "100km",            "location": {              "lat": 34.381716,              "lon": 108.945281            }          }        }      ]    }  }}

4、距离范围内的桶聚合(更多功能参考官方文档

#以西安北站(108.94528,34.381716)为原点

 #查询落在(*-100000),(100000-300000),(300000-*)的点

GET /t_city/_search{  "size": 1,  "track_total_hits": true,  "query": {    "match_all": {}  },  "aggs": {    "distance": {      "geo_distance": {        "field": "location",        "unit": "m",         "origin": {          "lat": 34.381716,          "lon": 108.945281        },        "ranges": [          {            "to": 100000          },          {            "from": 100000,            "to": 300000          },          {            "from": 300000          }        ]      }    }  }}

注意:Note that this aggregation includes the from value and excludes the to value for each range.

后记:很多数据库都有对GEO地理位置数据的处理,除了文中说到的MySQL、Elasticsearch;还有Redis、MongoDB等。

posted @ 2022-01-28 16:06 下午喝什么茶 阅读(72) 评论(0) 编辑 收藏 举报
回帖
    张三

    张三 (王者 段位)

    821 积分 (2)粉丝 (41)源码

     

    温馨提示

    亦奇源码

    最新会员