├── .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 | --------------------------------------------------------------------------------