Geohash 算法:
这是一套纬度/经度地理编码算法,把纬度/经度编码成base32位的字符串。这种编码和纬度/经度不是唯一对应,其实是一个纬度/经度区间。算法有一个精度概念,精度越高,字符串越长,所表示的区间越小。可以编码后的字符串想象成一个格子,里面存放一些纬度/经度值。格子趋近很小的时候,只能存放一纬度/经度值,那么编码和纬度/经度就是唯一对应的关系。但是这个不是重点,这套算法目的就是把纬度/经度编码成近似值,通过近似值搜索,就能很高效的缩小范围,然后再从小范围里查找精确值。
例如,坐标57.64911,10.40744(日德兰半岛的顶端附近,在丹麦)产生一个u4pruydqqvj字符串。参考Wikipedia:http://en.wikipedia.org/wiki/Geohash
算法原理:
以A[-170,42.6] 为例,纬度范围(-90, 90)平分成两个区间(-90, 0)、(0, 90),位于前一个区间,则编码为0,否则编码为1。由于42.6属于(0, 90),所以取编码为1。
再将(0, 90)分成 (0, 45), (45, 90)两个区间,而42.6位于(0, 45),所以编码为0,
再将(0, 45)分成 (0, 22.5), (22.5, 45)两个区间,而42.6位于(22.5, 45),所以编码为1,
再将(22.5, 45)分成 (22.5, 33.7.5), (33.7.5, 45)两个区间
最后划分四次后纬度编码为:1011
同理经度编码为:0000
如图绿色格子就是此编码代表的区间范围

算出经纬度编码后,从高到低,奇数为经度,偶数为纬度,合并经纬度编码。
lng:0111110000000
lat:101111001001
合并后:01101 11111 11000 00100 00010
然后再把二进制按每五个一组,按base 32 编码成字符串。

01101 11111 11000 00100 00010
13 31 24 4 2
e z s 4 2
最后的Geohash 编码为:ezs42
应用场景:
前面介绍了下编码的规则,现在来讨论下一些应用场景。我们知道,地球是一个近似球体。球面上两点相对球心的角度偏差,和两点的球面距离是一个等比关系。而Geohash 编码其实就是一个纬度/经度区间,区间的角度范围就决定了区间内的点之间的距离范围。通过这个原理,就可以通过一个坐标的经纬度,找出所在的区间和周边区间来搜索 该点周边的坐标。
Wikipedia上以纬度42.6 为例,统计出每次划分后的每个区间的纬度范围。

划分十二次后,每个区间的纬度范围 0.044 ,根据地球半径可心算出每个距离范围为4.8 公里。

当geohash length=5 时,通过搜索某点周边的8个相邻区间,可以大概找出周边5公里的坐标。
这个算法有一定限制,纬度越高,基于经度偏差和距离的比值越低,表格中的距离计算精度也随着降低,需要根据cos(纬度)的值进行调整。
下面是官方提供的代码,一个是根据经纬度计算HashCode ,另一个是根据HashCode 计算周边的8个HashCode 。在实际应用中就可以用这几个方法
构建地标的hashCode 并通过hashCode来检索。
C#代码:

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

1 <script type="text/javascript">
2 BITS = [16, 8, 4, 2, 1];
3
4 BASE32 = "0123456789bcdefghjkmnpqrstuvwxyz";
5 NEIGHBORS = { right: { even: "bc01fg45238967deuvhjyznpkmstqrwx" },
6 left: { even: "238967debc01fg45kmstqrwxuvhjyznp" },
7 top: { even: "p0r21436x8zb9dcf5h7kjnmqesgutwvy" },
8 bottom: { even: "14365h7k9dcfesgujnmqp0r2twvyx8zb" }
9 };
10 BORDERS = { right: { even: "bcfguvyz" },
11 left: { even: "0145hjnp" },
12 top: { even: "prxz" },
13 bottom: { even: "028b" }
14 };
15
16 NEIGHBORS.bottom.odd = NEIGHBORS.left.even;
17 NEIGHBORS.top.odd = NEIGHBORS.right.even;
18 NEIGHBORS.left.odd = NEIGHBORS.bottom.even;
19 NEIGHBORS.right.odd = NEIGHBORS.top.even;
20
21 BORDERS.bottom.odd = BORDERS.left.even;
22 BORDERS.top.odd = BORDERS.right.even;
23 BORDERS.left.odd = BORDERS.bottom.even;
24 BORDERS.right.odd = BORDERS.top.even;
25
26 function refine_interval(interval, cd, mask) {
27 if (cd & mask)
28 interval[0] = (interval[0] + interval[1]) / 2;
29 else
30 interval[1] = (interval[0] + interval[1]) / 2;
31 }
32
33 function calculateAdjacent(srcHash, dir) {
34 srcHash = srcHash.toLowerCase();
35 var lastChr = srcHash.charAt(srcHash.length - 1);
36 var type = (srcHash.length % 2) ? 'odd' : 'even';
37 var base = srcHash.substring(0, srcHash.length - 1);
38 if (BORDERS[dir][type].indexOf(lastChr) != -1)
39 base = calculateAdjacent(base, dir);
40 return base + BASE32[NEIGHBORS[dir][type].indexOf(lastChr)];
41 }
42
43 function decodeGeoHash(geohash) {
44 var is_even = 1;
45 var lat = []; var lon = [];
46 lat[0] = -90.0; lat[1] = 90.0;
47 lon[0] = -180.0; lon[1] = 180.0;
48 lat_err = 90.0; lon_err = 180.0;
49
50 for (i = 0; i < geohash.length; i++) {
51 c = geohash[i];
52 cd = BASE32.indexOf(c);
53 for (j = 0; j < 5; j++) {
54 mask = BITS[j];
55 if (is_even) {
56 lon_err /= 2;
57 refine_interval(lon, cd, mask);
58 } else {
59 lat_err /= 2;
60 refine_interval(lat, cd, mask);
61 }
62 is_even = !is_even;
63 }
64 }
65 lat[2] = (lat[0] + lat[1]) / 2;
66 lon[2] = (lon[0] + lon[1]) / 2;
67
68 return { latitude: lat, longitude: lon };
69 }
70
71 function encodeGeoHash(latitude, longitude) {
72 var is_even = 1;
73 var i = 0;
74 var lat = []; var lon = [];
75 var bit = 0;
76 var ch = 0;
77 var precision = 12;
78 geohash = "";
79
80 lat[0] = -90.0; lat[1] = 90.0;
81 lon[0] = -180.0; lon[1] = 180.0;
82
83 while (geohash.length < precision) {
84 if (is_even) {
85 mid = (lon[0] + lon[1]) / 2;
86 if (longitude > mid) {
87 ch |= BITS[bit];
88 lon[0] = mid;
89 } else
90 lon[1] = mid;
91 } else {
92 mid = (lat[0] + lat[1]) / 2;
93 if (latitude > mid) {
94 ch |= BITS[bit];
95 lat[0] = mid;
96 } else
97 lat[1] = mid;
98 }
99
100 is_even = !is_even;
101 if (bit < 4)
102 bit++;
103 else {
104 geohash += BASE32[ch];
105 bit = 0;
106 ch = 0;
107 }
108 }
109 return geohash;
110 }
111 </script>
来源:https://www.cnblogs.com/zrhai/p/3832070.html
