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