目录

最近项目中需要搜索周边的 POI 信息,查找的过程中了解到了 Geohash ,这这里记录下以便自己牢记也和大家分享下。

一、简介

GeoHash是一种地址编码方法。他能够把二维的空间经纬度数据编码成一个字符串。GeoHash具有以下特点:
1、GeoHash用一个字符串表示经度和纬度两个坐标。在数据库中可以实现在一列上应用索引
2、GeoHash表示的并不是一个点,而是一个区域;
3、GeoHash编码的前缀可以表示更大的区域。例如wx4g0ec1,它的前缀wx4g0e表示包含编码wx4g0ec1在内的更大范围。 这个特性可以用于附近地点搜索

二、计算方法

GeoHash的计算过程分为三步:
1、将经纬度转换成二进制:
比如这样一个点(39.923201, 116.390705)
纬度的范围是(-90,90),其中间值为0。对于纬度39.923201,在区间(0,90)中,因此得到一个1;(0,90)区间的中间值为45度,纬度39.923201小于45,因此得到一个0,依次计算下去,即可得到纬度的二进制表示,如下表:

最后得到纬度的二进制表示为:
10111000110001111001
同理可以得到经度116.390705的二进制表示为:
11010010110001000100
2、合并纬度、经度的二进制:
合并方法是将经度、纬度二进制按照奇偶位合并:
11100 11101 00100 01111 00000 01101 01011 00001
3、按照Base32进行编码:
Base32编码表(其中一种):

将上述合并后二进制编码后结果为:
wx4g0ec1

三、GeoHash的精度

编码越长,表示的范围越小,位置也越精确。因此我们就可以通过比较GeoHash匹配的位数来判断两个点之间的大概距离。

四、查找相邻8个区域的Geohash编码(.NET)

为什么会有这样的算法,原因是Geohash是有缺点的,如下:

边缘附近的点,黄色的点要比黑色的点更加靠近红点,但是由于黑点跟红点的GeoHash前缀匹配数目更多,因此得到黑点更加靠近红点的结果(如下图)

这个问题的解决办法就是:筛选周围8个区域内的所有点,然后计算距离得到满足条件结果

下面是用C#写的在 .NET 平台下的寻找给定区域相邻的8个区域的代码

 using System;

 namespace sharonjl.utils
{
public static class Geohash
{
#region Direction enum public enum Direction
{
Top = ,
Right = ,
Bottom = ,
Left =
} #endregion private const string Base32 = "0123456789bcdefghjkmnpqrstuvwxyz";
private static readonly int[] Bits = new[] {, , , , }; private static readonly string[][] Neighbors = {
new[]
{
"p0r21436x8zb9dcf5h7kjnmqesgutwvy", // Top
"bc01fg45238967deuvhjyznpkmstqrwx", // Right
"14365h7k9dcfesgujnmqp0r2twvyx8zb", // Bottom
"238967debc01fg45kmstqrwxuvhjyznp", // Left
}, new[]
{
"bc01fg45238967deuvhjyznpkmstqrwx", // Top
"p0r21436x8zb9dcf5h7kjnmqesgutwvy", // Right
"238967debc01fg45kmstqrwxuvhjyznp", // Bottom
"14365h7k9dcfesgujnmqp0r2twvyx8zb", // Left
}
}; private static readonly string[][] Borders = {
new[] {"prxz", "bcfguvyz", "028b", "0145hjnp"},
new[] {"bcfguvyz", "prxz", "0145hjnp", "028b"}
};
/// <summary>
/// 计算相邻
/// </summary>
/// <param name="hash"></param>
/// <param name="direction"></param>
/// <returns></returns>
public static String CalculateAdjacent(String hash, Direction direction)
{
hash = hash.ToLower(); char lastChr = hash[hash.Length - ];
int type = hash.Length%;
var dir = (int) direction;
string nHash = hash.Substring(, hash.Length - ); if (Borders[type][dir].IndexOf(lastChr) != -)
{
nHash = CalculateAdjacent(nHash, (Direction) dir);
}
return nHash + Base32[Neighbors[type][dir].IndexOf(lastChr)];
}
/// <summary>
/// 细化间隔
/// </summary>
/// <param name="interval"></param>
/// <param name="cd"></param>
/// <param name="mask"></param>
public static void RefineInterval(ref double[] interval, int cd, int mask)
{
if ((cd & mask) != )
{
interval[] = (interval[] + interval[])/;
}
else
{
interval[] = (interval[] + interval[])/;
}
} /// <summary>
/// 解码
/// </summary>
/// <param name="geohash"></param>
/// <returns></returns>
public static double[] Decode(String geohash)
{
bool even = true;
double[] lat = {-90.0, 90.0};
double[] lon = {-180.0, 180.0}; foreach (char c in geohash)
{
int cd = Base32.IndexOf(c);
for (int j = ; j < ; j++)
{
int mask = Bits[j];
if (even)
{
RefineInterval(ref lon, cd, mask);
}
else
{
RefineInterval(ref lat, cd, mask);
}
even = !even;
}
} return new[] {(lat[] + lat[])/, (lon[] + lon[])/};
}
/// <summary>
/// 编码
/// </summary>
/// <param name="latitude">纬度</param>
/// <param name="longitude">经度</param>
/// <param name="precision">精度</param>
/// <returns></returns>
public static String Encode(double latitude, double longitude, int precision = )
{
bool even = true;
int bit = ;
int ch = ;
string geohash = ""; double[] lat = {-90.0, 90.0};
double[] lon = {-180.0, 180.0}; if (precision < || precision > ) precision = ; while (geohash.Length < precision)
{
double mid; if (even)
{
mid = (lon[] + lon[])/;
if (longitude > mid)
{
ch |= Bits[bit];
lon[] = mid;
}
else
lon[] = mid;
}
else
{
mid = (lat[] + lat[])/;
if (latitude > mid)
{
ch |= Bits[bit];
lat[] = mid;
}
else
lat[] = mid;
} even = !even;
if (bit < )
bit++;
else
{
geohash += Base32[ch];
bit = ;
ch = ;
}
}
return geohash;
} /// <summary>
/// 获取九个格子 顺序 本身 上、下、左、右、 左上、 右上、 左下、右下
/// </summary>
/// <param name="geohash"></param>
/// <returns></returns>
public static String[] getGeoHashExpand(String geohash)
{ try {
String geohashTop = CalculateAdjacent(geohash, Direction.Top);//上 String geohashBottom = CalculateAdjacent(geohash, Direction.Bottom);//下 String geohashLeft = CalculateAdjacent(geohash, Direction.Left);//左 String geohashRight = CalculateAdjacent(geohash, Direction.Right);//右 String geohashTopLeft = CalculateAdjacent(geohashLeft, Direction.Top);//左上 String geohashTopRight = CalculateAdjacent(geohashRight, Direction.Top);//右上 String geohashBottomLeft = CalculateAdjacent(geohashLeft, Direction.Bottom);//左下 String geohashBottomRight = CalculateAdjacent(geohashRight, Direction.Bottom);//右下 String[] expand = { geohash, geohashTop, geohashBottom, geohashLeft, geohashRight, geohashTopLeft, geohashTopRight, geohashBottomLeft, geohashBottomRight};
return expand;
} catch (Exception e) {
return null;
}
} ///// <summary>
///// test
///// </summary>
///// <param name="args"></param>
//public void main()
//{
// double lat = 39.90403;
// double lon = 116.407526; //需要查询经纬度,目前指向的是BeiJing
// string hash = Geohash.Encode(lat, lon);
// int geohashLen = 6;
// /*获取中心点的geohash*/
// String geohash = hash.Substring(0, geohashLen);
// /*获取所有的矩形geohash, 一共是九个 ,包含中心点,打印顺序请参考参数*/
// String[] result = Geohash.getGeoHashExpand(geohash);
//}
}
}

五、MySQL 中使用 GeoHash

在MySQL 5.7 以后,对GIS空间数据的更好的支持,加上虚拟列可以很方便的使用GeoHash。

选择存储 GeoHash 的列,设置为“虚拟列”,在“表达式”里填入“st_geohash(`point`,6)”,其中 point 是要编码的点字段

参考:

https://blog.csdn.net/youhongaa/article/details/78816700

https://www.cnblogs.com/lucoo/p/5085986.html

Geohash 基本知识及 .NET 下计算相邻8个区域编码的更多相关文章

  1. 从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 十八║Vue基础: 指令(下)+计算属性+watch

    回顾 今天来晚辣,给公司做了一个小项目,一个瀑布流+动态视频控制的DEMO,有需要的可以联系我,公司的项目就不对外展示了(一个后端程序员真的要干前端了哈哈哈). 书接上文,昨天正式的开始了Vue的代码 ...

  2. PS图像菜单下计算命令

    PS图像菜单下计算命令通过通道的混合模式得到的选区非常精细,从而调色的时候过度非常好.功能十分强大.   下面用计算命令中的"相加"和"减去"模式做实例解析,这 ...

  3. Windows下计算md5值

    目录 Windows下计算md5值 1.linux 下计算md5值 2.Windows下计算md5值 Windows下计算md5值 1.linux 下计算md5值 [root@master yl]# ...

  4. php计算字符串长度:utf8编码,包含中文

    php计算字符串长度:utf8编码 中文当作1个字符处理(strlen默认当作两个字符) 上函数: /** * 计算 UTF-8 字符串长度 * * @param string $str * @ret ...

  5. CORDIC算法(1):圆周旋转模式下计算三角函数和模值

    CORDIC(Coordinate Rotation Digital Computer)坐标旋转数字计算机,是数学与计算机技术交叉产生的一种机器算法,用于解决计算机的数学计算问题.发展到现在,CORD ...

  6. MySQL计算相邻两行某列差值的方法

    简述 博主最近因工作任务缠身,都无暇顾及到我的这片自留地了.前段时间稍有空闲,花了较多的精力学习<啊哈算法>,从中学习到很多之前没有太注重的内容,收益颇丰.但是这些算法题目还没有看完,等后 ...

  7. Caffe学习系列(12):不同格式下计算图片的均值和caffe.proto

    均值是所有训练样本的均值,减去之后再进行训练会提高其速度和精度. 1.caffe下的均值 数据格式是二进制的binaryproto,作者提供了计算均值的文件compute_image_mean, 计算 ...

  8. windows下计算文件的md和sha值

    在windows下可以使用FCIV命令行工具计算文件的md5和sha值,具体例子如下: FCIV -md5 -sha1 path\filename.ext 例如: FCIV-md5-sha1 c:\w ...

  9. Nginx入门篇-基础知识与linux下安装操作

    我们要深刻理解学习NG的原理与安装方法,要切合实际结合业务需求,应用场景进行灵活使用. 一.Nginx知识简述Nginx是一个高性能的HTTP服务器和反向代理服务器,也是一个 IMAP/POP3/SM ...

随机推荐

  1. 微服务Consul系列之服务部署、搭建、使用

    使用Consul解决了哪些问题 是否在为不同环境来维护不同项目配置而发愁 是否有因为配置的更改导致代码还要进行修改.发布因为客流量大了还要规避开高峰期等到半夜来发布 微服务架构下应用的分解业务系统与服 ...

  2. 【Gstreamer开发】TI嵌入式处理器GStreamer pipeline

    Example GStreamer Pipelines From Texas Instruments Embedded Processors Wiki Jump to: navigation, sea ...

  3. Can't find bundle for base name javax.servlet.LocalStrings, locale zh_CN

    启动junit4测试报错,原因是没有servlet-api.jar eclipse右键项目>>>>>>Build Path>>>>>C ...

  4. python 线程队列PriorityQueue(优先队列)(37)

    在 线程队列Queue / 线程队列LifoQueue 文章中分别介绍了先进先出队列Queue和先进后出队列LifoQueue,而今天给大家介绍的是最后一种:优先队列PriorityQueue,对队列 ...

  5. JDBC缓冲池配置druid.properties

    driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mytest?characterEncoding=UTF-8 ...

  6. python 之 数据库(数据库安装方法、基本sql语句、存储引擎)

    第十章 数据库 10.1 数据库介绍 1.数据库相关概念 数据库服务器:本质就是一个台计算机,该计算机之上安装有数据库管理软件的服务端 数据库管理系统RDBMS:本质就是一个C/S架构的套接字软件 库 ...

  7. PAT(B) 1039 到底买不买(Java)字符串

    题目链接:1039 到底买不买 (20 point(s)) 题目描述 小红想买些珠子做一串自己喜欢的珠串.卖珠子的摊主有很多串五颜六色的珠串,但是不肯把任何一串拆散了卖.于是小红要你帮忙判断一下,某串 ...

  8. C++枚举类型教案

    一.枚举类型的应用场景 只需要将需要的变量值一一列举出来,便构成一个枚举类型. 二.枚举类型的定义 ·定义方式: enum 枚举类型名字{枚举常量表}: ·关键字enum:说明接下来定义的是一个枚举类 ...

  9. 暑期ACM集训

    2019-07-17 08:42:11 这是总结昨天的做题情况 总体来说,好久的没做题了,实力下降了许多,这一个月假又学习吧!!!! A - Ropewalkers Polycarp decided ...

  10. ubuntu修改密码

    ubuntu修改密码 本文链接:https://blog.csdn.net/heybob/article/details/9095727 修改root密码: 1,$sudo su,输入密码进入root ...