├── .gitignore
├── geohash.h
├── Makefile
├── tests
├── geohash_exact.py
├── benchmark.c
└── tests.c
├── etc
├── geohash_ctypes.py
├── example.c
└── geohash.py.c
├── UNLICENSE
├── README.md
└── geohash.c
/.gitignore:
--------------------------------------------------------------------------------
1 | main
2 | benchmarks
3 | example
4 | *.exe
5 | *.so
6 | *.dll
7 | __pycache__
8 |
--------------------------------------------------------------------------------
/geohash.h:
--------------------------------------------------------------------------------
1 | // This is free and unencumbered software released into the public domain.
2 | #ifndef GEOHASH_H
3 | #define GEOHASH_H
4 |
5 | // Validate and decode a geohash into a lat/lon pair. Returns 1 on success,
6 | // or 0 if the buffer does not contain a valid geohash.
7 | int geohash_decode(double *lat, double *lon, const char *buf, int len);
8 |
9 | // Store a 21-byte geohash encoding of a lat/lon pair. Does not write a
10 | // terminating null byte. Truncate as needed.
11 | void geohash_encode(char *buf, double lat, double lon);
12 |
13 | #endif
14 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | CC = cc
2 | CFLAGS = -Wall -Wextra -O3 -g
3 | LDFLAGS =
4 | LDLIBS = -lm
5 |
6 | all: check benchmark example$(EXE)
7 |
8 | check: main$(EXE)
9 | @./main$(EXE)
10 |
11 | test: check
12 |
13 | main$(EXE): tests/tests.c geohash.c geohash.h
14 | $(CC) $(CFLAGS) $(LDFLAGS) -o $@ tests/tests.c geohash.c $(LDLIBS)
15 |
16 | benchmark: benchmarks$(EXE)
17 | @./benchmarks$(EXE)
18 |
19 | benchmarks$(EXE): tests/benchmark.c geohash.c geohash.h
20 | $(CC) $(CFLAGS) $(LDFLAGS) -o $@ tests/benchmark.c geohash.c $(LDLIBS)
21 |
22 | example$(EXE): etc/example.c geohash.c geohash.h
23 | $(CC) $(CFLAGS) $(LDFLAGS) -o $@ etc/example.c geohash.c $(LDLIBS)
24 |
25 | clean:
26 | rm -f main$(EXE) benchmarks$(EXE) example$(EXE)
27 |
--------------------------------------------------------------------------------
/tests/geohash_exact.py:
--------------------------------------------------------------------------------
1 | # Used to create the tests so that they were exact.
2 | from fractions import Fraction
3 |
4 | def _decode(bits, interval):
5 | for b in bits:
6 | interval[b] = (interval[0] + interval[1]) / 2
7 | return (interval[0] + interval[1]) / 2
8 |
9 | def decode(h):
10 | """Decode a geohash to an arbitrary precision."""
11 | i = 1
12 | bins = [], []
13 | for c in h:
14 | c = "0123456789bcdefghjkmnpqrstuvwxyz".index(c)
15 | assert c >= 0
16 | bins[(i+0)%2].append(c>>4 & 1)
17 | bins[(i+1)%2].append(c>>3 & 1)
18 | bins[(i+2)%2].append(c>>2 & 1)
19 | bins[(i+3)%2].append(c>>1 & 1)
20 | bins[(i+4)%2].append(c>>0 & 1)
21 | i += 5
22 | return 1.0 * _decode(bins[0], [Fraction( 90), Fraction( -90)]), \
23 | 1.0 * _decode(bins[1], [Fraction(180), Fraction(-180)])
24 |
--------------------------------------------------------------------------------
/etc/geohash_ctypes.py:
--------------------------------------------------------------------------------
1 | import ctypes
2 |
3 | # cc -shared -fPIC -O3 -s -o libgeohash.so geohash.c
4 | _SO = ctypes.CDLL("./libgeohash.so")
5 | _SO.geohash_encode.argtypes = (
6 | ctypes.c_char_p,
7 | ctypes.c_double,
8 | ctypes.c_double,
9 | )
10 | _SO.geohash_decode.restype = ctypes.c_int
11 | _SO.geohash_decode.argtypes = (
12 | ctypes.POINTER(ctypes.c_double),
13 | ctypes.POINTER(ctypes.c_double),
14 | ctypes.c_char_p,
15 | ctypes.c_int
16 | )
17 |
18 | def encode(lat, lon):
19 | if not (-90 <= lat < 90):
20 | raise ValueError("latitude must be in [90, 90)")
21 | if not (-180 <= lon < 180):
22 | raise ValueError("longitude must be in [180, 180)")
23 | buf = ctypes.create_string_buffer(21)
24 | _SO.geohash_encode(buf, lat, lon)
25 | return buf.value.decode()
26 |
27 | def decode(geohash):
28 | lat, lon = ctypes.c_double(), ctypes.c_double()
29 | encoded = geohash.encode()
30 | r = _SO.geohash_decode(
31 | ctypes.byref(lat), ctypes.byref(lon),
32 | encoded, len(encoded),
33 | )
34 | if r != 1:
35 | raise ValueError("invalid geohash")
36 | return lat.value, lon.value
37 |
--------------------------------------------------------------------------------
/UNLICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Geohash encoder/decoder in C
2 |
3 | A lean, efficient, accurate [geohash][] encoder and decoder library
4 | implemented in C. It does not depend on the C standard library and is
5 | well-suited for use in embedded systems. Care has been taken to ensure
6 | results are as accurate as possible within the limitations of IEEE 754
7 | double precision. It's also very fast, decoding ~50 million and encoding
8 | ~80 million latlons per second on modern desktop and laptop hardware.
9 |
10 | This is the fastest, most correct, and most accurate (to double precision)
11 | geohash decoder and encoder currently available.
12 |
13 | ```c
14 | // Validate and decode a geohash into a lat/lon pair. Returns 1 on success,
15 | // or 0 if the buffer does not contain a valid geohash.
16 | int geohash_decode(double *lat, double *lon, const char *buf, int len);
17 |
18 | // Store a 21-byte geohash encoding of a lat/lon pair. Does not write a
19 | // terminating null byte. Truncate as needed.
20 | void geohash_encode(char *buf, double lat, double lon);
21 | ```
22 |
23 | ## Example usage
24 |
25 | The source file `tests/example.c` demonstrates decoding and encoding
26 | lat/lon coordinates as command line arguments.
27 |
28 | $ ./example -d ezs42 dppn59uz86jzd 000000000000 zzzzzzzzzzzz
29 | ezs42 42@ 36' 17.929" N 5@ 36' 10.898" W
30 | dppn59uz86jzd 40@ 26' 26.160" N 79@ 59' 45.239" W
31 | 000000000000 89@ 59' 59.999" S 179@ 59' 59.999" W
32 | zzzzzzzzzzzz 89@ 59' 59.999" N 179@ 59' 59.999" E
33 |
34 | $ ./example -e "40@26'26.160\"N 79@59'45.239\"W"
35 | dppn59uz86jzd 40@ 26' 26.160" N 79@ 59' 45.239" W
36 |
37 |
38 | [geohash]: https://en.wikipedia.org/wiki/Geohash
39 |
--------------------------------------------------------------------------------
/tests/benchmark.c:
--------------------------------------------------------------------------------
1 | #define _POSIX_C_SOURCE 200112L
2 | #include
3 | #include "../geohash.h"
4 |
5 | #ifdef _WIN32
6 | #include
7 |
8 | static double
9 | now(void)
10 | {
11 | LARGE_INTEGER n, f;
12 | QueryPerformanceFrequency(&f);
13 | QueryPerformanceCounter(&n);
14 | return (double)n.QuadPart / f.QuadPart;
15 | }
16 |
17 | #else
18 | #include
19 |
20 | static double
21 | now(void)
22 | {
23 | struct timespec tv[1];
24 | clock_gettime(CLOCK_MONOTONIC, tv);
25 | return tv->tv_sec + tv->tv_nsec/1e9;
26 | }
27 | #endif
28 |
29 | static double
30 | benchmark_encode(int n)
31 | {
32 | char buf[21] = {0};
33 | unsigned tally = 0;
34 |
35 | double beg = now();
36 | for (long i = 0; i < 1<> 32;
40 | double lat = (double)(a >> 32 ) / 0x100000000 * 180 - 90;
41 | double lon = (double)(a & 0xffffffff) / 0x100000000 * 360 - 180;
42 |
43 | geohash_encode(buf, lat, lon);
44 | tally += buf[11];
45 | }
46 | volatile unsigned sink = tally; (void)sink;
47 | return (1<> 32;
62 | buf[ 0] = b32[a >> 0 & 0x1f];
63 | buf[ 1] = b32[a >> 5 & 0x1f];
64 | buf[ 2] = b32[a >> 10 & 0x1f];
65 | buf[ 3] = b32[a >> 15 & 0x1f];
66 | buf[ 4] = b32[a >> 20 & 0x1f];
67 | buf[ 5] = b32[a >> 25 & 0x1f];
68 | buf[ 6] = b32[a >> 30 & 0x1f];
69 | buf[ 7] = b32[a >> 35 & 0x1f];
70 | buf[ 8] = b32[a >> 40 & 0x1f];
71 | buf[ 9] = b32[a >> 45 & 0x1f];
72 | buf[10] = b32[a >> 50 & 0x1f];
73 | buf[11] = b32[a >> 55 & 0x1f];
74 |
75 | double lat, lon;
76 | geohash_decode(&lat, &lon, buf, 12);
77 | tally += 2*lat - lon;
78 | }
79 | volatile double sink = tally; (void)sink;
80 | return (1<
2 | #include
3 | #include
4 | #include
5 | #include "../geohash.h"
6 |
7 | struct hms { short h, m; unsigned short ms; };
8 |
9 | static struct hms
10 | hms_unpack(double a)
11 | {
12 | double f = fabs(fmod(a, 1.0));
13 | int h = a;
14 | int m = f*60;
15 | int ms = (f*3600 - m*60) * 1000;
16 | return (struct hms){h, m, ms};
17 | }
18 |
19 | static double
20 | hms_pack(struct hms v)
21 | {
22 | int sign = v.h < 0 ? -1 : +1;
23 | return sign * (labs(v.h) + v.m/60.0 + (v.ms/1000.0)/3600.0);
24 | }
25 |
26 | static int
27 | hms_parse(struct hms *lat, struct hms *lon, const char *s)
28 | {
29 | unsigned lath, latm, lats;
30 | unsigned lonh, lonm, lons;
31 | char latf[4], lonf[4];
32 | int r, latsign, lonsign;
33 | char ns, ew;
34 |
35 | r = sscanf(s, "%u@%u'%d.%3[0-9]\" %c %u@%u'%u.%3[0-9]\" %c",
36 | &lath, &latm, &lats, latf, &ns,
37 | &lonh, &lonm, &lons, lonf, &ew);
38 | if (r != 10) {
39 | return 0;
40 | }
41 | if (lath>89 || latm>59 || lats>59 || strlen(latf) < 3) {
42 | return 0;
43 | }
44 | if (lonh>179 || lonm>59 || lons>59 || strlen(lonf) < 3) {
45 | return 0;
46 | }
47 |
48 | switch (ns) {
49 | case 'N': latsign = +1; break;
50 | case 'S': latsign = -1; break;
51 | default: return 0;
52 | }
53 | switch (ew) {
54 | case 'E': lonsign = +1; break;
55 | case 'W': lonsign = -1; break;
56 | default: return 0;
57 | }
58 |
59 | lat->h = latsign*lath;
60 | lat->m = latm;
61 | lat->ms = lats*1000 + atoi(latf);
62 | lon->h = lonsign*lonh;
63 | lon->m = lonm;
64 | lon->ms = lons*1000 + atoi(lonf);
65 | return 1;
66 | }
67 |
68 | static void
69 | hms_print(char *buf, struct hms lat, struct hms lon)
70 | {
71 | sprintf(buf, "%2d@%3d'%3d.%03d\" %c%5d@%3d'%3d.%03d\" %c", // 38 bytes
72 | (int)labs(lat.h), lat.m, lat.ms/1000, lat.ms%1000, "NS"[lat.h<0],
73 | (int)labs(lon.h), lon.m, lon.ms/1000, lon.ms%1000, "EW"[lon.h<0]);
74 | }
75 |
76 | static void
77 | usage(FILE *f, const char *name)
78 | {
79 | fprintf(f, "usage: %s <-d|-e> [latlon...]\n", name);
80 | fprintf(f, "examples:\n");
81 | fprintf(f, " $ %s -d dppn59uz86jzd\n", name);
82 | fprintf(f, " $ %s -e \"40@26'26.160\\\"N 79@59'45.239\\\"W\"\n", name);
83 | }
84 |
85 | static int
86 | decode(int argc, char *argv[])
87 | {
88 | for (int i = 2; i < argc; i++) {
89 | double latlon[2];
90 | size_t len = strlen(argv[i]);
91 | int r = geohash_decode(latlon, latlon+1, argv[i], len > 14 ? 14 : len);
92 | if (!r) {
93 | fprintf(stderr, "%s: invalid geohash, %s\n", argv[0], argv[i]);
94 | return 1;
95 | }
96 |
97 | char repr[64];
98 | struct hms lat = hms_unpack(latlon[0]);
99 | struct hms lon = hms_unpack(latlon[1]);
100 | hms_print(repr, lat, lon);
101 | printf("%-15.14s%s\n", argv[i], repr);
102 | }
103 | return 0;
104 | }
105 |
106 | static int
107 | encode(int argc, char *argv[])
108 | {
109 | for (int i = 2; i < argc; i++) {
110 | struct hms lat, lon;
111 | if (!hms_parse(&lat, &lon, argv[i])) {
112 | fprintf(stderr, "%s: invalid lat/lon, %s\n", argv[0], argv[i]);
113 | return 1;
114 | }
115 |
116 | char target[64];
117 | hms_print(target, lat, lon);
118 |
119 | char buf[32];
120 | double latlon[] = { hms_pack(lat), hms_pack(lon) };
121 | geohash_encode(buf, latlon[0], latlon[1]);
122 |
123 | // Find shortest geohash with the same printout
124 | int best = 14;
125 | for (int n = 13; n >= 0; n--) {
126 | char repr[64];
127 | geohash_decode(latlon, latlon+1, buf, n);
128 | lat = hms_unpack(latlon[0]);
129 | lon = hms_unpack(latlon[1]);
130 | hms_print(repr, lat, lon);
131 | if (!strcmp(target, repr)) {
132 | best = n;
133 | }
134 | }
135 | buf[best] = 0;
136 |
137 | printf("%-15.14s%s\n", buf, target);
138 | }
139 | return 0;
140 | }
141 |
142 | int
143 | main(int argc, char *argv[])
144 | {
145 | if (argc < 2 || argv[1][0] != '-') {
146 | usage(stderr, argv[0]);
147 | return 1;
148 | }
149 | switch (argv[1][1]) {
150 | case 'd': return decode(argc, argv);
151 | case 'e': return encode(argc, argv);
152 | case 'h': usage(stdout, argv[0]); return 0;
153 | default: usage(stderr, argv[0]); return 2;
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/etc/geohash.py.c:
--------------------------------------------------------------------------------
1 | // Binary geohash module for Python 3.7+
2 | //
3 | // Linux build:
4 | // $ cc -shared $(python-config --includes) -fPIC -DNDEBUG \
5 | // -O3 -s -ffreestanding -nostdlib -o geohash.so geohash.py.c
6 | //
7 | // Windows build (w64devkit):
8 | // $ cc -shared -I"$PYTHONHOME/include" -L"$PYTHONHOME/libs" -DNDEBUG \
9 | // -O3 -s -ffreestanding -nostdlib -o geohash.pyd geohash.py.c -lpython3
10 | //
11 | // This is free and unencumbered software released into the public domain.
12 | #define PY_SSIZE_T_CLEAN
13 | #include
14 | #include "../geohash.c"
15 |
16 | PyDoc_STRVAR(encode_doc,
17 | "encode(latitude, longitude, /)\n--\n\n"
18 | "Encode (latitude, longitude) degrees into a geohash.\n\n"
19 | "Latitude must be in [-90, 90) and longitude must be in [-180, 180).");
20 |
21 | static PyObject *
22 | encode(PyObject *self, PyObject **args, Py_ssize_t nargs)
23 | {
24 | (void)self;
25 |
26 | double lat, lon;
27 | if (nargs != 2) {
28 | PyErr_SetString(PyExc_TypeError, "takes exactly two arguments");
29 | return 0;
30 | }
31 | lat = PyFloat_AsDouble(args[0]);
32 | if (PyErr_Occurred()) {
33 | return 0;
34 | }
35 | lon = PyFloat_AsDouble(args[1]);
36 | if (PyErr_Occurred()) {
37 | return 0;
38 | }
39 |
40 | if (!(lat >= -90.0 && lat < 90.0)) {
41 | PyErr_SetString(PyExc_ValueError, "latitude must be in [-90, 90)");
42 | return 0;
43 | }
44 | if (!(lon >= -180.0 && lon < 180.0)) {
45 | PyErr_SetString(PyExc_ValueError, "longitude must be in [-180, 180)");
46 | return 0;
47 | }
48 |
49 | char buf[21];
50 | geohash_encode(buf, lat, lon);
51 | return Py_BuildValue("s#", buf, (int)sizeof(buf));
52 | }
53 |
54 | PyDoc_STRVAR(decode_doc,
55 | "decode(geohash, /)\n--\n\n"
56 | "Decode a geohash into (latitude, longitude) degrees, as pair.\n\n"
57 | "The geohash must be either bytes or a string.");
58 |
59 | static PyObject *
60 | decode(PyObject *self, PyObject **args, Py_ssize_t nargs)
61 | {
62 | (void)self;
63 | (void)nargs;
64 |
65 | if (nargs != 1) {
66 | PyErr_SetString(PyExc_TypeError, "takes exactly one argument");
67 | return 0;
68 | }
69 |
70 | int len;
71 | char tmp[22];
72 | char *buf = tmp;
73 |
74 | if (Py_TYPE(args[0]) == &PyUnicode_Type) {
75 | Py_ssize_t slen;
76 | wchar_t *u = PyUnicode_AsWideCharString(args[0], &slen);
77 | len = slen > (Py_ssize_t)sizeof(tmp) ? (int)sizeof(tmp) : (int)slen;
78 | for (int i = 0; i < len; i++) {
79 | tmp[i] = u[i] > 127 ? 0 : u[i];
80 | }
81 |
82 | } else if (Py_TYPE(args[0]) == &PyBytes_Type) {
83 | Py_ssize_t slen = PyBytes_GET_SIZE(args[0]);
84 | buf = PyBytes_AsString(args[0]);
85 | len = slen > (Py_ssize_t)sizeof(buf) ? (int)sizeof(buf) : (int)slen;
86 |
87 | } else {
88 | PyErr_SetString(PyExc_TypeError, "must be bytes or str");
89 | return 0;
90 | }
91 |
92 | double lat, lon;
93 | if (!geohash_decode(&lat, &lon, buf, len)) {
94 | PyErr_SetString(PyExc_ValueError, "invalid geohash");
95 | return 0;
96 | }
97 | return Py_BuildValue("ff", lat, lon);
98 | }
99 |
100 | PyDoc_STRVAR(maxerr_doc,
101 | "maxerr(length, /)\n--\n\n"
102 | "Compute the maximum error in degrees for a given geohash byte length,\n"
103 | "as pair (latitude_maxerr, longitude_maxerr).\n\n");
104 |
105 | static PyObject *
106 | maxerr(PyObject *self, PyObject **args, Py_ssize_t nargs)
107 | {
108 | (void)self;
109 |
110 | if (nargs != 1) {
111 | PyErr_SetString(PyExc_TypeError, "takes exactly one argument");
112 | return 0;
113 | }
114 |
115 | long n = PyLong_AsLong(args[0]);
116 | if (PyErr_Occurred()) {
117 | return 0;
118 | }
119 | if (n < 0) {
120 | PyErr_SetString(PyExc_ValueError, "length must be non-negative");
121 | return 0;
122 | }
123 | if (n > 21) {
124 | n = 21;
125 | }
126 |
127 | double laterr = 90.0 / (1LL << ((5*n + 0) / 2));
128 | double lonerr = 180.0 / (1LL << ((5*n + 1) / 2));
129 | return Py_BuildValue("ff", laterr, lonerr);
130 | }
131 |
132 | static PyMethodDef methods[] = {
133 | {"encode", (PyCFunction)encode, METH_FASTCALL, encode_doc},
134 | {"decode", (PyCFunction)decode, METH_FASTCALL, decode_doc},
135 | {"maxerr", (PyCFunction)maxerr, METH_FASTCALL, maxerr_doc},
136 | {0, 0, 0, 0}
137 | };
138 |
139 | static struct PyModuleDef module = {
140 | PyModuleDef_HEAD_INIT,
141 | .m_name = "geohash",
142 | .m_doc = "Encoder and decoder for geohash",
143 | .m_methods = methods,
144 | };
145 |
146 | PyMODINIT_FUNC
147 | PyInit_geohash(void)
148 | {
149 | return PyModule_Create(&module);
150 | }
151 |
152 | #if defined(_WIN32) && __STDC_HOSTED__ == 0
153 | int DllMainCRTStartup(void) { return 1; }
154 | #endif
155 |
--------------------------------------------------------------------------------
/tests/tests.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "../geohash.h"
4 |
5 | // Test values were computed to exact precision using arbitrary precision
6 | static const struct {
7 | short len;
8 | char buf[22];
9 | double lat, lon;
10 | } tests[] = {
11 | {1, "y", 67.5, 112.5},
12 | {1, "6", -22.5, -67.5},
13 | {1, "e", 22.5, -22.5},
14 | {1, "f", 67.5, -67.5},
15 | {1, "k", -22.5, 22.5},
16 | {1, "u", 67.5, 22.5},
17 | {1, "q", -22.5, 112.5},
18 | {1, "d", 22.5, -67.5},
19 | {2, "zv", 75.9375, 174.375},
20 | {2, "z1", 53.4375, 140.625},
21 | {2, "mc", -36.5625, 84.375},
22 | {2, "vk", 70.3125, 61.875},
23 | {2, "2z", -2.8125, -140.625},
24 | {2, "kv", -14.0625, 39.375},
25 | {2, "zt", 75.9375, 163.125},
26 | {2, "qz", -2.8125, 129.375},
27 | {3, "q2q", -42.890625, 110.390625},
28 | {3, "81e", 9.140625, -175.078125},
29 | {3, "epn", 40.078125, -35.859375},
30 | {3, "v67", 58.359375, 61.171875},
31 | {3, "5bn", -89.296875, -2.109375},
32 | {3, "cgy", 66.796875, -92.109375},
33 | {3, "qfq", -31.640625, 132.890625},
34 | {3, "hw1", -55.546875, 24.609375},
35 | {4, "pxxs", -47.021484375, 168.22265625},
36 | {4, "g9dw", 54.580078125, -18.80859375},
37 | {4, "4d9f", -75.498046875, -64.86328125},
38 | {4, "x2qb", 1.494140625, 155.91796875},
39 | {4, "h7mp", -70.400390625, 18.45703125},
40 | {4, "sqs5", 37.177734375, 17.05078125},
41 | {4, "u646", 56.689453125, 14.58984375},
42 | {4, "uyf6", 83.408203125, 37.08984375},
43 | {5, "edcct", 15.75439453125, -19.79736328125},
44 | {5, "e54d9", 17.33642578125, -41.41845703125},
45 | {5, "d66m6", 13.60107421875, -75.47607421875},
46 | {5, "1qpcc", -55.92041015625, -112.78564453125},
47 | {5, "t7857", 20.28076171875, 56.40380859375},
48 | {5, "2xnzz", -4.24072265625, -147.67822265625},
49 | {5, "z00ry", 46.38427734375, 135.63720703125},
50 | {5, "ytx9t", 76.22314453125, 123.28857421875},
51 | {6, "zksr9z", 71.67205810546875, 152.3089599609375},
52 | {6, "e1k1t6", 7.30865478515625, -39.1387939453125},
53 | {6, "dnwd1y", 36.94976806640625, -80.7769775390625},
54 | {6, "yccrng", 56.09344482421875, 125.8099365234375},
55 | {6, "uswux8", 71.10626220703125, 32.3272705078125},
56 | {6, "uwkksc", 80.95550537109375, 28.6907958984375},
57 | {6, "mq3j2s", -8.89617919921875, 57.6837158203125},
58 | {6, "zrfspt", 89.32708740234375, 150.1007080078125},
59 | {7, "b9mh6g9", 52.79823303222656, -150.34584045410156},
60 | {7, "egse2zw", 20.300674438476562, -4.8799896240234375},
61 | {7, "wu0m6bs", 23.426284790039062, 124.22859191894531},
62 | {7, "jzg2b7g", -46.25312805175781, 83.33610534667969},
63 | {7, "1g8ryn2", -68.91517639160156, -100.63407897949219},
64 | {7, "f9mjshu", 53.02482604980469, -60.28678894042969},
65 | {7, "bf1g2pv", 56.86454772949219, -143.78150939941406},
66 | {7, "jsdtg7f", -63.65547180175781, 71.16188049316406},
67 | {8, "h96ym45x", -81.85784339904785, 26.59189224243164},
68 | {8, "um0gb5ku", 73.80280494689941, 12.311382293701172},
69 | {8, "8jybftw3", 32.50605583190918, -170.3891944885254},
70 | {8, "su74ppev", 24.299955368041992, 38.28168869018555},
71 | {8, "e4kjubc4", 13.671541213989258, -39.16471481323242},
72 | {8, "rgg502sx", -23.3748722076416, 172.98608779907227},
73 | {8, "01jt30rz", -83.44948768615723, -172.21086502075195},
74 | {8, "cqz25716", 82.98565864562988, -113.40997695922852},
75 | {9, "1evyv5c37", -67.69889116287231, -104.19246912002563},
76 | {9, "z3xggwugd", 54.13438081741333, 157.30887651443481},
77 | {9, "52szqdt3m", -85.89911699295044, -26.777222156524658},
78 | {9, "454xcmbqw", -71.72997236251831, -86.42882108688354},
79 | {9, "jpjd5s5sq", -50.25071382522583, 52.893269062042236},
80 | {9, "37nntnmmb", -26.947081089019775, -115.08554220199585},
81 | {9, "8245dx4vh", 0.6545662879943848, -165.82366704940796},
82 | {9, "pghzrdgzt", -71.83417081832886, 175.7646632194519},
83 | {10, "4y7u1k3pey", -54.11595672369003, -50.92009127140045},
84 | {10, "wxqt5t422v", 41.6876944899559, 121.79756104946136},
85 | {10, "cm7kebvdhx", 75.326769053936, -119.00714099407196},
86 | {10, "xtx2vcnfzb", 31.075303852558136, 167.95760571956635},
87 | {10, "j3v57qnnzd", -79.55082982778549, 63.43263924121857},
88 | {10, "fep9snz5vw", 62.17642933130264, -56.767489314079285},
89 | {10, "kb0b8yt29p", -44.87627774477005, 34.8449045419693},
90 | {10, "3qhubjezuh", -10.38347214460373, -117.06498563289642},
91 | {11, "7r0fvynppdv", -5.1074255257844925, -32.43405796587467},
92 | {11, "s44ny2wmzne", 12.440292611718178, 3.0960463732481003},
93 | {11, "6cgqvv3nv3w", -33.93972001969814, -51.425394639372826},
94 | {11, "f4b1z8krrub", 60.77901117503643, -89.6642404049635},
95 | {11, "81gnjcspgmu", 10.908039137721062, -175.52292577922344},
96 | {11, "dxtzrgft071", 43.483388498425484, -59.07004036009312},
97 | {11, "41ryucru4zb", -81.77463121712208, -78.882060572505},
98 | {11, "pxsjbh94urk", -46.77652694284916, 163.12656171619892},
99 | {12, "6738wm9rqbxk", -26.59939899109304, -76.36395061388612},
100 | {12, "re6rv439vb5u", -25.34378453157842, 160.88610095903277},
101 | {12, "suqbzyesf1nq", 24.07461334951222, 43.5876645706594},
102 | {12, "engkf9kmk2z6", 38.81148305721581, -40.31379545107484},
103 | {12, "mjq87eeuvb0n", -15.40476213209331, 54.29983036592603},
104 | {12, "1811x3uy327k", -89.72564010880888, -110.76856518164277},
105 | {12, "3ftq3m9mwdu3", -29.80769564397633, -93.8102544285357},
106 | {12, "eh2utyk3dnz7", 24.731892189010978, -43.686694744974375},
107 | {13, "us12qmc7zw9tr", 67.57621050579473, 24.53451150795445},
108 | {13, "7jqbr77bsg38r", -15.406848576385528, -35.183852969203144},
109 | {13, "eq7ndje9dw7s9", 36.32933232234791, -29.438440811354667},
110 | {13, "8m5yp4z48qymd", 29.195260007400066, -163.1593130598776},
111 | {13, "6n62596c95c2s", -9.836605831515044, -86.67830757563934},
112 | {13, "hjp13zh4k5mg8", -61.61641700891778, 9.926326854620129},
113 | {13, "krxdzn8zjb7pq", -2.292191789019853, 22.105773214716464},
114 | {13, "z04m8ewx4nq04", 45.987258127424866, 138.19505552994087},
115 | {14, "6rzfkqkpw29dte", -0.9751193321426399, -67.65903128951322},
116 | {14, "qqzn5enebxc8ks", -5.959397637925576, 111.25650864851195},
117 | {14, "ykpbp299n7uzf9", 67.50293966702884, 112.46937565330882},
118 | {14, "pypbe22j9seums", -56.15976707049413, 179.7913294035243},
119 | {14, "8sm27c03smexvd", 23.95597678521881, -149.95186193555128},
120 | {14, "xzhwuncqfs610c", 40.59978398348903, 175.25573287100997},
121 | {14, "mkveqqsxkrhzde", -17.672971999563742, 64.26540188316721},
122 | {14, "3dnev0qpy8q00b", -33.088115302089136, -103.13112165837083},
123 | {15, "8nbwe360bsjyn9p", 39.118345795504865, -179.15127670108632},
124 | {15, "u4r62pq8rm1jrhv", 58.091653895025956, 10.204551669521607},
125 | {15, "0gnknh1yq4j1wbv", -72.39881847526703, -137.19459787804226},
126 | {15, "wuz8ugph5kw9e6g", 26.867776481594774, 134.51537660432223},
127 | {15, "rbfqxyrzhmfme8m", -39.603106200593174, 172.26546723883075},
128 | {15, "ng4we6wz041gg1c", -71.96747596790374, 127.41771945460641},
129 | {15, "2ssy4qunnffj12s", -18.594688080702326, -150.71564996374946},
130 | {15, "78gcqccd26vx71y", -40.551508184944396, -16.927853464949294},
131 | {16, "ycke272q6d48bmtk", 52.62147561342317, 130.08956530951764},
132 | {16, "hhm2dgvv8dzthr3f", -65.98430024346271, 7.511590229241847},
133 | {16, "dubf7hcgetyjpm5e", 27.14098286697663, -55.060914744299225},
134 | {16, "f5zkb9prqzc9nrx3", 66.93549042795894, -79.77246652206304},
135 | {16, "02d7j0ess702m4v6", -86.65662104179319, -165.3612214768691},
136 | {16, "smycq4csg7udh4kx", 32.57941862007101, 21.008064995057794},
137 | {16, "zw21pwypg5x4umfj", 80.3704597505839, 157.8379679045065},
138 | {16, "bm4g8n50pe3wy1mq", 73.773211932903, -164.878368491718},
139 | {17, "txgx178hfcrpwyh65", 44.84426821034731, 72.47693433415179},
140 | {17, "tsyr5duk9p57cnx4y", 27.965135641459256, 76.44875490584354},
141 | {17, "vhgukh10jw6gev7v3", 72.48782711147896, 50.45083192557968},
142 | {17, "k7ctqsqv3eqqgrj4s", -22.959132664989284, 13.654362143463459},
143 | {17, "hwwkcdxkmdye48hh9", -52.58806133567077, 31.36518169025976},
144 | {17, "10q9j5tc60w9177y0", -88.39852523118452, -125.63165740897375},
145 | {17, "q6djkt4up1hdxw2hb", -29.98649015462661, 104.26433721326475},
146 | {17, "dwyxdzzc28p89zupx", 39.32989609887272, -58.227850790410685},
147 | {18, "87fe3tc177u8330hjv", 21.696859496979357, -165.16693817776797},
148 | {18, "pv5rgc6uhpx1jq6bj0", -60.50510177839641, 173.48906524511278},
149 | {18, "71m06pk755mfzk6hdc", -37.88444634554395, -37.87488612421896},
150 | {18, "d3e1esdzkr41p35q1b", 8.727173383051934, -74.37347947535784},
151 | {18, "3zm3e9jx0k7vgg026t", -3.9483595528182036, -93.70580989009056},
152 | {18, "13tcrjm87wvdqyg4ns", -81.3138547881087, -115.34873434504505},
153 | {18, "88dpse23bvqtg6ec5y", 4.14904170602, -154.48936172603283},
154 | {18, "6suhg5p9rrwwejuufh", -17.42955363988557, -61.732544394421325},
155 | {19, "2v32wm95nkp0ggud4d3", -15.35011038514007, -144.21587823406787},
156 | {19, "gv167nckx0neve4mfbm", 73.55835979142022, -9.358325590572818},
157 | {19, "7ffxusv8v400zrvd6xn", -28.142713141601163, -7.528853105285052},
158 | {19, "eqmhqhmdvds5frmmqy5", 35.927152407111365, -26.447283524149796},
159 | {19, "mcsj5hf86596evz0yrx", -35.65743908051637, 84.51035683870195},
160 | {19, "zke7y5dyjhw34xv55qm", 70.99196007862325, 151.08798399537955},
161 | {19, "eh01u9jf8v3se8dbmbc", 22.813568425235573, -44.79431532172306},
162 | {19, "zfcvj2btqhpw7ur8hvb", 61.35269987298038, 171.4426047536603},
163 | {20, "50kb4162ks10g95emn5v", -88.58681904037465, -38.22913735858451},
164 | {20, "vp5tcwqhv2wpvtf42e8n", 85.42089386634491, 49.99626630017035},
165 | {20, "0djemt78ev4qkwv96b8x", -78.14975809008581, -149.5189551167599},
166 | {20, "8rzxugxxpp8cb8s4gj1n", 44.97652292095542, -157.98378364345209},
167 | {20, "e8dg6qxny5mf6fhqpnu1", 3.4206711662393285, -18.524057185240235},
168 | {20, "kxtsr7j1fn3qm9z1em4t", -2.0486152031125204, 30.55993271757865},
169 | {20, "emsr9e2ukef1tre7dzww", 32.27446212698244, -27.706293537327777},
170 | {20, "rgv11mqxe8cczbq5m7y6", -23.700337859803284, 175.8452609157441},
171 | };
172 | static const int ntests = sizeof(tests) / sizeof(*tests);
173 |
174 | int
175 | main(void)
176 | {
177 | int fails = 0;
178 |
179 | for (int i = 0; i < ntests; i++) {
180 | double lat, lon;
181 | geohash_decode(&lat, &lon, tests[i].buf, tests[i].len);
182 | if (tests[i].lat != lat || tests[i].lon != lon) {
183 | printf("FAIL: %-20s %.17g %.17g, got %.17g %.17g\n",
184 | tests[i].buf, tests[i].lat, tests[i].lon, lat, lon);
185 | fails++;
186 | }
187 | }
188 |
189 | for (int i = 0; i < ntests; i++) {
190 | char buf[22] = {0};
191 | geohash_encode(buf, tests[i].lat, tests[i].lon);
192 | if (memcmp(buf, tests[i].buf, tests[i].len)) {
193 | buf[tests[i].len] = 0;
194 | printf("FAIL: %-20s %.17g %.17g, got %s\n",
195 | tests[i].buf, tests[i].lat, tests[i].lon, buf);
196 | fails++;
197 | }
198 | }
199 |
200 | if (fails) {
201 | printf("%d tests failed\n", fails);
202 | return 1;
203 | }
204 | printf("All tests passed\n");
205 | return 0;
206 | }
207 |
--------------------------------------------------------------------------------
/geohash.c:
--------------------------------------------------------------------------------
1 | // This is free and unencumbered software released into the public domain.
2 | #include "geohash.h"
3 |
4 | int
5 | geohash_decode(double *lat, double *lon, const char *buf, int len)
6 | {
7 | static const signed char b32[256] = {
8 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
9 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
10 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
11 | +0, +1, +2, +3, +4, +5, +6, +7, +8, +9, -1, -1, -1, -1, -1, -1,
12 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
13 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
14 | -1, -1, 10, 11, 12, 13, 14, 15, 16, -1, 17, 18, -1, 19, 20, -1,
15 | 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, -1,
16 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
17 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
18 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
19 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
20 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
21 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
22 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
23 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
24 | };
25 | static const short deinterleave[1024] = {
26 | 0x000, 0x001, 0x020, 0x021, 0x002, 0x003, 0x022, 0x023,
27 | 0x040, 0x041, 0x060, 0x061, 0x042, 0x043, 0x062, 0x063,
28 | 0x004, 0x005, 0x024, 0x025, 0x006, 0x007, 0x026, 0x027,
29 | 0x044, 0x045, 0x064, 0x065, 0x046, 0x047, 0x066, 0x067,
30 | 0x080, 0x081, 0x0a0, 0x0a1, 0x082, 0x083, 0x0a2, 0x0a3,
31 | 0x0c0, 0x0c1, 0x0e0, 0x0e1, 0x0c2, 0x0c3, 0x0e2, 0x0e3,
32 | 0x084, 0x085, 0x0a4, 0x0a5, 0x086, 0x087, 0x0a6, 0x0a7,
33 | 0x0c4, 0x0c5, 0x0e4, 0x0e5, 0x0c6, 0x0c7, 0x0e6, 0x0e7,
34 | 0x008, 0x009, 0x028, 0x029, 0x00a, 0x00b, 0x02a, 0x02b,
35 | 0x048, 0x049, 0x068, 0x069, 0x04a, 0x04b, 0x06a, 0x06b,
36 | 0x00c, 0x00d, 0x02c, 0x02d, 0x00e, 0x00f, 0x02e, 0x02f,
37 | 0x04c, 0x04d, 0x06c, 0x06d, 0x04e, 0x04f, 0x06e, 0x06f,
38 | 0x088, 0x089, 0x0a8, 0x0a9, 0x08a, 0x08b, 0x0aa, 0x0ab,
39 | 0x0c8, 0x0c9, 0x0e8, 0x0e9, 0x0ca, 0x0cb, 0x0ea, 0x0eb,
40 | 0x08c, 0x08d, 0x0ac, 0x0ad, 0x08e, 0x08f, 0x0ae, 0x0af,
41 | 0x0cc, 0x0cd, 0x0ec, 0x0ed, 0x0ce, 0x0cf, 0x0ee, 0x0ef,
42 | 0x100, 0x101, 0x120, 0x121, 0x102, 0x103, 0x122, 0x123,
43 | 0x140, 0x141, 0x160, 0x161, 0x142, 0x143, 0x162, 0x163,
44 | 0x104, 0x105, 0x124, 0x125, 0x106, 0x107, 0x126, 0x127,
45 | 0x144, 0x145, 0x164, 0x165, 0x146, 0x147, 0x166, 0x167,
46 | 0x180, 0x181, 0x1a0, 0x1a1, 0x182, 0x183, 0x1a2, 0x1a3,
47 | 0x1c0, 0x1c1, 0x1e0, 0x1e1, 0x1c2, 0x1c3, 0x1e2, 0x1e3,
48 | 0x184, 0x185, 0x1a4, 0x1a5, 0x186, 0x187, 0x1a6, 0x1a7,
49 | 0x1c4, 0x1c5, 0x1e4, 0x1e5, 0x1c6, 0x1c7, 0x1e6, 0x1e7,
50 | 0x108, 0x109, 0x128, 0x129, 0x10a, 0x10b, 0x12a, 0x12b,
51 | 0x148, 0x149, 0x168, 0x169, 0x14a, 0x14b, 0x16a, 0x16b,
52 | 0x10c, 0x10d, 0x12c, 0x12d, 0x10e, 0x10f, 0x12e, 0x12f,
53 | 0x14c, 0x14d, 0x16c, 0x16d, 0x14e, 0x14f, 0x16e, 0x16f,
54 | 0x188, 0x189, 0x1a8, 0x1a9, 0x18a, 0x18b, 0x1aa, 0x1ab,
55 | 0x1c8, 0x1c9, 0x1e8, 0x1e9, 0x1ca, 0x1cb, 0x1ea, 0x1eb,
56 | 0x18c, 0x18d, 0x1ac, 0x1ad, 0x18e, 0x18f, 0x1ae, 0x1af,
57 | 0x1cc, 0x1cd, 0x1ec, 0x1ed, 0x1ce, 0x1cf, 0x1ee, 0x1ef,
58 | 0x010, 0x011, 0x030, 0x031, 0x012, 0x013, 0x032, 0x033,
59 | 0x050, 0x051, 0x070, 0x071, 0x052, 0x053, 0x072, 0x073,
60 | 0x014, 0x015, 0x034, 0x035, 0x016, 0x017, 0x036, 0x037,
61 | 0x054, 0x055, 0x074, 0x075, 0x056, 0x057, 0x076, 0x077,
62 | 0x090, 0x091, 0x0b0, 0x0b1, 0x092, 0x093, 0x0b2, 0x0b3,
63 | 0x0d0, 0x0d1, 0x0f0, 0x0f1, 0x0d2, 0x0d3, 0x0f2, 0x0f3,
64 | 0x094, 0x095, 0x0b4, 0x0b5, 0x096, 0x097, 0x0b6, 0x0b7,
65 | 0x0d4, 0x0d5, 0x0f4, 0x0f5, 0x0d6, 0x0d7, 0x0f6, 0x0f7,
66 | 0x018, 0x019, 0x038, 0x039, 0x01a, 0x01b, 0x03a, 0x03b,
67 | 0x058, 0x059, 0x078, 0x079, 0x05a, 0x05b, 0x07a, 0x07b,
68 | 0x01c, 0x01d, 0x03c, 0x03d, 0x01e, 0x01f, 0x03e, 0x03f,
69 | 0x05c, 0x05d, 0x07c, 0x07d, 0x05e, 0x05f, 0x07e, 0x07f,
70 | 0x098, 0x099, 0x0b8, 0x0b9, 0x09a, 0x09b, 0x0ba, 0x0bb,
71 | 0x0d8, 0x0d9, 0x0f8, 0x0f9, 0x0da, 0x0db, 0x0fa, 0x0fb,
72 | 0x09c, 0x09d, 0x0bc, 0x0bd, 0x09e, 0x09f, 0x0be, 0x0bf,
73 | 0x0dc, 0x0dd, 0x0fc, 0x0fd, 0x0de, 0x0df, 0x0fe, 0x0ff,
74 | 0x110, 0x111, 0x130, 0x131, 0x112, 0x113, 0x132, 0x133,
75 | 0x150, 0x151, 0x170, 0x171, 0x152, 0x153, 0x172, 0x173,
76 | 0x114, 0x115, 0x134, 0x135, 0x116, 0x117, 0x136, 0x137,
77 | 0x154, 0x155, 0x174, 0x175, 0x156, 0x157, 0x176, 0x177,
78 | 0x190, 0x191, 0x1b0, 0x1b1, 0x192, 0x193, 0x1b2, 0x1b3,
79 | 0x1d0, 0x1d1, 0x1f0, 0x1f1, 0x1d2, 0x1d3, 0x1f2, 0x1f3,
80 | 0x194, 0x195, 0x1b4, 0x1b5, 0x196, 0x197, 0x1b6, 0x1b7,
81 | 0x1d4, 0x1d5, 0x1f4, 0x1f5, 0x1d6, 0x1d7, 0x1f6, 0x1f7,
82 | 0x118, 0x119, 0x138, 0x139, 0x11a, 0x11b, 0x13a, 0x13b,
83 | 0x158, 0x159, 0x178, 0x179, 0x15a, 0x15b, 0x17a, 0x17b,
84 | 0x11c, 0x11d, 0x13c, 0x13d, 0x11e, 0x11f, 0x13e, 0x13f,
85 | 0x15c, 0x15d, 0x17c, 0x17d, 0x15e, 0x15f, 0x17e, 0x17f,
86 | 0x198, 0x199, 0x1b8, 0x1b9, 0x19a, 0x19b, 0x1ba, 0x1bb,
87 | 0x1d8, 0x1d9, 0x1f8, 0x1f9, 0x1da, 0x1db, 0x1fa, 0x1fb,
88 | 0x19c, 0x19d, 0x1bc, 0x1bd, 0x19e, 0x19f, 0x1be, 0x1bf,
89 | 0x1dc, 0x1dd, 0x1fc, 0x1fd, 0x1de, 0x1df, 0x1fe, 0x1ff,
90 | 0x200, 0x201, 0x220, 0x221, 0x202, 0x203, 0x222, 0x223,
91 | 0x240, 0x241, 0x260, 0x261, 0x242, 0x243, 0x262, 0x263,
92 | 0x204, 0x205, 0x224, 0x225, 0x206, 0x207, 0x226, 0x227,
93 | 0x244, 0x245, 0x264, 0x265, 0x246, 0x247, 0x266, 0x267,
94 | 0x280, 0x281, 0x2a0, 0x2a1, 0x282, 0x283, 0x2a2, 0x2a3,
95 | 0x2c0, 0x2c1, 0x2e0, 0x2e1, 0x2c2, 0x2c3, 0x2e2, 0x2e3,
96 | 0x284, 0x285, 0x2a4, 0x2a5, 0x286, 0x287, 0x2a6, 0x2a7,
97 | 0x2c4, 0x2c5, 0x2e4, 0x2e5, 0x2c6, 0x2c7, 0x2e6, 0x2e7,
98 | 0x208, 0x209, 0x228, 0x229, 0x20a, 0x20b, 0x22a, 0x22b,
99 | 0x248, 0x249, 0x268, 0x269, 0x24a, 0x24b, 0x26a, 0x26b,
100 | 0x20c, 0x20d, 0x22c, 0x22d, 0x20e, 0x20f, 0x22e, 0x22f,
101 | 0x24c, 0x24d, 0x26c, 0x26d, 0x24e, 0x24f, 0x26e, 0x26f,
102 | 0x288, 0x289, 0x2a8, 0x2a9, 0x28a, 0x28b, 0x2aa, 0x2ab,
103 | 0x2c8, 0x2c9, 0x2e8, 0x2e9, 0x2ca, 0x2cb, 0x2ea, 0x2eb,
104 | 0x28c, 0x28d, 0x2ac, 0x2ad, 0x28e, 0x28f, 0x2ae, 0x2af,
105 | 0x2cc, 0x2cd, 0x2ec, 0x2ed, 0x2ce, 0x2cf, 0x2ee, 0x2ef,
106 | 0x300, 0x301, 0x320, 0x321, 0x302, 0x303, 0x322, 0x323,
107 | 0x340, 0x341, 0x360, 0x361, 0x342, 0x343, 0x362, 0x363,
108 | 0x304, 0x305, 0x324, 0x325, 0x306, 0x307, 0x326, 0x327,
109 | 0x344, 0x345, 0x364, 0x365, 0x346, 0x347, 0x366, 0x367,
110 | 0x380, 0x381, 0x3a0, 0x3a1, 0x382, 0x383, 0x3a2, 0x3a3,
111 | 0x3c0, 0x3c1, 0x3e0, 0x3e1, 0x3c2, 0x3c3, 0x3e2, 0x3e3,
112 | 0x384, 0x385, 0x3a4, 0x3a5, 0x386, 0x387, 0x3a6, 0x3a7,
113 | 0x3c4, 0x3c5, 0x3e4, 0x3e5, 0x3c6, 0x3c7, 0x3e6, 0x3e7,
114 | 0x308, 0x309, 0x328, 0x329, 0x30a, 0x30b, 0x32a, 0x32b,
115 | 0x348, 0x349, 0x368, 0x369, 0x34a, 0x34b, 0x36a, 0x36b,
116 | 0x30c, 0x30d, 0x32c, 0x32d, 0x30e, 0x30f, 0x32e, 0x32f,
117 | 0x34c, 0x34d, 0x36c, 0x36d, 0x34e, 0x34f, 0x36e, 0x36f,
118 | 0x388, 0x389, 0x3a8, 0x3a9, 0x38a, 0x38b, 0x3aa, 0x3ab,
119 | 0x3c8, 0x3c9, 0x3e8, 0x3e9, 0x3ca, 0x3cb, 0x3ea, 0x3eb,
120 | 0x38c, 0x38d, 0x3ac, 0x3ad, 0x38e, 0x38f, 0x3ae, 0x3af,
121 | 0x3cc, 0x3cd, 0x3ec, 0x3ed, 0x3ce, 0x3cf, 0x3ee, 0x3ef,
122 | 0x210, 0x211, 0x230, 0x231, 0x212, 0x213, 0x232, 0x233,
123 | 0x250, 0x251, 0x270, 0x271, 0x252, 0x253, 0x272, 0x273,
124 | 0x214, 0x215, 0x234, 0x235, 0x216, 0x217, 0x236, 0x237,
125 | 0x254, 0x255, 0x274, 0x275, 0x256, 0x257, 0x276, 0x277,
126 | 0x290, 0x291, 0x2b0, 0x2b1, 0x292, 0x293, 0x2b2, 0x2b3,
127 | 0x2d0, 0x2d1, 0x2f0, 0x2f1, 0x2d2, 0x2d3, 0x2f2, 0x2f3,
128 | 0x294, 0x295, 0x2b4, 0x2b5, 0x296, 0x297, 0x2b6, 0x2b7,
129 | 0x2d4, 0x2d5, 0x2f4, 0x2f5, 0x2d6, 0x2d7, 0x2f6, 0x2f7,
130 | 0x218, 0x219, 0x238, 0x239, 0x21a, 0x21b, 0x23a, 0x23b,
131 | 0x258, 0x259, 0x278, 0x279, 0x25a, 0x25b, 0x27a, 0x27b,
132 | 0x21c, 0x21d, 0x23c, 0x23d, 0x21e, 0x21f, 0x23e, 0x23f,
133 | 0x25c, 0x25d, 0x27c, 0x27d, 0x25e, 0x25f, 0x27e, 0x27f,
134 | 0x298, 0x299, 0x2b8, 0x2b9, 0x29a, 0x29b, 0x2ba, 0x2bb,
135 | 0x2d8, 0x2d9, 0x2f8, 0x2f9, 0x2da, 0x2db, 0x2fa, 0x2fb,
136 | 0x29c, 0x29d, 0x2bc, 0x2bd, 0x29e, 0x29f, 0x2be, 0x2bf,
137 | 0x2dc, 0x2dd, 0x2fc, 0x2fd, 0x2de, 0x2df, 0x2fe, 0x2ff,
138 | 0x310, 0x311, 0x330, 0x331, 0x312, 0x313, 0x332, 0x333,
139 | 0x350, 0x351, 0x370, 0x371, 0x352, 0x353, 0x372, 0x373,
140 | 0x314, 0x315, 0x334, 0x335, 0x316, 0x317, 0x336, 0x337,
141 | 0x354, 0x355, 0x374, 0x375, 0x356, 0x357, 0x376, 0x377,
142 | 0x390, 0x391, 0x3b0, 0x3b1, 0x392, 0x393, 0x3b2, 0x3b3,
143 | 0x3d0, 0x3d1, 0x3f0, 0x3f1, 0x3d2, 0x3d3, 0x3f2, 0x3f3,
144 | 0x394, 0x395, 0x3b4, 0x3b5, 0x396, 0x397, 0x3b6, 0x3b7,
145 | 0x3d4, 0x3d5, 0x3f4, 0x3f5, 0x3d6, 0x3d7, 0x3f6, 0x3f7,
146 | 0x318, 0x319, 0x338, 0x339, 0x31a, 0x31b, 0x33a, 0x33b,
147 | 0x358, 0x359, 0x378, 0x379, 0x35a, 0x35b, 0x37a, 0x37b,
148 | 0x31c, 0x31d, 0x33c, 0x33d, 0x31e, 0x31f, 0x33e, 0x33f,
149 | 0x35c, 0x35d, 0x37c, 0x37d, 0x35e, 0x35f, 0x37e, 0x37f,
150 | 0x398, 0x399, 0x3b8, 0x3b9, 0x39a, 0x39b, 0x3ba, 0x3bb,
151 | 0x3d8, 0x3d9, 0x3f8, 0x3f9, 0x3da, 0x3db, 0x3fa, 0x3fb,
152 | 0x39c, 0x39d, 0x3bc, 0x3bd, 0x39e, 0x39f, 0x3be, 0x3bf,
153 | 0x3dc, 0x3dd, 0x3fc, 0x3fd, 0x3de, 0x3df, 0x3fe, 0x3ff,
154 | };
155 |
156 | len = len > 21 ? 21 : len;
157 | long long blat = 0;
158 | long long blon = 0;
159 |
160 | // Note: This has been optimized for decoding shorter geohashes by
161 | // bailing out early when input ends. The benchmark shows that this is
162 | // faster than unconditionally parsing the full 20 bytes (with care not
163 | // to overread) and discarding. If the input length is known statically,
164 | // these conditional branches could be eliminated and perhaps even the
165 | // loop unrolled.
166 |
167 | // Decode two bytes at a time
168 | for (int i = 0; i+1 < len; i += 2) {
169 | int hi = b32[buf[i+0]&0xff];
170 | int lo = b32[buf[i+1]&0xff];
171 | if (hi < 0 || lo < 0) {
172 | return 0;
173 | }
174 | int c = deinterleave[hi<<5 | lo];
175 | blat |= (long long)(c & 0x01f) << (55 - i/2*5);
176 | blon |= (long long)(c & 0x3e0) << (50 - i/2*5);
177 | }
178 |
179 | // Handle tail byte, if any
180 | if (len & 1) {
181 | int hi = b32[buf[len-1]&0xff];
182 | if (hi < 0) {
183 | return 0;
184 | }
185 | int c = deinterleave[hi<<5];
186 | blat |= (long long)(c & 0x01f) << (55 - len/2*5);
187 | blon |= (long long)(c & 0x3e0) << (50 - len/2*5);
188 | }
189 |
190 | // The bottom 5 bits are unused. Everything was done higher to simplify
191 | // shifting (no prior right shifts were necessary). We'll need more
192 | // overhead below.
193 | blat >>= 5;
194 | blon >>= 5;
195 |
196 | // Compute the center (mean) between the decoded value (lower bound) and
197 | // the next neighbor up (upper bound). Shortcut: Set the bit just below
198 | // the last decoded bit to split the difference. Then shift the range by
199 | // half to center it around zero.
200 | long long hlat = blat + (1LL << (54 - (0 + len*5)/2)) - (1LL << 54);
201 | long long hlon = blon + (1LL << (54 - (1 + len*5)/2)) - (1LL << 54);
202 | *lat = 180 * hlat / (double)(1LL << 55);
203 | *lon = 360 * hlon / (double)(1LL << 55);
204 | return 1;
205 | }
206 |
207 | void
208 | geohash_encode(char *buf, double lat, double lon)
209 | {
210 | static const short interleave_b32[1024] = {
211 | 0x3030, 0x3031, 0x3034, 0x3035, 0x3068, 0x306a, 0x306e, 0x3070,
212 | 0x3230, 0x3231, 0x3234, 0x3235, 0x3268, 0x326a, 0x326e, 0x3270,
213 | 0x3830, 0x3831, 0x3834, 0x3835, 0x3868, 0x386a, 0x386e, 0x3870,
214 | 0x6230, 0x6231, 0x6234, 0x6235, 0x6268, 0x626a, 0x626e, 0x6270,
215 | 0x3032, 0x3033, 0x3036, 0x3037, 0x306b, 0x306d, 0x3071, 0x3072,
216 | 0x3232, 0x3233, 0x3236, 0x3237, 0x326b, 0x326d, 0x3271, 0x3272,
217 | 0x3832, 0x3833, 0x3836, 0x3837, 0x386b, 0x386d, 0x3871, 0x3872,
218 | 0x6232, 0x6233, 0x6236, 0x6237, 0x626b, 0x626d, 0x6271, 0x6272,
219 | 0x3038, 0x3039, 0x3064, 0x3065, 0x3073, 0x3074, 0x3077, 0x3078,
220 | 0x3238, 0x3239, 0x3264, 0x3265, 0x3273, 0x3274, 0x3277, 0x3278,
221 | 0x3838, 0x3839, 0x3864, 0x3865, 0x3873, 0x3874, 0x3877, 0x3878,
222 | 0x6238, 0x6239, 0x6264, 0x6265, 0x6273, 0x6274, 0x6277, 0x6278,
223 | 0x3062, 0x3063, 0x3066, 0x3067, 0x3075, 0x3076, 0x3079, 0x307a,
224 | 0x3262, 0x3263, 0x3266, 0x3267, 0x3275, 0x3276, 0x3279, 0x327a,
225 | 0x3862, 0x3863, 0x3866, 0x3867, 0x3875, 0x3876, 0x3879, 0x387a,
226 | 0x6262, 0x6263, 0x6266, 0x6267, 0x6275, 0x6276, 0x6279, 0x627a,
227 | 0x3130, 0x3131, 0x3134, 0x3135, 0x3168, 0x316a, 0x316e, 0x3170,
228 | 0x3330, 0x3331, 0x3334, 0x3335, 0x3368, 0x336a, 0x336e, 0x3370,
229 | 0x3930, 0x3931, 0x3934, 0x3935, 0x3968, 0x396a, 0x396e, 0x3970,
230 | 0x6330, 0x6331, 0x6334, 0x6335, 0x6368, 0x636a, 0x636e, 0x6370,
231 | 0x3132, 0x3133, 0x3136, 0x3137, 0x316b, 0x316d, 0x3171, 0x3172,
232 | 0x3332, 0x3333, 0x3336, 0x3337, 0x336b, 0x336d, 0x3371, 0x3372,
233 | 0x3932, 0x3933, 0x3936, 0x3937, 0x396b, 0x396d, 0x3971, 0x3972,
234 | 0x6332, 0x6333, 0x6336, 0x6337, 0x636b, 0x636d, 0x6371, 0x6372,
235 | 0x3138, 0x3139, 0x3164, 0x3165, 0x3173, 0x3174, 0x3177, 0x3178,
236 | 0x3338, 0x3339, 0x3364, 0x3365, 0x3373, 0x3374, 0x3377, 0x3378,
237 | 0x3938, 0x3939, 0x3964, 0x3965, 0x3973, 0x3974, 0x3977, 0x3978,
238 | 0x6338, 0x6339, 0x6364, 0x6365, 0x6373, 0x6374, 0x6377, 0x6378,
239 | 0x3162, 0x3163, 0x3166, 0x3167, 0x3175, 0x3176, 0x3179, 0x317a,
240 | 0x3362, 0x3363, 0x3366, 0x3367, 0x3375, 0x3376, 0x3379, 0x337a,
241 | 0x3962, 0x3963, 0x3966, 0x3967, 0x3975, 0x3976, 0x3979, 0x397a,
242 | 0x6362, 0x6363, 0x6366, 0x6367, 0x6375, 0x6376, 0x6379, 0x637a,
243 | 0x3430, 0x3431, 0x3434, 0x3435, 0x3468, 0x346a, 0x346e, 0x3470,
244 | 0x3630, 0x3631, 0x3634, 0x3635, 0x3668, 0x366a, 0x366e, 0x3670,
245 | 0x6430, 0x6431, 0x6434, 0x6435, 0x6468, 0x646a, 0x646e, 0x6470,
246 | 0x6630, 0x6631, 0x6634, 0x6635, 0x6668, 0x666a, 0x666e, 0x6670,
247 | 0x3432, 0x3433, 0x3436, 0x3437, 0x346b, 0x346d, 0x3471, 0x3472,
248 | 0x3632, 0x3633, 0x3636, 0x3637, 0x366b, 0x366d, 0x3671, 0x3672,
249 | 0x6432, 0x6433, 0x6436, 0x6437, 0x646b, 0x646d, 0x6471, 0x6472,
250 | 0x6632, 0x6633, 0x6636, 0x6637, 0x666b, 0x666d, 0x6671, 0x6672,
251 | 0x3438, 0x3439, 0x3464, 0x3465, 0x3473, 0x3474, 0x3477, 0x3478,
252 | 0x3638, 0x3639, 0x3664, 0x3665, 0x3673, 0x3674, 0x3677, 0x3678,
253 | 0x6438, 0x6439, 0x6464, 0x6465, 0x6473, 0x6474, 0x6477, 0x6478,
254 | 0x6638, 0x6639, 0x6664, 0x6665, 0x6673, 0x6674, 0x6677, 0x6678,
255 | 0x3462, 0x3463, 0x3466, 0x3467, 0x3475, 0x3476, 0x3479, 0x347a,
256 | 0x3662, 0x3663, 0x3666, 0x3667, 0x3675, 0x3676, 0x3679, 0x367a,
257 | 0x6462, 0x6463, 0x6466, 0x6467, 0x6475, 0x6476, 0x6479, 0x647a,
258 | 0x6662, 0x6663, 0x6666, 0x6667, 0x6675, 0x6676, 0x6679, 0x667a,
259 | 0x3530, 0x3531, 0x3534, 0x3535, 0x3568, 0x356a, 0x356e, 0x3570,
260 | 0x3730, 0x3731, 0x3734, 0x3735, 0x3768, 0x376a, 0x376e, 0x3770,
261 | 0x6530, 0x6531, 0x6534, 0x6535, 0x6568, 0x656a, 0x656e, 0x6570,
262 | 0x6730, 0x6731, 0x6734, 0x6735, 0x6768, 0x676a, 0x676e, 0x6770,
263 | 0x3532, 0x3533, 0x3536, 0x3537, 0x356b, 0x356d, 0x3571, 0x3572,
264 | 0x3732, 0x3733, 0x3736, 0x3737, 0x376b, 0x376d, 0x3771, 0x3772,
265 | 0x6532, 0x6533, 0x6536, 0x6537, 0x656b, 0x656d, 0x6571, 0x6572,
266 | 0x6732, 0x6733, 0x6736, 0x6737, 0x676b, 0x676d, 0x6771, 0x6772,
267 | 0x3538, 0x3539, 0x3564, 0x3565, 0x3573, 0x3574, 0x3577, 0x3578,
268 | 0x3738, 0x3739, 0x3764, 0x3765, 0x3773, 0x3774, 0x3777, 0x3778,
269 | 0x6538, 0x6539, 0x6564, 0x6565, 0x6573, 0x6574, 0x6577, 0x6578,
270 | 0x6738, 0x6739, 0x6764, 0x6765, 0x6773, 0x6774, 0x6777, 0x6778,
271 | 0x3562, 0x3563, 0x3566, 0x3567, 0x3575, 0x3576, 0x3579, 0x357a,
272 | 0x3762, 0x3763, 0x3766, 0x3767, 0x3775, 0x3776, 0x3779, 0x377a,
273 | 0x6562, 0x6563, 0x6566, 0x6567, 0x6575, 0x6576, 0x6579, 0x657a,
274 | 0x6762, 0x6763, 0x6766, 0x6767, 0x6775, 0x6776, 0x6779, 0x677a,
275 | 0x6830, 0x6831, 0x6834, 0x6835, 0x6868, 0x686a, 0x686e, 0x6870,
276 | 0x6b30, 0x6b31, 0x6b34, 0x6b35, 0x6b68, 0x6b6a, 0x6b6e, 0x6b70,
277 | 0x7330, 0x7331, 0x7334, 0x7335, 0x7368, 0x736a, 0x736e, 0x7370,
278 | 0x7530, 0x7531, 0x7534, 0x7535, 0x7568, 0x756a, 0x756e, 0x7570,
279 | 0x6832, 0x6833, 0x6836, 0x6837, 0x686b, 0x686d, 0x6871, 0x6872,
280 | 0x6b32, 0x6b33, 0x6b36, 0x6b37, 0x6b6b, 0x6b6d, 0x6b71, 0x6b72,
281 | 0x7332, 0x7333, 0x7336, 0x7337, 0x736b, 0x736d, 0x7371, 0x7372,
282 | 0x7532, 0x7533, 0x7536, 0x7537, 0x756b, 0x756d, 0x7571, 0x7572,
283 | 0x6838, 0x6839, 0x6864, 0x6865, 0x6873, 0x6874, 0x6877, 0x6878,
284 | 0x6b38, 0x6b39, 0x6b64, 0x6b65, 0x6b73, 0x6b74, 0x6b77, 0x6b78,
285 | 0x7338, 0x7339, 0x7364, 0x7365, 0x7373, 0x7374, 0x7377, 0x7378,
286 | 0x7538, 0x7539, 0x7564, 0x7565, 0x7573, 0x7574, 0x7577, 0x7578,
287 | 0x6862, 0x6863, 0x6866, 0x6867, 0x6875, 0x6876, 0x6879, 0x687a,
288 | 0x6b62, 0x6b63, 0x6b66, 0x6b67, 0x6b75, 0x6b76, 0x6b79, 0x6b7a,
289 | 0x7362, 0x7363, 0x7366, 0x7367, 0x7375, 0x7376, 0x7379, 0x737a,
290 | 0x7562, 0x7563, 0x7566, 0x7567, 0x7575, 0x7576, 0x7579, 0x757a,
291 | 0x6a30, 0x6a31, 0x6a34, 0x6a35, 0x6a68, 0x6a6a, 0x6a6e, 0x6a70,
292 | 0x6d30, 0x6d31, 0x6d34, 0x6d35, 0x6d68, 0x6d6a, 0x6d6e, 0x6d70,
293 | 0x7430, 0x7431, 0x7434, 0x7435, 0x7468, 0x746a, 0x746e, 0x7470,
294 | 0x7630, 0x7631, 0x7634, 0x7635, 0x7668, 0x766a, 0x766e, 0x7670,
295 | 0x6a32, 0x6a33, 0x6a36, 0x6a37, 0x6a6b, 0x6a6d, 0x6a71, 0x6a72,
296 | 0x6d32, 0x6d33, 0x6d36, 0x6d37, 0x6d6b, 0x6d6d, 0x6d71, 0x6d72,
297 | 0x7432, 0x7433, 0x7436, 0x7437, 0x746b, 0x746d, 0x7471, 0x7472,
298 | 0x7632, 0x7633, 0x7636, 0x7637, 0x766b, 0x766d, 0x7671, 0x7672,
299 | 0x6a38, 0x6a39, 0x6a64, 0x6a65, 0x6a73, 0x6a74, 0x6a77, 0x6a78,
300 | 0x6d38, 0x6d39, 0x6d64, 0x6d65, 0x6d73, 0x6d74, 0x6d77, 0x6d78,
301 | 0x7438, 0x7439, 0x7464, 0x7465, 0x7473, 0x7474, 0x7477, 0x7478,
302 | 0x7638, 0x7639, 0x7664, 0x7665, 0x7673, 0x7674, 0x7677, 0x7678,
303 | 0x6a62, 0x6a63, 0x6a66, 0x6a67, 0x6a75, 0x6a76, 0x6a79, 0x6a7a,
304 | 0x6d62, 0x6d63, 0x6d66, 0x6d67, 0x6d75, 0x6d76, 0x6d79, 0x6d7a,
305 | 0x7462, 0x7463, 0x7466, 0x7467, 0x7475, 0x7476, 0x7479, 0x747a,
306 | 0x7662, 0x7663, 0x7666, 0x7667, 0x7675, 0x7676, 0x7679, 0x767a,
307 | 0x6e30, 0x6e31, 0x6e34, 0x6e35, 0x6e68, 0x6e6a, 0x6e6e, 0x6e70,
308 | 0x7130, 0x7131, 0x7134, 0x7135, 0x7168, 0x716a, 0x716e, 0x7170,
309 | 0x7730, 0x7731, 0x7734, 0x7735, 0x7768, 0x776a, 0x776e, 0x7770,
310 | 0x7930, 0x7931, 0x7934, 0x7935, 0x7968, 0x796a, 0x796e, 0x7970,
311 | 0x6e32, 0x6e33, 0x6e36, 0x6e37, 0x6e6b, 0x6e6d, 0x6e71, 0x6e72,
312 | 0x7132, 0x7133, 0x7136, 0x7137, 0x716b, 0x716d, 0x7171, 0x7172,
313 | 0x7732, 0x7733, 0x7736, 0x7737, 0x776b, 0x776d, 0x7771, 0x7772,
314 | 0x7932, 0x7933, 0x7936, 0x7937, 0x796b, 0x796d, 0x7971, 0x7972,
315 | 0x6e38, 0x6e39, 0x6e64, 0x6e65, 0x6e73, 0x6e74, 0x6e77, 0x6e78,
316 | 0x7138, 0x7139, 0x7164, 0x7165, 0x7173, 0x7174, 0x7177, 0x7178,
317 | 0x7738, 0x7739, 0x7764, 0x7765, 0x7773, 0x7774, 0x7777, 0x7778,
318 | 0x7938, 0x7939, 0x7964, 0x7965, 0x7973, 0x7974, 0x7977, 0x7978,
319 | 0x6e62, 0x6e63, 0x6e66, 0x6e67, 0x6e75, 0x6e76, 0x6e79, 0x6e7a,
320 | 0x7162, 0x7163, 0x7166, 0x7167, 0x7175, 0x7176, 0x7179, 0x717a,
321 | 0x7762, 0x7763, 0x7766, 0x7767, 0x7775, 0x7776, 0x7779, 0x777a,
322 | 0x7962, 0x7963, 0x7966, 0x7967, 0x7975, 0x7976, 0x7979, 0x797a,
323 | 0x7030, 0x7031, 0x7034, 0x7035, 0x7068, 0x706a, 0x706e, 0x7070,
324 | 0x7230, 0x7231, 0x7234, 0x7235, 0x7268, 0x726a, 0x726e, 0x7270,
325 | 0x7830, 0x7831, 0x7834, 0x7835, 0x7868, 0x786a, 0x786e, 0x7870,
326 | 0x7a30, 0x7a31, 0x7a34, 0x7a35, 0x7a68, 0x7a6a, 0x7a6e, 0x7a70,
327 | 0x7032, 0x7033, 0x7036, 0x7037, 0x706b, 0x706d, 0x7071, 0x7072,
328 | 0x7232, 0x7233, 0x7236, 0x7237, 0x726b, 0x726d, 0x7271, 0x7272,
329 | 0x7832, 0x7833, 0x7836, 0x7837, 0x786b, 0x786d, 0x7871, 0x7872,
330 | 0x7a32, 0x7a33, 0x7a36, 0x7a37, 0x7a6b, 0x7a6d, 0x7a71, 0x7a72,
331 | 0x7038, 0x7039, 0x7064, 0x7065, 0x7073, 0x7074, 0x7077, 0x7078,
332 | 0x7238, 0x7239, 0x7264, 0x7265, 0x7273, 0x7274, 0x7277, 0x7278,
333 | 0x7838, 0x7839, 0x7864, 0x7865, 0x7873, 0x7874, 0x7877, 0x7878,
334 | 0x7a38, 0x7a39, 0x7a64, 0x7a65, 0x7a73, 0x7a74, 0x7a77, 0x7a78,
335 | 0x7062, 0x7063, 0x7066, 0x7067, 0x7075, 0x7076, 0x7079, 0x707a,
336 | 0x7262, 0x7263, 0x7266, 0x7267, 0x7275, 0x7276, 0x7279, 0x727a,
337 | 0x7862, 0x7863, 0x7866, 0x7867, 0x7875, 0x7876, 0x7879, 0x787a,
338 | 0x7a62, 0x7a63, 0x7a66, 0x7a67, 0x7a75, 0x7a76, 0x7a79, 0x7a7a,
339 | };
340 | long long blat = (lat + 90) / 180 * (1LL << 53);
341 | long long blon = (lon + 180) / 360 * (1LL << 53);
342 | short chunks[11] = {
343 | interleave_b32[(blon >> 43 & 0x3e0) | (blat >> 48 & 0x01f)],
344 | interleave_b32[(blon >> 38 & 0x3e0) | (blat >> 43 & 0x01f)],
345 | interleave_b32[(blon >> 33 & 0x3e0) | (blat >> 38 & 0x01f)],
346 | interleave_b32[(blon >> 28 & 0x3e0) | (blat >> 33 & 0x01f)],
347 | interleave_b32[(blon >> 23 & 0x3e0) | (blat >> 28 & 0x01f)],
348 | interleave_b32[(blon >> 18 & 0x3e0) | (blat >> 23 & 0x01f)],
349 | interleave_b32[(blon >> 13 & 0x3e0) | (blat >> 18 & 0x01f)],
350 | interleave_b32[(blon >> 8 & 0x3e0) | (blat >> 13 & 0x01f)],
351 | interleave_b32[(blon >> 3 & 0x3e0) | (blat >> 8 & 0x01f)],
352 | interleave_b32[(blon << 2 & 0x3e0) | (blat >> 3 & 0x01f)],
353 | interleave_b32[(blon << 7 & 0x3e0) | (blat << 2 & 0x01f)],
354 | };
355 | buf[ 0] = chunks[ 0] >> 8; buf[ 1] = chunks[ 0] >> 0;
356 | buf[ 2] = chunks[ 1] >> 8; buf[ 3] = chunks[ 1] >> 0;
357 | buf[ 4] = chunks[ 2] >> 8; buf[ 5] = chunks[ 2] >> 0;
358 | buf[ 6] = chunks[ 3] >> 8; buf[ 7] = chunks[ 3] >> 0;
359 | buf[ 8] = chunks[ 4] >> 8; buf[ 9] = chunks[ 4] >> 0;
360 | buf[10] = chunks[ 5] >> 8; buf[11] = chunks[ 5] >> 0;
361 | buf[12] = chunks[ 6] >> 8; buf[13] = chunks[ 6] >> 0;
362 | buf[14] = chunks[ 7] >> 8; buf[15] = chunks[ 7] >> 0;
363 | buf[16] = chunks[ 8] >> 8; buf[17] = chunks[ 8] >> 0;
364 | buf[18] = chunks[ 9] >> 8; buf[19] = chunks[ 9] >> 0;
365 | buf[20] = chunks[10] >> 8;
366 | }
367 |
--------------------------------------------------------------------------------