文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/

1.背景

多个项目中实现范围(圆)搜索的方案为:依赖库表中的X和Y字段构造一个矩形查询范围,再通过几何计算范围中的数据到指定坐标的距离是否在阈值半径中,最后返回阈值中的数据。
该方案有几个优点:

  • 无需对数据预处理,仅通过sql就可以实现,实现方式简单。
  • 数据库环境中,通过数字搜索比通过字符串搜索效率更高,占用的CPU更少。

但是,该方案在表数据量庞大的情况下,通过X和Y两个字段,并且有四个查询条件,对性能有一定损耗。
在之前我写过一篇关于Geohash编码研究的文章WebGIS中GeoHash编码的研究和扩展,这里提到了一种将X和Y以哈夫曼原理编码成一维字符串的方案。那么这里如果我们使用geohash编码方案来优化查询效率是否有用?

2.基于GeoHash编码的范围查询

2.1需要解决的点

  • 基于GeoHash编码原理,将编码对象从经纬度数据扩展到也支持平面坐标数据
  • 由于编码值对应的是一个范围,如果查询坐标落入在范围的角落,仅通过相同字符串匹配可能导致查询结果不全,这里需要重构查询范围
  • 根据查询的容差范围,可以计算出该范围所对应的geohash字符串位数

2.2解决思路

  • 针对平面坐标:将编码范围改变成该地图平面坐标真实范围,基于哈夫曼编码规则进行计算,最后使用base32编码成字符串。
  • 针对查询范围:以查询点为中心通过查询范围构造出查询范围矩形,利用目前查询范围所对应的hash编码长度所对应的精度,利用该精度将矩形进行切割,然后对格网分别编码。
  • geohash长度所对应的真实精度:基于编码规律,经度的bit长度可以为奇偶,但是纬度的bit长度必须是偶数,反算出经度和纬度的bit长度。然后根据经纬对范围,结合各方向的二分法次数(bit长度),即可算出经纬度此时的精度。

2.3方案实现

这里重点给出查询搜索代码,即通过hash长度对应的精度、查询范围参数,进行网格切分和编码。

/**** 通过传入指定范围、指定坐标、查询范围和geohash长度,返回查询范围中对应的所有geohash编码* @param minX* @param minY* @param maxX* @param maxY * @param X * @param Y * @param geohashLength geohash字符串编码长度 * @param searchRange 查询范围,如果是平面坐标系100M则传入100,经纬度坐标系0.0001度则传入0.0001 * @return */ public static List<String> GeoHashSearch(double minX, double minY, double maxX, double maxY, double X, double Y, int geohashLength,double searchRange){ List<Integer> latLngLength = SetHashLength(geohashLength); double boundMinX = X - searchRange; double boundMaxX = X + searchRange; double boundMinY = Y - searchRange; double boundMaxY = Y + searchRange; List<Double> range = GetGoeHashRange(minX, minY, maxX, maxY, latLngLength.get(0), latLngLength.get(1)); List<String> searchResult= new ArrayList<String>(); double xrange = range.get(0); double yrange = range.get(1); double value = 0.5; for (int i = 0; boundMinX + (i - value) * xrange <= boundMaxX; i++) { for (int j = 0; boundMinY + (j - value) * yrange <= boundMaxY; j++) { String geohashCode = Encode(minX, minY, maxX, maxY, boundMinX + i* xrange, boundMinY + j * yrange, geohashLength); if (!searchResult.contains(geohashCode)) { searchResult.add(geohashCode); } } } return searchResult; }

2.4优缺点探讨

2.4.1优点

  • geohash编码通过不断的二分,如果有必要可以直接将精度编码至厘米或毫米级别,并且对应的编码长度不会特别长。比如,当经纬度坐标系下,即使坐标范围用全球范围(-90到90,-180到180),其厘米级的编码长度也不长。以下是此时的长度精确表: 

2.4.2缺点

  • 高精度编码没法使用:虽然精度到厘米编码长度也不长,但是当查询范围是1Km例如,此时编码长度只需要到2位,而查询却必须使用like去匹配,此时查询效率反而太低。
  • 不同编码长度间跨越的精度太大:比如,查询1000M和查询2000M范围所对应的编码长度可能都是2,这样导致查询的结果的个数(格网切分)可能特别多。那么此时即使对编码字段做了索引,也不一定会产生实际效果(如果使用In则索引无效,而使用OR,查询条件又过多影响sql解析等)。
  • 编码为字符串影响查询效率:geohash编码的结果是基于Base32规范进行结果编码,为字符串,影响数据库查询效率。

2.5 换一种思路

geohash编码由于随着地图范围不同各编码长度精度无法确定、编码只能以字符串存储等问题,在我们的业务场景上无法使用。那么,如果我们让编码精度确定、编码可以用数字替代,是否就可以达到业务场景的需要呢?

3.基于格网编码的范围查询

3.1算法介绍

格网划分算是GIS算法中的万金油。以前博客中写过的空间索引、地理插值、影像金字塔、矢量切片等等均可以基于格网的思路去探索。这里,同样可以利用格网算法来进行编码。

3.1.1基本算法

  • 将地图的左上角坐标当做原点,设定好格网的长度(X方向和Y方向)
  • 传入坐标,计算坐标分别在X方向和Y方向离坐标原点的格网个数,分别为xNum、yNum
/**** 通过传入地图起始点,待编码坐标,编码的X和Y方向精确度,获取网格编码字符串* @param minX 地图起始点X坐标* @param minY 地图起始点Y坐标* @param X* @param Y * @param gridXSize X方向精确度。平面坐标为M,经纬度坐标为度 * @param gridYSize Y方向精确度。平面坐标为M,经纬度坐标为度 * @return */ public static long GetGridCode(double minX, double minY, double X, double Y, double gridXSize,double gridYSize){ if (X < minX || Y < minY){ return -1; } int xNum = (int)Math.ceil(Math.abs(X - minX) / gridXSize); int yNum = (int)Math.ceil(Math.abs(Y - minY) / gridYSize); return CreateLongCode(xNum,yNum); }

3.1.2编码优化

如果我们需要将编码转换成数字编码,那么我们同样需要设定一种规则。这里,我规定xNum和yNum都必须是八个字符串长度,不足的在前缀以0补充,最后再合并转换成整数。(注意,这里我设计以0作为前缀而不是后缀补充,是为了及时转换成数字后,以后可以通过数字将编码反转换为空间范围)

/**** 以8位数和8位数分别将col和row填充组合成一个整数*/private static long  CreateLongCode(int x,int y){ String sx=String.valueOf(y); String sy=String.valueOf(y); for(int i=sx.length();i<XLen;i++){ sx="0"+sx; } for(int j=sy.length();j<YLen;j++){ sy="0"+sy; } String scode=sx+sy; long code=Long.parseLong(scode); return code; } /*** * 获取网格编码所对应的真实地理范围 * @param minX * @param minY * @param value 编码值 * @param gridXSize X方向精确度。平面坐标为M,经纬度坐标为度 * @param gridYSize Y方向精确度。平面坐标为M,经纬度坐标为度 * @return */ public static List<Double> Decode(double minX, double minY, long value, double gridXSize,double gridYSize){ String svalue=String.valueOf(value); String sx=svalue.substring(0,svalue.length()-YLen-1); String sy=svalue.substring(svalue.length()-YLen); int xnum=Integer.parseInt(sx); int ynum=Integer.parseInt(sy); double boundMinX = minX + (xnum - 1) * gridXSize; double boundMaxX = boundMinX + gridXSize; double boundMinY = minY + (ynum - 1) * gridYSize; double boundMaxY = boundMinY + gridYSize; List<Double> bound = new ArrayList<Double>(); bound.add(boundMinX); bound.add(boundMinY); bound.add(boundMaxX); bound.add(boundMaxY); return bound; }

3.2范围查询

同样,这里也需要考虑与geohash查询时一样的情况:

  • 查询XY落在网格的边角上
  • 查询范围阈值大于网格大小 解决思路与之前相同:
/**** 通过传入地图起始点、网格X和Y方向精确度、查询范围和查询点,返回对应查询范围内所有网格编码* @param minX* @param minY* @param X* @param Y * @param gridXSize X方向精确度。平面坐标为M,经纬度坐标为度 * @param gridYSize Y方向精确度。平面坐标为M,经纬度坐标为度 * @param range 查询范围,平面坐标为M,经纬度坐标为度 * @return */ public static List<Long> GridCodeSearch(double minX, double minY, double X, double Y, double gridXSize, double gridYSize,double range){ if (X < minX || Y < minY){ return null; } double boundMinX = X - range; double boundMinY = Y - range; double boundMaxX = X + range; double boundMaxY = Y + range; double value=0.5; List<Long> searchResult = new ArrayList<Long>(); for (int i = 0; boundMinX + (i - value) * gridXSize <= boundMaxX; i++){ for (int j = 0; boundMinY + (j - value) * gridYSize <= boundMaxY; j++){ long gridCode = GetGridCode(minX, minY, boundMinX + i * gridXSize, boundMinY + j * gridYSize, gridXSize, gridYSize); if (!searchResult.contains(gridCode)){ searchResult.add(gridCode); } } } return searchResult; }

3.3格网划分的一点建议

  • 格网不宜划分太小,建议划分的比查询范围大,这样保证范围过滤查询时返回的匹配格网编码少。比如,格网大小500M,查询范围100M,查询时,在多数情况下将只返回一个编码。当然,此时基于该编码去数据库中查询,将得到更多的数据点,于是需要我们做精确的范围计算量变大。但是:将数据库压力适当转移到服务器计算是一种更划算的策略。当然,格网划的太大,也会适得其反,建议通用查询范围一两倍即可。

4.后续方案描述

  • 坐标存入时,将坐标基于格网编码并同步存入到指定字段,对该字段建立索引(此时字段为长度大于16的长整型)。
  • 查询时,调用编码查询接口,获取到该XY以及查询范围下,对应的网格编码。在数据库中利用这些编码做匹配查询(粗过滤)。对返回的结果进一步做精确范围匹配(精过滤可做可不做,视需求规格而定)。

                      

                          -----欢迎转载,但保留版权,请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/

      如果您觉得本文确实帮助了您,可以微信扫一扫,进行小额的打赏和鼓励,谢谢 ^_^

                                                          

转载于:https://www.cnblogs.com/naaoveGIS/p/8892464.html

空间搜索(圆范围)中Geohash编码方案和格网编码方案对比探讨相关推荐

  1. arcgis fishnet 单位_ArcGIS中使用Create Fishnet生成格网

    ArcGIS 中使用 Create Fishnet 生成格网 1. 如果单元格的宽和高定义为 0 ,那么必须指定行与列的数目以及格网对角的坐标 2. 格网的范围可以手动输入,也可以引用已有数据为模板. ...

  2. Matlab中解决pcolor和imagesc格网显示问题

    我们需要绘制一个这样的绘图效果:tick向外,加上格网虚线. tick向外很容易实现,代码如下: set(gca,'TickDir','out') 在格网上加上虚线.如果我们使用pcolor绘制,则是 ...

  3. 算法进阶系列1 空间搜索 GeoHash 算法

    1. 背景 我们经常会用到 App 打车和共享单车,App 界面上会显示出自己附近一个范围内可用的出租车或者共享单车: 那如何发现以自己为圆心一定范围内的车呢?最直观的想法就是在数据库里存储每一辆车的 ...

  4. 基于Solr的空间搜索学习笔记

    基于Solr的空间搜索学习笔记 在Solr中基于空间地址查询主要围绕2个概念实现: (1) Cartesian Tiers 笛卡尔层 Cartesian Tiers是通过将一个平面地图的根据设定的层次 ...

  5. Solr空间搜索原理分析与实践

    前言 在美团CRM系统中,搜索商家的效率与公司的销售额息息相关,为了让BD们更便捷又直观地去搜索商家,美团CRM技术团队基于Solr提供了空间搜索功能,其中移动端周边商家搜索和PC端的地图模式搜索功能 ...

  6. 基于Solr空间搜索

    概括: 最近一个项目需要基于LBS查询附近的商铺信息,看了一下网上都是基于Solr和ELS方式来实现, 本来想使用ELS来实现的,但是由于项目以前用的是Solr, 所以就去调研了一下基于Solr来实现 ...

  7. 【Solr空间搜索SpatialSearch】

    喂,这几个单词什么意思,你晓得伐 名词 含义 longitude 经度 latitude 纬度 LatLon 纬度经度的缩写 Grid 网格 空间搜索 空间搜索,又名Spatial Search(Sp ...

  8. [ElasticSearch] 空间搜索 (一)

    根据索引文档的地理坐标来进行搜索,Elasticsearch 也能够处理这样的搜索.--空间搜索 一.为空间搜索准备映射 PUT my_space_test {"mappings" ...

  9. 搜索和推荐系统中的深度匹配模型

    文章作者:辛俊波 腾讯 高级研究员 编辑整理:DataFunTalk 导读:推荐系统和搜索应该是机器学习乃至深度学习在工业界落地应用最多也最容易变现的场景.而无论是搜索还是推荐,本质其实都是匹配,搜索 ...

  10. 搜索引擎语法+网络空间搜索and在线web工具

    你用过哪些搜索引擎呢? 百度 bing Google ? 一 :利用百度的高级搜索功能帮助你找到关键信息 二 :Google Hack google hack是指使用Google等搜索引擎对某些特定的 ...

最新文章

  1. 实验七 访问列表配置
  2. centos7 安装mysql php_Centos7安装mysql与php的方法
  3. 【论文笔记】APPLYING DEEP LEARNING TO ANSWER SELECTION: A STUDY AND AN OPEN TASK
  4. 阿里云智能物联网解决方案宣讲会,帮助天津东丽临空经济区数字腾飞
  5. docker安装到指定目录_Docker常用命令总结
  6. ad如何镜像器件_使用 Dockerfile 制作镜像
  7. centos-7部署docker
  8. uva 11396Claw Decomposotion(二分图判定)
  9. 把别人的Tcl/Tk代码加入到Go语言里2 矩形
  10. centos6.5安装sublime text 2
  11. PDF背景怎么添加,如何修改PDF背景
  12. 解题:CEOI 2017 Mousetrap
  13. 开发撞墙之奇怪的需求:度分秒格式动态转换
  14. python股票分析入门_学习用Python分析股票数据(入门)
  15. Non-local Neural Networks论文理解
  16. Windows Azure案例:迈阿密市政府使用“云”平台改善服务方案,降低运营成本
  17. IT十年人生过客-十六-再见雍和宫
  18. 阿里云ACA认证到底是什么?
  19. 同济大学符长虹教授作“基于计算机视觉的无人飞行器自主导航研究与应用”为题的报告
  20. 我决定辞掉工作,全职开发我的操作系统!(续)

热门文章

  1. 干货:中国移动互联网行业深度报告
  2. 用户名xxx不在sudoers文件中,此事将被报告
  3. springboot easypoi excel导出功能
  4. eve手游服务器维护,EVE手游国际服新手教程,云手机小号多开辅助快速获得资源...
  5. error obtaining controller instance: failed to create NAT chain
  6. uiautomatorviewer 提示 Error obtaining UI hierarchy 的解决办法
  7. 【Node.js 微信公众号实战】4.Node.js 微信消息管理
  8. 如何抢功,甩锅,立于不败之地???
  9. python实现去除图片水印
  10. OSChina 周五乱弹 —— 埃塞俄比亚的远房大表姐