前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何在附近商户中查找离你最近的商家?

如何在附近商户中查找离你最近的商家?

原创
作者头像
天下之猴
发布2024-09-16 14:27:18
920
发布2024-09-16 14:27:18
举报

前提背景

  1. 用户位置按照经纬度获取
  2. 用户可选范围内的商家
  3. 查询后的结果按顺序返回给用户
  4. 商户位置以经纬度存储

常用方法

数据库查询筛选

根据用户当前位置和用户所选择范围, 在数据库中查询后将结果在数据库中排序或者在内存中排序, 返回给用户

代码语言:txt
复制
--longitude 表中经度字段
--latitude 表中维度字段
--lat1 指定点维度
--lon1 指定点经度
-- radius_in_km为用户所选择的范围
select business_id ,   6371 * ACOS(
        COS(RADIANS(lat1)) * COS(RADIANS(latitude)) * COS(RADIANS(longitude) - RADIANS(lon1)) +
        SIN(RADIANS(lat1)) * SIN(RADIANS(latitude)
    ) AS distance 
    from business
    having
    6371 * ACOS(
        COS(RADIANS(lat1)) * COS(RADIANS(latitude)) * COS(RADIANS(longitude) - RADIANS(lon1)) +
        SIN(RADIANS(lat1)) * SIN(RADIANS(latitude)
    ) <= radius_in_km;
    order by distance asc
    limit 0, 20

上述中我们可以给longitude 与latitude 建立联合索引, 方便我们做查询, 另外mysql中还有point类型, 用来表示点的位置, 我们可以利用ST_Distance_Sphere函数来计算店铺点位与用户点位之间的距离, 在做筛选也可

关于数据库查询更优秀的写法大家可以看看这篇文章

附近商家算法-地理空间距离计算优化 - 金泽夕 - 博客园 (cnblogs.com)

利用redis中的geo类型来做范围筛选

可以将用户最大能选范围内的所有商户的经纬度预先存redis中, 之后只需要用户将精度度传递给服务器, 服务器直接在redis中计算之后就可以将商户信息统计返回给用户

代码语言:txt
复制
GEORADIUS geo:merchants $user_latitude $user_longitude 5 km WITHDIST WITHCOORD

这里userlatitude和user_longitude是用户的当前位置坐标。此命令将返回所有在5公里范围内的商家及其距离和坐标。我们还可以使用GEOFILTER命令对结果进行更复杂的排序和过滤,例如只返回特定类型的商家,或者按照距离排序。

四叉树解决

这里贴一篇某管上关于四叉树的连接,个人认为通俗易懂,https://www.youtube.com/watch?v=gGgyc9O7dqc , 只在这里做简单简述, 一个数四个节点, 每个节点有个容量为n, 节点存储该范围内的数据, 对应我们的场景就是存储商户信息, 每个节点表示大块区域, 节点的子节点表示他父节点中区域的一部分, 方便更细的划分, 比如中国就是根节点, 湖南,湖北, 北京,上海,,,,都是子节点, 长沙, 常德, ,,,,都是湖南的子节点, 然后每个县又是每个市的子节点, 知道划分成为最小区域位置, 比如我的筛选最小区域是1km * 1km,那么我就将中国分为n个1km*1km的小块存在数中, 四叉树的是将中国分为四块, 每块再划分四块, 知道划分为最小块, 之后我们新增商户或者查询的时候都可以在树中查询

选取上面视频连接的部分片段方便各位理解

查询的时候,我们根据用户位置以及用户筛选的位置, 对四叉树的节点进行遍历, 判断是否相交, 如果相交. 再对相交节点的子节点进行遍历, 与哪些子节点相交. 一直遍历到叶子节点, 之后将叶子节点所有的数据返回即可

另外, 我们可以以县作为根节点, 这样深度更小, 查询更快

业界通用解决方案:Geo Hash

关于geohash网上有更为详细的文章,这里制作简单概述,地图的经纬度范围分别为[-180,180],[90, -90],这里我们以经度为例,将经度分为[-180,0],[0,180],有一个点经纬度为[-121,34],用1表示在[-180,0]区间,0表示[0,180]区间,那么这个点表示为1,之后可以看到这个划分太粗糙了,我们继续对该点位于的区间对半分,分别为,[-180,-90],[-90,0],同样1表示在左边区间,0表示在右边区间,这里第二位仍然为1,继续划分左区间,划分结果为[-180,-135],[-135,-90],1表示在左区间,0表示右区间,这次第三位为0,因此通过110,我们划分三次,就可以知道该经度的范围了 ,同理纬度也按照同样方式获取,之后将维度放奇数为,径度放偶数位,进行base32编码,5位一编码,就能获取一串字符串,这串字符串就是该点所处的区域,想要更精准我们只需要划分更多次,就行了,经纬度各划分25次后,geohash位数经过编码后为10,这时候误差为0.6米,几乎不影响使用,如果需要更高精度,可以继续划分

另外geohash检索时常见的边缘问题,因为geohash是按矩形块检索的,如果一个矩形块内有a,b两点,b与a的距离为10km,相邻矩形块有c点,c与a的距离为5km,由于a与b前缀编码相同位数更多,将会认为a与b的距离更近,因此为了避免边缘问题,我们在检索时,还要将相邻矩形块也一起遍历,,也就是看似在第三层矩形中找距离最近的点实际上由于边缘问题,我们应该在第二层找最近节点

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前提背景
  • 常用方法
    • 数据库查询筛选
      • 利用redis中的geo类型来做范围筛选
        • 四叉树解决
          • 业界通用解决方案:Geo Hash
          相关产品与服务
          对象存储
          对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档