geohash 算法原理及实现方式
转自:http://www.cnblogs.com/dengxinglin/archive/2012/12/14/2817761.html
geohash 算法原理及实现方式
3、geohash 的 php 、python、java、C# 实现代码
4、观点讨论
geohash 有以下几个特点:
首先,geohash 用一个字符串表示经度和纬度两个坐标。某些情况下无法在两列上同时应用索引 (例如 MySQL 4 之前的版本,Google App Engine 的数据层等),利用geohash,只需在一列上应用索引即可。
其次,geohash 表示的并不是一个点,而是一个矩形区域。比如编码 wx4g0ec19,它表示的是一个矩形区域。 使用者可以发布地址编码,既能表明自己位于北海公园附近,又不至于暴露自己的精确坐标,有助于隐私保护。
第三,编码的前缀可以表示更大的区域。例如wx4g0ec1,它的前缀wx4g0e表示包含编码wx4g0ec1在内的更大范围。 这个特性可以用于附近地点搜索。首先根据用户当前坐标计算geohash(例如wx4g0ec1)然后取其前缀进行查询 (SELECT * FROM place WHERE geohash LIKE 'wx4g0e%'),即可查询附近的所有地点。
Geohash比直接用经纬度的高效很多。
Geohash的原理
Geohash的最简单的解释就是:将一个经纬度信息,转换成一个可以排序,可以比较的字符串编码
首先将纬度范围(-90, 90)平分成两个区间(-90,0)、(0, 90),如果目标纬度位于前一个区间,则编码为0,否则编码为1。
由于39.92324属于(0, 90),所以取编码为1。
然后再将(0, 90)分成 (0, 45), (45, 90)两个区间,而39.92324位于(0, 45),所以编码为0。
以此类推,直到精度符合要求为止,得到纬度编码为1011 1000 1100 0111 1001。
|
纬度范围 |
划分区间0 |
划分区间1 |
39.92324所属区间 |
|
(-90, 90) |
(-90, 0.0) |
(0.0, 90) |
1 |
|
(0.0, 90) |
(0.0, 45.0) |
(45.0, 90) |
0 |
|
(0.0, 45.0) |
(0.0, 22.5) |
(22.5, 45.0) |
1 |
|
(22.5, 45.0) |
(22.5, 33.75) |
(33.75, 45.0) |
1 |
|
(33.75, 45.0) |
(33.75, 39.375) |
(39.375, 45.0) |
1 |
|
(39.375, 45.0) |
(39.375, 42.1875) |
(42.1875, 45.0) |
0 |
|
(39.375, 42.1875) |
(39.375, 40.7812) |
(40.7812, 42.1875) |
0 |
|
(39.375, 40.7812) |
(39.375, 40.0781) |
(40.0781, 40.7812) |
0 |
|
(39.375, 40.0781) |
(39.375, 39.7265) |
(39.7265, 40.0781) |
1 |
|
(39.7265, 40.0781) |
(39.7265, 39.9023) |
(39.9023, 40.0781) |
1 |
|
(39.9023, 40.0781) |
(39.9023, 39.9902) |
(39.9902, 40.0781) |
0 |
|
(39.9023, 39.9902) |
(39.9023, 39.9462) |
(39.9462, 39.9902) |
0 |
|
(39.9023, 39.9462) |
(39.9023, 39.9243) |
(39.9243, 39.9462) |
0 |
|
(39.9023, 39.9243) |
(39.9023, 39.9133) |
(39.9133, 39.9243) |
1 |
|
(39.9133, 39.9243) |
(39.9133, 39.9188) |
(39.9188, 39.9243) |
1 |
|
(39.9188, 39.9243) |
(39.9188, 39.9215) |
(39.9215, 39.9243) |
1 |
经度也用同样的算法,对(-180, 180)依次细分,得到116.3906的编码为1101 0010 1100 0100 0100。
|
经度范围 |
划分区间0 |
划分区间1 |
116.3906所属区间 |
|
(-180, 180) |
(-180, 0.0) |
(0.0, 180) |
1 |
|
(0.0, 180) |
(0.0, 90.0) |
(90.0, 180) |
1 |
|
(90.0, 180) |
(90.0, 135.0) |
(135.0, 180) |
0 |
|
(90.0, 135.0) |
(90.0, 112.5) |
(112.5, 135.0) |
1 |
|
(112.5, 135.0) |
(112.5, 123.75) |
(123.75, 135.0) |
0 |
|
(112.5, 123.75) |
(112.5, 118.125) |
(118.125, 123.75) |
0 |
|
(112.5, 118.125) |
(112.5, 115.312) |
(115.312, 118.125) |
1 |
|
(115.312, 118.125) |
(115.312, 116.718) |
(116.718, 118.125) |
0 |
|
(115.312, 116.718) |
(115.312, 116.015) |
(116.015, 116.718) |
1 |
|
(116.015, 116.718) |
(116.015, 116.367) |
(116.367, 116.718) |
1 |
|
(116.367, 116.718) |
(116.367, 116.542) |
(116.542, 116.718) |
0 |
|
(116.367, 116.542) |
(116.367, 116.455) |
(116.455, 116.542) |
0 |
|
(116.367, 116.455) |
(116.367, 116.411) |
(116.411, 116.455) |
0 |
|
(116.367, 116.411) |
(116.367, 116.389) |
(116.389, 116.411) |
1 |
|
(116.389, 116.411) |
(116.389, 116.400) |
(116.400, 116.411) |
0 |
|
(116.389, 116.400) |
(116.389, 116.394) |
(116.394, 116.400) |
0 |
接下来将经度和纬度的编码合并,奇数位是纬度,偶数位是经度,得到编码 11100 11101 00100 01111 00000 01101 01011 00001。
最后,用0-9、b-z(去掉a, i, l, o)这32个字母进行base32编码,得到(39.92324, 116.3906)的编码为wx4g0ec1。
|
十进制 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
|
base32 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
b |
c |
d |
e |
f |
g |
|
十进制 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
|
base32 |
h |
j |
k |
m |
n |
p |
q |
r |
s |
t |
u |
v |
w |
x |
y |
z |
解码算法与编码算法相反,先进行base32解码,然后分离出经纬度,最后根据二进制编码对经纬度范围进行细分即可,这里不再赘述。
实现代码:
php版本的实现方式:http://blog.dixo.net/downloads/geohash-php-class/ 我下载了一个上传的
php:
geohash.class.php
python:
python版本的geohash:python-geohash
java:
java版本的geohash,实现:http://code.google.com/p/geospatialweb/source/browse/#svn/trunk/geohash/src
C#:

C#版本的geohash代

1 using System;
2
3 namespace sharonjl.utils
4 {
5 public static class Geohash
6 {
7 #region Direction enum
8
9 public enum Direction
10 {
11 Top = 0,
12 Right = 1,
13 Bottom = 2,
14 Left = 3
15 }
16
17 #endregion
18
19 private const string Base32 = "0123456789bcdefghjkmnpqrstuvwxyz";
20 private static readonly int[] Bits = new[] {16, 8, 4, 2, 1};
21
22 private static readonly string[][] Neighbors = {
23 new[]
24 {
25 "p0r21436x8zb9dcf5h7kjnmqesgutwvy", // Top
26 "bc01fg45238967deuvhjyznpkmstqrwx", // Right
27 "14365h7k9dcfesgujnmqp0r2twvyx8zb", // Bottom
28 "238967debc01fg45kmstqrwxuvhjyznp", // Left
29 }, new[]
30 {
31 "bc01fg45238967deuvhjyznpkmstqrwx", // Top
32 "p0r21436x8zb9dcf5h7kjnmqesgutwvy", // Right
33 "238967debc01fg45kmstqrwxuvhjyznp", // Bottom
34 "14365h7k9dcfesgujnmqp0r2twvyx8zb", // Left
35 }
36 };
37
38 private static readonly string[][] Borders = {
39 new[] {"prxz", "bcfguvyz", "028b", "0145hjnp"},
40 new[] {"bcfguvyz", "prxz", "0145hjnp", "028b"}
41 };
42
43 public static String CalculateAdjacent(String hash, Direction direction)
44 {
45 hash = hash.ToLower();
46
47 char lastChr = hash[hash.Length - 1];
48 int type = hash.Length%2;
49 var dir = (int) direction;
50 string nHash = hash.Substring(0, hash.Length - 1);
51
52 if (Borders[type][dir].IndexOf(lastChr) != -1)
53 {
54 nHash = CalculateAdjacent(nHash, (Direction) dir);
55 }
56 return nHash + Base32[Neighbors[type][dir].IndexOf(lastChr)];
57 }
58
59 public static void RefineInterval(ref double[] interval, int cd, int mask)
60 {
61 if ((cd & mask) != 0)
62 {
63 interval[0] = (interval[0] + interval[1])/2;
64 }
65 else
66 {
67 interval[1] = (interval[0] + interval[1])/2;
68 }
69 }
70
71 public static double[] Decode(String geohash)
72 {
73 bool even = true;
74 double[] lat = {-90.0, 90.0};
75 double[] lon = {-180.0, 180.0};
76
77 foreach (char c in geohash)
78 {
79 int cd = Base32.IndexOf(c);
80 for (int j = 0; j < 5; j++)
81 {
82 int mask = Bits[j];
83 if (even)
84 {
85 RefineInterval(ref lon, cd, mask);
86 }
87 else
88 {
89 RefineInterval(ref lat, cd, mask);
90 }
91 even = !even;
92 }
93 }
94
95 return new[] {(lat[0] + lat[1])/2, (lon[0] + lon[1])/2};
96 }
97
98 public static String Encode(double latitude, double longitude, int precision = 12)
99 {
100 bool even = true;
101 int bit = 0;
102 int ch = 0;
103 string geohash = "";
104
105 double[] lat = {-90.0, 90.0};
106 double[] lon = {-180.0, 180.0};
107
108 if (precision < 1 || precision > 20) precision = 12;
109
110 while (geohash.Length < precision)
111 {
112 double mid;
113
114 if (even)
115 {
116 mid = (lon[0] + lon[1])/2;
117 if (longitude > mid)
118 {
119 ch |= Bits[bit];
120 lon[0] = mid;
121 }
122 else
123 lon[1] = mid;
124 }
125 else
126 {
127 mid = (lat[0] + lat[1])/2;
128 if (latitude > mid)
129 {
130 ch |= Bits[bit];
131 lat[0] = mid;
132 }
133 else
134 lat[1] = mid;
135 }
136
137 even = !even;
138 if (bit < 4)
139 bit++;
140 else
141 {
142 geohash += Base32[ch];
143 bit = 0;
144 ch = 0;
145 }
146 }
147 return geohash;
148 }
149 }
150 }

C#代码来自:https://github.com/sharonjl/geohash-net

geohash演示:http://openlocation.org/geohash/geohash-js/
各种版本下载:打包下载
观点讨论
引用阿里云以为技术专家的博客上的讨论:
这一点是有些用户对geohash的误解,虽然geo确实尽可能的将位置相近的点hash到了一起,可是这并不是严格意义上的(实际上也并不可能,因为毕竟多一维坐标),
例如在方格4的左下部分的点和大方格1的右下部分的点离的很近,可是它们的geohash值一定是相差的相当远,因为头一次的分块就相差太大了,很多时候我们对geohash的值进行简单的排序比较,结果貌似真的能够找出相近的点,并且似乎还是按照距离的远近排列的,可是实际上会有一些点被漏掉了。
上述这个问题,可以通过搜索一个格子,周围八个格子的数据,统一获取后再进行过滤。这样就在编码层次解决了这个问题。
2.既然不能做到将相近的点hash值也相近,那么geohash的意义何在呢?
我觉得geohash还是相当有用的一个算法,毕竟这个算法通过无穷的细分,能确保将每一个小块的geohash值确保在一定的范围之内,这样就为灵活的周边查找和范围查找提供了可能。
常见的一些应用场景
A、如果想查询附近的点?如何操作
查出改点的gehash值,然后到数据库里面进行前缀匹配就可以了。
B、如果想查询附近点,特定范围内,例如一个点周围500米的点,如何搞?
可以查询结果,在结果中进行赛选,将geohash进行解码为经纬度,然后进行比较
*在纬度相等的情况下:
*经度每隔0.00001度,距离相差约1米;
*每隔0.0001度,距离相差约10米;
*每隔0.001度,距离相差约100米;
*每隔0.01度,距离相差约1000米;
*每隔0.1度,距离相差约10000米。
*在经度相等的情况下:
*纬度每隔0.00001度,距离相差约1.1米;
*每隔0.0001度,距离相差约11米;
*每隔0.001度,距离相差约111米;
*每隔0.01度,距离相差约1113米;
*每隔0.1度,距离相差约11132米。
Geohash,如果geohash的位数是6位数的时候,大概为附近1千米…
参考资料:
http://iamzhongyong.iteye.com/blog/1399333
geohash 算法原理及实现方式的更多相关文章
- geohash算法原理及实现方式
1.geohash特点 2.geohash原理 3.geohash的php .python.java.C#实现代码 4.观点讨论 w微博:http://weibo.com/dxl0321 geohas ...
- 【算法】(查找你附近的人) GeoHash核心原理解析及代码实现
本文地址 原文地址 分享提纲: 0. 引子 1. 感性认识GeoHash 2. GeoHash算法的步骤 3. GeoHash Base32编码长度与精度 4. GeoHash算法 5. 使用注意点( ...
- 查找附近网点geohash算法及实现 (Java版本号)
參考文档: http://blog.csdn.net/wangxiafghj/article/details/9014363geohash 算法原理及实现方式 http://blog.charlee ...
- 用简单直白的方式讲解A星寻路算法原理
很多游戏特别是rts,rpg类游戏,都需要用到寻路.寻路算法有深度优先搜索(DFS),广度优先搜索(BFS),A星算法等,而A星算法是一种具备启发性策略的算法,效率是几种算法中最高的,因此也成为游戏中 ...
- 空间索引 - GeoHash算法及其实现优化
h1,h2,h3,h4,h5,h6,p,blockquote { margin: 0; padding: 0 } body { font-family: "Helvetica Neue&qu ...
- MySQL索引背后的数据结构及算法原理【转】
本文来自:张洋的MySQL索引背后的数据结构及算法原理 摘要 本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话题.特别需要说明的是,MySQL支持诸多存储引擎,而各种存储引擎对索引的支持 ...
- OpenGL学习进程(13)第十课:基本图形的底层实现及算法原理
本节介绍OpenGL中绘制直线.圆.椭圆,多边形的算法原理. (1)绘制任意方向(任意斜率)的直线: 1)中点画线法: 中点画线法的算法原理不做介绍,但这里用到最基本的画0<=k ...
- 分布式缓存技术memcached学习(四)—— 一致性hash算法原理
分布式一致性hash算法简介 当你看到“分布式一致性hash算法”这个词时,第一时间可能会问,什么是分布式,什么是一致性,hash又是什么.在分析分布式一致性hash算法原理之前,我们先来了解一下这几 ...
- GBDT算法原理深入解析
GBDT算法原理深入解析 标签: 机器学习 集成学习 GBM GBDT XGBoost 梯度提升(Gradient boosting)是一种用于回归.分类和排序任务的机器学习技术,属于Boosting ...
随机推荐
- MongoDB 索引的使用, 管理 和优化
MongoDB 索引的使用, 管理 和优化 2014-03-25 17:12 6479人阅读 评论(0) 收藏 举报 分类: MongoDB(9) [使用explain和hint] 前面讲高级查询 ...
- Setting up a EDK II build environment on Windows and Linux:搭建Windows和Linux开发环境[2.2]
Setting up a EDK II build environment on Windows and Linux:搭建Windows和Linux开发环境[2.2] 2015-07 北京海淀区 ...
- 网页设计——Dreamweaver
在看ASP.NET视频时,讲到了一款编写HTML代码的软件--Dreamweaver.它是一款专门进行网页设置的软件.通过它能够设计出多彩的界面,相对于vs中自带的设计方式来说,他不须要知道太多的技术 ...
- xml配置文件中的转义字符
https://stackoverflow.com/questions/14607920/the-character-breaks-passwords-that-are-stored-in-the-w ...
- c# xml操作总结
一前言 先来了解下操作XML所涉及到的几个类及之间的关系 如果大家发现少写了一些常用的方法,麻烦在评论中指出,我一定会补上的!谢谢大家 * 1 XMLElement 主要是针对节点的一些属性进行操作 ...
- POJ3087 Shuffle'm Up
题目: 现有字符串s1.s2.s12,其中s1.s2的长度为len,s12的长度为2*len. 是否可以通过一些操作使s1和s2转换合并成s12? 变换的操作规则如下: 假设s1=11111,s2=0 ...
- BZOJ 1531 二进制优化多重背包
思路: 讲道理我应该写单调队列优化多重背包的 但是我不会啊 但是我现在! 还不会啊 我就写了个二进制优化的.. 过了 //By SiriusRen #include <cstdio> #i ...
- POJ 1442 Treap模板
// by SiriusRen #include <cstdio> #include <cstring> #include <algorithm> using na ...
- hihoCoder挑战赛32
Rikka with Sequence V 构造 #pragma comment(linker, "/STACK:102400000,102400000") #include< ...
- P1732 活蹦乱跳的香穗子
题目描述 香穗子在田野上调蘑菇!她跳啊跳,发现自己很无聊,于是她想了一个有趣的事情,每个格子最多只能经过1次,且每个格子都有其价值 跳的规则是这样的,香穗子可以向上下左右四个方向跳到相邻的格子,并且她 ...