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 ...
随机推荐
- web端调起Windows系统应用程序(exe执行文件),全面兼容所有浏览器
1. 首先,你要有一个exe可执行文件2. 创建注册表创建注册表有两种方式(以“MyApp.exe”为例): 方式一:可视化编辑Win+R 打开运行,输入 regedit 并回车,进入注册表编辑器新建 ...
- 【GStreamer开发】GStreamer基础教程02——GStreamer概念
上一个教程演示了如何自动生成一个pipeline.这次我们打算用一个个element来手动搭建一个pipeline.我们这个教程会演示: 1. 什么是GStreamer的element以及如何建立一个 ...
- PHP正则匹配价格
/** * 匹配价格 * @param $price * @return bool */ public static function checkPrice($price) { // 不能小于0 if ...
- LeetCode 70. 爬楼梯(Climbing Stairs)
70. 爬楼梯 70. Climbing Stairs 题目描述 假设你正在爬楼梯.需要 n 阶你才能到达楼顶. 每次你可以爬 1 或 2 个台阶.你有多少种不同的方法可以爬到楼顶呢? 注意: 给定 ...
- 通过tushare获取股票价格
# Author llll # coding=utf-8 # ---描述# 完成股票 价格查询和展示# 不直接根据网页进行爬虫获取股票价格,而是通过已有组件查询股票价格,并保存到csv文件或者exce ...
- 从零开始学Flask框架-006
重定向和用户会话 因为刷新页面时浏览器会重新发送之前已经发送过的最后一个请求.如果这个请求是一个包含表单数据的POST 请求,刷新页面后会再次提交表单. 基于这个原因,最好别让Web 程序把POST ...
- WUSTOJ 1302: 区间k大数查询(Java)
题目链接:
- 在论坛中出现的比较难的sql问题:26(动态行专列+合并字符串、补足行数)
原文:在论坛中出现的比较难的sql问题:26(动态行专列+合并字符串.补足行数) 最近,在论坛中,遇到了不少比较难的sql问题,虽然自己都能解决,但发现过几天后,就记不起来了,也忘记解决的方法了. 所 ...
- ReLU函数的缺陷
ReLU激活功能并不完美. 它有一个被称为 “ReLU 死区” 的问题:在训练过程中,一些神经元会“死亡”,即它们停止输出 0 以外的任何东西.在某些情况下,你可能会发现你网络的一半神经元已经死亡,特 ...
- vue多页面项目搭建(vue-cli 4.0)
1.创建vue项目 cmd命令执行 vue create app (app 自定义的项目名) 一般都会选择后者,自己配置一下自己需要的选项(空格为选中) 这是我个人需要的一些选项,路由Router.状 ...