转自:http://www.cnblogs.com/dengxinglin/archive/2012/12/14/2817761.html

geohash 算法原理及实现方式

1、geohash 特点

2、geohash 原理

3、geohash 的 php 、python、java、C# 实现代码

4、观点讨论

w微博: http://weibo.com/dxl0321

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/

各种版本下载:打包下载

观点讨论

引用阿里云以为技术专家的博客上的讨论:

1.两个离的越近,geohash的结果相同的位数越多,对么?

这一点是有些用户对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

http://tech.idv2.com/2011/06/17/location-search/

http://blog.sina.com.cn/s/blog_62ba0fdd0100tul4.html

作者:划风
邮箱:emaisi@hotmail.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.

geohash 算法原理及实现方式的更多相关文章

  1. geohash算法原理及实现方式

    1.geohash特点 2.geohash原理 3.geohash的php .python.java.C#实现代码 4.观点讨论 w微博:http://weibo.com/dxl0321 geohas ...

  2. 【算法】(查找你附近的人) GeoHash核心原理解析及代码实现

    本文地址 原文地址 分享提纲: 0. 引子 1. 感性认识GeoHash 2. GeoHash算法的步骤 3. GeoHash Base32编码长度与精度 4. GeoHash算法 5. 使用注意点( ...

  3. 查找附近网点geohash算法及实现 (Java版本号)

    參考文档: http://blog.csdn.net/wangxiafghj/article/details/9014363geohash  算法原理及实现方式 http://blog.charlee ...

  4. 用简单直白的方式讲解A星寻路算法原理

    很多游戏特别是rts,rpg类游戏,都需要用到寻路.寻路算法有深度优先搜索(DFS),广度优先搜索(BFS),A星算法等,而A星算法是一种具备启发性策略的算法,效率是几种算法中最高的,因此也成为游戏中 ...

  5. 空间索引 - GeoHash算法及其实现优化

    h1,h2,h3,h4,h5,h6,p,blockquote { margin: 0; padding: 0 } body { font-family: "Helvetica Neue&qu ...

  6. MySQL索引背后的数据结构及算法原理【转】

    本文来自:张洋的MySQL索引背后的数据结构及算法原理 摘要 本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话题.特别需要说明的是,MySQL支持诸多存储引擎,而各种存储引擎对索引的支持 ...

  7. OpenGL学习进程(13)第十课:基本图形的底层实现及算法原理

        本节介绍OpenGL中绘制直线.圆.椭圆,多边形的算法原理.     (1)绘制任意方向(任意斜率)的直线: 1)中点画线法: 中点画线法的算法原理不做介绍,但这里用到最基本的画0<=k ...

  8. 分布式缓存技术memcached学习(四)—— 一致性hash算法原理

    分布式一致性hash算法简介 当你看到“分布式一致性hash算法”这个词时,第一时间可能会问,什么是分布式,什么是一致性,hash又是什么.在分析分布式一致性hash算法原理之前,我们先来了解一下这几 ...

  9. GBDT算法原理深入解析

    GBDT算法原理深入解析 标签: 机器学习 集成学习 GBM GBDT XGBoost 梯度提升(Gradient boosting)是一种用于回归.分类和排序任务的机器学习技术,属于Boosting ...

随机推荐

  1. Web Service 附件技术的发展及演变

    Web Service 通常将业务数据封装在 SOAP 主体或者 SOAP 消息附件中进行传输,这些附件往往采用 Base64 编码二进制方式进行封装,这将大大增加待传输的数据量,消耗比较长的编码时间 ...

  2. MySQL Migration Toolkit v2.1特别版

    MySQL数据库转换工具MySQL Migration Toolkit v2.1特别版 支持mssql\oracle\access\excel与mysql互换 可以将任何数据源转换成mysql的数据, ...

  3. 转载 - Tarjan算法(求SCC)

    出处:http://blog.csdn.net/xinghongduo/article/details/6195337 说到以Tarjan命名的算法,我们经常提到的有3个,其中就包括本文所介绍的求强连 ...

  4. NYOJ 一笔画问题 欧拉路

    一笔画问题 时间限制:3000 ms  |  内存限制:65535 KB 难度:4   描述 zyc从小就比较喜欢玩一些小游戏,其中就包括画一笔画,他想请你帮他写一个程序,判断一个图是否能够用一笔画下 ...

  5. Spring Boot实例Hello World Demo

    Spring Boot要求Maven的版本达到3.2或以上. 实例: POM: <project xmlns="http://maven.apache.org/POM/4.0.0&qu ...

  6. Centos6.2中配置tomcat

    这里我使用的是tomcat6, 我使用的是server版本号的Centos, 前提是安装而且配置好了JDK. 首先通过samba把我的tomcat压缩包, 复制到了共享的文件夹.然后移动到./usr文 ...

  7. 设计模式 - 适配器模式(adapter pattern) 枚举器和迭代器 具体解释

    适配器模式(adapter pattern) 枚举器和迭代器 具体解释 本文地址: http://blog.csdn.net/caroline_wendy 參考适配器模式(adapter patter ...

  8. 有一种蓝叫 APEC 蓝

    有如是解释 APEC 者--Air Pollution Eventually Controlled. 有说此次是继零八后的重新万国来朝.丝路大略明白了,西域必通. 站在历史的远处回眸,这是继零八年后重 ...

  9. Linux命令(五)——磁盘操作及文件系统的管理

    文件系统是所有文件夹和文件的基础,磁盘是文件系统的基础,文件系统以磁盘为基础存储文件. 一.linux文件系统类型 1.ext扩展文件系统/ext2二级扩展文件系统/ext3日志式文件系统(默认) 2 ...

  10. DDos攻击,使用深度学习中 栈式自编码的算法

    转自:http://www.airghc.top/2016/11/10/Dection-DDos/ 最近研究了一篇论文,关于检测DDos攻击,使用了深度学习中 栈式自编码的算法,现在简要介绍一下内容论 ...