Geohash 基本知识及 .NET 下计算相邻8个区域编码
目录
最近项目中需要搜索周边的 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个区域编码的更多相关文章
- 从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 十八║Vue基础: 指令(下)+计算属性+watch
回顾 今天来晚辣,给公司做了一个小项目,一个瀑布流+动态视频控制的DEMO,有需要的可以联系我,公司的项目就不对外展示了(一个后端程序员真的要干前端了哈哈哈). 书接上文,昨天正式的开始了Vue的代码 ...
- PS图像菜单下计算命令
PS图像菜单下计算命令通过通道的混合模式得到的选区非常精细,从而调色的时候过度非常好.功能十分强大. 下面用计算命令中的"相加"和"减去"模式做实例解析,这 ...
- Windows下计算md5值
目录 Windows下计算md5值 1.linux 下计算md5值 2.Windows下计算md5值 Windows下计算md5值 1.linux 下计算md5值 [root@master yl]# ...
- php计算字符串长度:utf8编码,包含中文
php计算字符串长度:utf8编码 中文当作1个字符处理(strlen默认当作两个字符) 上函数: /** * 计算 UTF-8 字符串长度 * * @param string $str * @ret ...
- CORDIC算法(1):圆周旋转模式下计算三角函数和模值
CORDIC(Coordinate Rotation Digital Computer)坐标旋转数字计算机,是数学与计算机技术交叉产生的一种机器算法,用于解决计算机的数学计算问题.发展到现在,CORD ...
- MySQL计算相邻两行某列差值的方法
简述 博主最近因工作任务缠身,都无暇顾及到我的这片自留地了.前段时间稍有空闲,花了较多的精力学习<啊哈算法>,从中学习到很多之前没有太注重的内容,收益颇丰.但是这些算法题目还没有看完,等后 ...
- Caffe学习系列(12):不同格式下计算图片的均值和caffe.proto
均值是所有训练样本的均值,减去之后再进行训练会提高其速度和精度. 1.caffe下的均值 数据格式是二进制的binaryproto,作者提供了计算均值的文件compute_image_mean, 计算 ...
- windows下计算文件的md和sha值
在windows下可以使用FCIV命令行工具计算文件的md5和sha值,具体例子如下: FCIV -md5 -sha1 path\filename.ext 例如: FCIV-md5-sha1 c:\w ...
- Nginx入门篇-基础知识与linux下安装操作
我们要深刻理解学习NG的原理与安装方法,要切合实际结合业务需求,应用场景进行灵活使用. 一.Nginx知识简述Nginx是一个高性能的HTTP服务器和反向代理服务器,也是一个 IMAP/POP3/SM ...
随机推荐
- Swoole练习 Web
WEB 服务端代码 $http = new swoole_http_server("0.0.0.0", 9501); $http->on('request', functio ...
- 一个unsigned int 数的二进制表示中有多少个1
这是一道面试题可以用以下的一些方案.第一种是很容易想到的采用循环的方式并且与1进行位与运算,具体代码如下. 1unsigned int GetBitNumOfOne_ByLoop1(unsigned ...
- [转帖]AMD:Zen 2霄龙处理器每美元性能可达英特尔至强5.6倍
AMD:Zen 2霄龙处理器每美元性能可达英特尔至强5.6倍 2019-10-20 6:35:38来源:IT之家作者:孤城责编:孤城评论:32 https://www.ithome.com/0/451 ...
- flask返回数据类型
服务器这种后台返回的数据只能是string,json或者是文件类型,对应Html的解析文件类型 无法返回自定义的元组,只能返回规定好的元组,说白了只第一个元素有效 所有返回前台的内容其实都应该是Res ...
- Lombok - 使用注解让你的JavaBean变得更加简洁
Lombok - 工具简介: Lombok是一个编译时注释预处理器,有助于在编译时注入一些代码.Lombok提供了一组在开发时处理的注释,以将代码注入到Java应用程序中,注入的代码在开发环境中立即可 ...
- Python32之类和对象2(self参数及魔法方法)
一.类方法中的self参数含义 在Python中类的方法都要有self参数,其实质为对类的实例化对象的绑定从而使得在类的实例化对象调用方法时能够确认出是对哪个对象进行操作. 带self的的参数是人家实 ...
- Python 实例代码二
1.实现isNum()函数,参数为一个字符串,如果这个字符串属于整数.浮点数或复数的表示,则返回True,否则返回False def isNum(word): try: word=type(eval( ...
- 20191125:Python中的上下文管理机制with
20191125:with上下文管理 with是一个上下文管理器,用于执行代码块所需要的运行的时候的上下文入口和出口.上下文管理器的典型用法包括保存和还原各种全局状态,锁定和解锁资源,关闭打开的文件等 ...
- git创建本地分支,推送到远程
创建本地分支git branch 分支名 例如:git branch dev,这条命令是基于当前分支创建的本地分支,假设当前分支是master(远程分支),则是基于master分支创建的本地分支dev ...
- AVOSCloud入门教程:Android Parse云服务的Hello World
本文时间戳:2013年8月30日 AVOSCloud(万象云)才刚刚推出来不久的咯,其背后创业的推动者据说是Youtube的华人老大陈士骏(SteveChen,貌似手头有很多创业,美味,玩拍,都是,开 ...