如何实现搜索附近的店铺
最近项目中遇到一个功能需求,让用户可以在地图上画出一个圆,然后我们将圆中已经录入的地点显示出来。这个可以理解为大众点评或者美团的店铺搜索,用户当前所在的位置就是那个圆心。考虑到这是一个非常成熟的功能点,我就去网上找了一些实现方法,在这里算是个梳理总结,理清一下思路。
本文部分内容引用转载了一些学习过程中看到的其他博主的博客内容,因为用图和描述都很容易理解,而我想不到更好的表达了,借用一下非常感谢.......如果侵权请与我联系,我会删除引用的部分。
参考引用的博客链接:
https://www.cnblogs.com/feiquan/p/11380461.html
https://blog.csdn.net/u011497262/article/details/81807622
https://blog.csdn.net/kangle0228/article/details/80876795
https://blog.csdn.net/gaojingyuan/article/details/79004990
Geohash算法:
一.Geohash算法原理:
Geohash是一种用于将任意精度的纬度和经度坐标编码为文本字符串,从而使二维数据变成一维数据。Geohash其实就是将整个地图或者某个分割所得的区域进行一次划分,由于采用的是base32编码方式,即Geohash中的每一个字母或者数字(如wx4g0e中的w)都是由5bits组成(2^5 = 32,base32),这5bits可以有32中不同的组合(0~31),这样我们可以将整个地图区域分为32个区域,通过00000 ~ 11111来标识这32个区域。第一次对地图划分后的情况如下图所示(每个区域中的编号对应于该区域所对应的编码):
Geohash的0、1串序列是经度0、1序列和纬度0、1序列中的数字交替进行排列的,偶数位对应的序列为经度序列,奇数位对应的序列为纬度序列,在进行第一次划分时,Geohash0、1序列中的前5个bits(11100),那么这5bits中有3bits是表示经度,2bits表示纬度,所以第一次划分时,是将经度划分成8个区段(2^3 = 8),将纬度划分为4个区段(2^2 = 4),这样就形成了32个区域。如下图:
同理,可以按照第一次划分所采用的方式对第一次划分所得的32个区域各自再次划分。
那么我们为什么要把经纬度进行混合编码呢?
如图所示,我们将二进制编码的结果填写到空间中,当将空间划分为四块时候,编码的顺序分别是左下角00,左上角01,右下脚10,右上角11,也就 是类似于Z的曲线,当我们递归的将各个块分解成更小的子块时,编码的顺序是自相似的(分形),每一个子快也形成Z曲线,这种类型的曲线被称为Peano空 间填充曲线。
这种类型的空间填充曲线的优点是将二维空间转换成一维曲线(事实上是分形维),对大部分而言,编码相似的距离也相近, 但Peano空间填充曲线最大的缺点就是突变性,有些编码相邻但距离却相差很远,比如0111与1000,编码是相邻的,但距离相差很大。
二.举个Geohash编码过程的栗子:
下面以北海公园为例介绍GeoHash算法的计算步骤
2.1. 根据经纬度计算GeoHash二进制编码
地球纬度区间是[-90,90], 北海公园的纬度是39.928167,可以通过下面算法对纬度39.928167进行逼近编码:
1)区间[-90,90]进行二分为[-90,0),[0,90],称为左右区间,可以确定39.928167属于右区间[0,90],给标记为1;
2)接着将区间[0,90]进行二分为 [0,45),[45,90],可以确定39.928167属于左区间 [0,45),给标记为0;
3)递归上述过程39.928167总是属于某个区间[a,b]。随着每次迭代区间[a,b]总在缩小,并越来越逼近39.928167;
4)如果给定的纬度x(39.928167)属于左区间,则记录0,如果属于右区间则记录1,这样随着算法的进行会产生一个序列1011100,序列的长度跟给定的区间划分次数有关。
根据纬度算编码
bit | min | mid | max |
1 | -90.000 | 0.000 | 90.000 |
0 | 0.000 | 45.000 | 90.000 |
1 | 0.000 | 22.500 | 45.000 |
1 | 22.500 | 33.750 | 45.000 |
1 | 33.7500 | 39.375 | 45.000 |
0 | 39.375 | 42.188 | 45.000 |
0 | 39.375 | 40.7815 | 42.188 |
0 | 39.375 | 40.07825 | 40.7815 |
1 | 39.375 | 39.726625 | 40.07825 |
1 | 39.726625 | 39.9024375 |
40.07825 |
同理,地球经度区间是[-180,180],可以对经度116.389550进行编码。
根据经度算编码
bit | min | mid | max |
1 | -180 | 0.000 | 180 |
1 | 0.000 | 90 | 180 |
0 | 90 | 135 | 180 |
1 | 90 | 112.5 | 135 |
0 | 112.5 | 123.75 | 135 |
0 | 112.5 | 118.125 | 123.75 |
1 | 112.5 | 115.3125 | 118.125 |
0 | 115.3125 | 116.71875 | 118.125 |
1 | 115.3125 | 116.015625 | 116.71875 |
1 | 116.015625 | 116.3671875 | 116.71875 |
2.2. 组码
通过上述计算,纬度产生的编码为10111 00011,经度产生的编码为11010 01011。偶数位放经度,奇数位放纬度,把2串编码组合生成新串:11100 11101 00100 01111。
最后使用用0-9、b-z(去掉a, i, l, o)这32个字母进行base32编码,首先将11100 11101 00100 01111转成十进制,对应着28、29、4、15,十进制对应的编码就是wx4g。
三.Geohash编码长度与精度:
通过了解Geohash算法的原理,我们也可以想象出Geohash编码的长度和具体的精度是正相关的。编码的长度越长,所表达的精度就越高。参考图一,长度为1的编码6,把大半个南美洲都给包进去了。那么具体编码长度和精度是怎么样一个对照关系呢,我们参照下图,这个精度非常重要。
Sptial Indexing空间索引:
空间索引是指依据空间对象的位置和形状或空间对象之间的某种空间关系按一定的顺序排列的一种数据结构,其中包含空间对象的概要信息,如对象的标识、外接矩形及指向空间对象实体的指针。因为我们项目中用的数据库是MySQL,索引本文主要针对MySQL的空间索引进行阐述。
MySQL中有一个类型Point,可以用来存储每个位置对应的经纬度,在这一列上建立空间索引。对于InnoDB
和MyISAM
表,MySQL可以使用类似于创建常规索引的语法创建空间索引,但是使用 SPATIAL
关键字。必须声明空间索引中的列NOT NULL
。下面的SQL语句是创建一个空间坐标点信息的表。其中location字段就是用来存储经纬度信息的。
CREATE TABLE `points` (`name` varchar(20) NOT NULL DEFAULT '',`location` point NOT NULL,`description` varchar(200) DEFAULT NULL,PRIMARY KEY (`name`),SPATIAL KEY `sp_index` (`location`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
那么如何插入坐标数据呢?
INSERT INTO points (name, location) VALUES ( 'data1' , POINT(116.273106,39.992524));
INSERT INTO points (name, location) VALUES ( 'data2' , POINT(116.397279,39.908149));
INSERT INTO points (name, location) VALUES ( 'data3' , POINT(116.397389,39.918149));
GeomFromText()是我们在接触空间数据之前从未见过的函数。他接受任何几何类型的WKT(文本)值作为其第一个参数。其他功能提供了特定于类型的构造功能,用于构造每种几何类型的几何值。
既然现在已经插入了数据,我们应该如何检索想要的数据呢?
# 定义多边形
SET @bbox = CONCAT('POLYGON((116.373871 39.915786,116.417645 39.916444,116.41816 39.900841,116.374214 39.900182,116.373871 39.915786))');
# 使用变量
select name,X(location),Y(location),Astext(location) from points where ST_INTERSECTS( location, GeomFromText(@bbox));
在这条语句中呢,又出现了陌生的数据结构和函数。Polygon(简单面)用来表示多边形平面,在地图中可以代表一片区域。并且我们可以看到组成多边形的入参中,第一个点和最后一个点是完全重合的,这样就完成了连接。来看一下mysql官方对Polygon的断言。
重点!!!ST_INTERSECTS,返回1或0以指示g1
空间上是否 相交 g2
。这里我们需要注意一下,官方还提供了一个函数INTERSECTS,和我们例子中用的函数非常相像。大家可以试一下,在有大量数据时,使用三角形作为多边形,这两个函数所得到的结果是不一样的。使用INTERSECTS的话,和矩形范围内检索的结果完全相同,并没有减少结果数。这也是我之前在测试这些函数的时候,好奇把测试的矩形改成三角形会有什么样的结果的时候发现的。那么是为什么呢?这里要提到一个概念叫MBR,最小边界矩阵,其实很多空间函数都在命名时写明了使用最小边界矩阵来进行计算,比如MBRContains,
返回1或0以指示的最小边界矩形是否g1
包含的最小边界矩形g2。
我们来看下查询的结果。
通过结果我们也可以观察出来,X(location)和Y(location)分别代表的是Point类型的X轴值和Y轴值。我们也如愿所偿的得到了想要的点。
其他空间函数
因为对MySQL所提供的空间函数比较感兴趣,大概翻阅了一下官方文档(主要是想知道三角形怎么查询的目的),也看到了几个比较有用的函数,在这里介绍一下。
(1)ST_Distance_Sphere(
g1
, g2
[, radius
])
返回球体上两个点和/或多点之间的最小球面距离(以米为单位),或者 NULL
如果任何几何参数为 NULL
或为空,则返回。
计算使用球形地球和可配置的半径。可选radius
参数应以米为单位。如果省略,则默认半径为6,370,986米。
几何参数应由指定(经度,纬度)坐标值的点组成:
经度和纬度分别是该点的第一和第二坐标。
两个坐标均以度为单位。
经度值必须在(-180,180]范围内。正值位于本初子午线以东。
纬度值必须在[-90,90]范围内。正值位于赤道以北。
mysql> SET @pt1 = ST_GeomFromText('POINT(0 0)');
mysql> SET @pt2 = ST_GeomFromText('POINT(180 0)');
mysql> SELECT ST_Distance_Sphere(@pt1, @pt2);
+--------------------------------+
| ST_Distance_Sphere(@pt1, @pt2) |
+--------------------------------+
| 20015042.813723423 |
+--------------------------------+
(2)ST_GeoHash(
, longitude
, latitude
, max_length
)ST_GeoHash(
point
, max_length
)
好,在这里见到老朋友了,call back,我们见到了MySQL对于我们前面那部分GeoHash的实现函数。
返回连接字符集和排序规则中的geohash字符串。
如果有任何参数NULL
,则返回值为NULL
。如果任何参数无效,则会发生错误。
对于第一种语法,longitude
必须为[-180,180]范围内的数字,并且 latitude
必须为[-90,90]范围内的数字。对于第二种语法,POINT
需要一个 值,其中X和Y坐标分别在经度和纬度的有效范围内。
生成的字符串不得超过 max_length
字符(上限为100个字符)。该字符串可能短于 max_length
字符,这是因为创建geohash值的算法会继续进行,直到创建的字符串可以精确表示位置或max_length
字符, 以先到者为准。
mysql> SELECT ST_GeoHash(180,0,10), ST_GeoHash(-180,-90,15);
+----------------------+-------------------------+
| ST_GeoHash(180,0,10) | ST_GeoHash(-180,-90,15) |
+----------------------+-------------------------+
| xbpbpbpbpb | 000000000000000 |
+----------------------+-------------------------+
关于性能
Mysql的空间索引是通过R树实现的,R树用来做空间数据存储的树状数据结构。例如给地理位置,矩形和多边形这类多维数据建立索引,理解起来其实和Geohash的实现思想非常相像。感兴趣的同学可以去了解一下,在这里就不详细描述了。
我造了10W的单表测试数据,执行下面这条测试语句用时是0.205s。
SET @center = GeomFromText('POINT(0 0)');SET @radius = 10;SET @bbox = CONCAT('POLYGON((',X(@center) - @radius, ' ', Y(@center) - @radius, ',',X(@center) + @radius, ' ', Y(@center) - @radius, ',',X(@center) + @radius, ' ', Y(@center) + @radius, ',',X(@center) - @radius, ' ', Y(@center) + @radius, ',',X(@center) - @radius, ' ', Y(@center) - @radius, '))');SELECT name, AsText(location), @bbox, ST_Distance_Sphere(location, @center) as disFROM pointsWHERE ST_INTERSECTS( location, GeomFromText(@bbox) ) order by dis;
好了,到这里,我想明白如何实现该功能了,希望也能够给到大家启发。
如何实现搜索附近的店铺相关推荐
- 速卖通运营之--如何通过搜索快速提升店铺流量
写在前面:速卖通新品上新了怎么没有流量,曝光怎么这么低,最近也有很多商家在咨询小编,店铺流量低,怎么提升,核心就是产品搜索排序的方法没有掌握,今天给大家分享的是如何快速的通过搜索获取更多的流量 1.产 ...
- 怎样用API接口搜索淘宝店铺列表数据
一.接口参数说明: item_search_seller-搜索店铺列表 API接口,包含店铺名,店铺ID,店铺主页,宝贝图片,店铺类型,商品列表等,价格低至几厘,详情请加我,或者私聊我,或者是点击更多 ...
- 22-0001 淘宝店铺搜索界面
淘宝店铺搜索界面 1.元素 2.过程 2.1 搜索界面的网页源码 2.2 通过Chrome控制台获取sellerid 2.3 搜索链接 2.4 控制台 3.总结 1.元素 获取店铺搜索界面每个店铺的' ...
- 拼多多店铺搜索相关问题,为什么新品上架搜索不到
给大家有关拼多多新店搜索的相关细节问题.随着拼多多平台发展的越来越好,不少人慢慢都依赖上拼多多购物,随着入驻的商家也是越来越多,但由于是新店再加上是新手,所以容易出现店铺商品搜索不到,甚至连店铺都搜不 ...
- 淘宝新品店铺如何快速打造店铺自然搜索流量?淘宝自然搜索流量优化方法介绍
在淘宝开了新的店铺,想要将店铺生存下去并不是一件简单的事情,因为如今在如此激烈的淘宝环境下,有很多的商家因为竞争压力都退出了淘宝商城.所以商家必须要懂得如何才能将新店运营好,下面就给大家带来一些相关的 ...
- 如何教你获取1688店铺所有商品,商品详情数据分析
随着诸如物联网(IoT),人工智能(AI),增强现实/虚拟现实(AR /VR)以及区块链等技术的兴起,电商行业也因此受益良多,企业可以通过API接口服务相互连接各种技术来改善电商应用程序和网站,提高企 ...
- 怎么查看拼多多店铺销量?怎么查看店铺后台数据图?
我相信商家明白,店铺运营的许多方面都是相互关联的.店铺排名越高,销量就越好,其他数据就会得到改善,从而促进销量的增长,从而建立一个良性循环.简单来讲,经营拼多多店并没那么容易.除了大量的精力.时间和金 ...
- 京东内部资料【自然搜索排序白皮书】打算混京东的屌丝必看!
混淘宝不懂淘宝搜索规则,不懂自然搜索,只靠付费推广,你永远做不大!混京东亦然!淘宝是非常成熟的平台,已经有N家公司专门从事淘宝搜索规则研究,提供这方面的seo服务.一直以来,京东搜索规则都不明朗,因京 ...
- Python采集全国各地百度地图上店铺POI数据(母婴、美食等)
Python采集全国各地百度地图上店铺POI数据 1. 注册百度地图开放平台账号 先注册百度地图开发平台账号,创建应用,获得AK 2. 通过Python批量检索不同城市店铺POI数据 2.1 大致思路 ...
最新文章
- Datawhale组队学习周报(第021周)
- leetcode-34-在排序数组中查找元素的第一个和最后一个位置
- I2C总线学习(四)--读写过程
- 从龙门镖局看自动化测试
- android驱动代码,GitHub - rumengsuifeng/AndroidDrivers: Android驱动的代码
- 2.7-源码编译安装
- eos java是什么框架_EOS的整体框架
- Redis常用API-使用文档
- 使用 SpiritManager 类管理在 XNA 游戏中的精灵(十四)
- linux非root用户添加rzsz,Linux rz sz 安装
- ios下js复制到粘贴板_js实现复制到剪贴板功能,兼容所有浏览器
- 民科微服务小程序怎么注册_民科微服务小程序app个人端认证下载-民科微服务小程序登录官方入口下载v2.4最新版_289手游网...
- 开学至此时总结。(月末总结好像一直没写)
- 百度搜索引擎排名规则,最新排名与优化的因素有哪些?
- python的编码解码是什么意思_python - 这是什么编码,如何解码
- 针式 PKM 个人知识管理软件 视频简介
- Javascript Yielding Processes 定时器数组分块技术
- C#写入注册表打印异常提示无法写入到注册表项
- 计算机原始图片大全,怎么判定图片是否PS过?又该如何找到原始图片?
- ZBrush自带笔刷的特性你都知道哪些?