Skip to content

Commit f09db89

Browse files
committed
added geohash extension
1 parent cd3bff8 commit f09db89

File tree

5 files changed

+266
-0
lines changed

5 files changed

+266
-0
lines changed

extensions/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ add_library(uuid_mod SHARED
9090
uuid.c
9191
deps/uuid4.c)
9292

93+
add_library(geohash_mod SHARED
94+
geohash.c
95+
deps/geohash.c)
96+
9397
target_link_libraries(sqlite_mod ${STATIC_LIB_TARGET})
9498
target_link_libraries(curl_mod ${STATIC_LIB_TARGET})
9599
target_link_libraries(redis_mod ${STATIC_LIB_TARGET})
@@ -102,6 +106,7 @@ target_link_libraries(leveldb_mod ${STATIC_LIB_TARGET})
102106
target_link_libraries(crypto_mod ${STATIC_LIB_TARGET})
103107
target_link_libraries(regex_mod ${STATIC_LIB_TARGET})
104108
target_link_libraries(uuid_mod ${STATIC_LIB_TARGET})
109+
target_link_libraries(geohash_mod ${STATIC_LIB_TARGET})
105110

106111
set_target_properties(sqlite_mod PROPERTIES PREFIX "")
107112
set_target_properties(curl_mod PROPERTIES PREFIX "")
@@ -115,6 +120,7 @@ set_target_properties(leveldb_mod PROPERTIES PREFIX "")
115120
set_target_properties(crypto_mod PROPERTIES PREFIX "")
116121
set_target_properties(regex_mod PROPERTIES PREFIX "")
117122
set_target_properties(uuid_mod PROPERTIES PREFIX "")
123+
set_target_properties(geohash_mod PROPERTIES PREFIX "")
118124

119125
if(MSVC)
120126
target_link_libraries(sqlite_mod PRIVATE SQLite::SQLite3)

extensions/deps/geohash.c

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/* This is a public domain geohash implementation written by WEI Zhicheng. */
2+
3+
#include <math.h>
4+
#include <float.h>
5+
#include <string.h>
6+
7+
#include "geohash.h"
8+
9+
/* BASE 32 encode table */
10+
static char base32en[] = {
11+
'0', '1', '2', '3', '4', '5', '6', '7',
12+
'8', '9', 'b', 'c', 'd', 'e', 'f', 'g',
13+
'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r',
14+
's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
15+
};
16+
17+
#define BASE32DE_FIRST '0'
18+
#define BASE32DE_LAST 'z'
19+
/* ASCII order for BASE 32 decode,from '0' to 'z', -1 in unused character */
20+
static signed char base32de[] = {
21+
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
22+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
23+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
24+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
25+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
26+
10, 11, 12, 13, 14, 15, 16, -1, 17, 18,
27+
-1, 19, 20, -1, 21, 22, 23, 24, 25, 26,
28+
27, 28, 29, 30, 31
29+
};
30+
31+
#define PRECISION .00000000001 /* XXX not validated */
32+
33+
static int
34+
max(int x, int y)
35+
{
36+
if (x < y)
37+
return y;
38+
return x;
39+
}
40+
41+
static double
42+
pround(double x, int precision)
43+
{
44+
double div = pow(10, precision);
45+
return round(x * div) / div;
46+
}
47+
48+
/* Quick and Dirty Floating-Point to Fractional Precision */
49+
static double
50+
fprec(double x)
51+
{
52+
int i;
53+
double int_part, frac_part;
54+
55+
frac_part = modf(x, &int_part);
56+
57+
/* check fractional is really close 0.0 or 1.0 */
58+
for (i = 0; fabs(frac_part - 0.0) > FLT_EPSILON &&
59+
fabs(frac_part - 1.0) > FLT_EPSILON; i++) {
60+
frac_part *= 10;
61+
frac_part = modf(frac_part, &int_part);
62+
}
63+
return pow(10, -i);
64+
}
65+
66+
int
67+
geohash_encode(double latitude, double longitude, char *hash, size_t len)
68+
{
69+
double lat[] = {-90.0, 90.0};
70+
double lon[] = {-180.0, 180.0};
71+
double mid;
72+
73+
double precision;
74+
75+
char mask[] = {16, 8, 4, 2, 1};
76+
77+
size_t i, n;
78+
int idx;
79+
80+
int even = 0;
81+
int right;
82+
83+
/* check input latitude/longitude is ok or invalid */
84+
if (latitude < lat[0] || latitude > lat[1] ||
85+
longitude < lon[0] || longitude > lon[1])
86+
return GEOHASH_INVALID;
87+
88+
precision = fmin(fprec(latitude), fprec(longitude));
89+
precision = fmax(PRECISION, precision);
90+
91+
/* save the last space for '\0' when len is not enough */
92+
len -= 1;
93+
for (i = 0; i < len; i++) {
94+
/* break when precision is enough for input latitude/longitude */
95+
if ((lat[1] - lat[0]) / 2.0 < precision &&
96+
(lon[1] - lon[0]) / 2.0 < precision)
97+
break;
98+
99+
for (n = idx = 0; n <= 4; n++) {
100+
if ((even = !even)) {
101+
mid = (lon[0] + lon[1]) / 2.0;
102+
right = (longitude > mid);
103+
if (right)
104+
idx |= mask[n];
105+
lon[!right] = mid;
106+
} else {
107+
mid = (lat[0] + lat[1]) / 2.0;
108+
right = (latitude > mid);
109+
if (right)
110+
idx |= mask[n];
111+
lat[!right] = mid;
112+
}
113+
}
114+
hash[i] = base32en[idx];
115+
}
116+
hash[i] = 0;
117+
return GEOHASH_OK;
118+
}
119+
120+
int
121+
geohash_decode(char *hash, double *latitude, double *longitude)
122+
{
123+
size_t len = strlen(hash);
124+
125+
double lat[] = {-90.0, 90.0};
126+
double lon[] = {-180.0, 180.0};
127+
double mid;
128+
129+
double lat_err, lon_err;
130+
131+
char mask[] = {16, 8, 4, 2, 1};
132+
133+
size_t i, n;
134+
int idx = 0;
135+
136+
int even = 0;
137+
138+
int right;
139+
140+
for (i = 0; i < len; i++) {
141+
if (hash[i] < BASE32DE_FIRST || hash[i] > BASE32DE_LAST ||
142+
(idx = base32de[hash[i] - BASE32DE_FIRST]) == -1)
143+
return GEOHASH_INVALID;
144+
145+
for (n = 0; n <= 4; n++) {
146+
if ((even = !even)) {
147+
mid = (lon[0] + lon[1]) / 2.0;
148+
right = (idx & mask[n]);
149+
lon[!right] = mid;
150+
} else {
151+
mid = (lat[0] + lat[1]) / 2.0;
152+
right = (idx & mask[n]);
153+
lat[!right] = mid;
154+
}
155+
}
156+
}
157+
lat_err = (lat[1] - lat[0]) / 2.0;
158+
*latitude = pround((lat[0] + lat[1]) / 2.0,
159+
max(1, (int)round(-log10(lat_err))) - 1);
160+
161+
lon_err = (lon[1] - lon[0]) / 2.0;
162+
*longitude = pround((lon[0] + lon[1]) / 2.0,
163+
max(1, (int)round(-log10(lon_err))) - 1);
164+
165+
return GEOHASH_OK;
166+
}

extensions/deps/geohash.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/* This is a public domain geohash implementation written by WEI Zhicheng. */
2+
3+
#ifndef __GEOHASH_H__
4+
#define __GEOHASH_H__
5+
6+
#include <stdlib.h>
7+
8+
enum {GEOHASH_OK = 0, GEOHASH_INVALID};
9+
10+
int
11+
geohash_encode(double latitude, double longitude, char *hash, size_t len);
12+
13+
int
14+
geohash_decode(char *hash, double *latitude, double *longitude);
15+
16+
#endif /* __GEOHASH_H__ */

extensions/geohash.c

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//
2+
// The Hook Programming Language
3+
// geohash.c
4+
//
5+
6+
#include "geohash.h"
7+
#include "deps/geohash.h"
8+
9+
static void encode_call(HkState *state, HkValue *args);
10+
static void decode_call(HkState *state, HkValue *args);
11+
12+
static void encode_call(HkState *state, HkValue *args)
13+
{
14+
hk_state_check_argument_number(state, args, 1);
15+
hk_return_if_not_ok(state);
16+
hk_state_check_argument_number(state, args, 2);
17+
hk_return_if_not_ok(state);
18+
double lat = hk_as_number(args[1]);
19+
double lon = hk_as_number(args[2]);
20+
char buf[16] = {0};
21+
if (geohash_encode(lat, lon, buf, sizeof(buf)) != GEOHASH_OK)
22+
{
23+
hk_state_push_nil(state);
24+
return;
25+
}
26+
HkString *str = hk_string_from_chars(-1, buf);
27+
hk_state_push_string(state, str);
28+
if (!hk_state_is_ok(state))
29+
hk_string_free(str);
30+
}
31+
32+
static void decode_call(HkState *state, HkValue *args)
33+
{
34+
hk_state_check_argument_string(state, args, 1);
35+
hk_return_if_not_ok(state);
36+
HkString *str = hk_as_string(args[1]);
37+
double lat = 0.0;
38+
double lon = 0.0;
39+
if (geohash_decode(str->chars, &lat, &lon) != GEOHASH_OK)
40+
{
41+
hk_state_push_nil(state);
42+
return;
43+
}
44+
HkArray *arr = hk_array_new_with_capacity(2);
45+
hk_array_inplace_add_element(arr, hk_number_value(lat));
46+
hk_array_inplace_add_element(arr, hk_number_value(lon));
47+
hk_state_push_array(state, arr);
48+
if (!hk_state_is_ok(state))
49+
hk_array_free(arr);
50+
}
51+
52+
HK_LOAD_MODULE_HANDLER(geohash)
53+
{
54+
hk_state_push_string_from_chars(state, -1, "geohash");
55+
hk_return_if_not_ok(state);
56+
hk_state_push_string_from_chars(state, -1, "encode");
57+
hk_return_if_not_ok(state);
58+
hk_state_push_new_native(state, "encode", 2, encode_call);
59+
hk_return_if_not_ok(state);
60+
hk_state_push_string_from_chars(state, -1, "decode");
61+
hk_return_if_not_ok(state);
62+
hk_state_push_new_native(state, "decode", 1, decode_call);
63+
hk_return_if_not_ok(state);
64+
hk_state_construct(state, 2);
65+
}

extensions/geohash.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//
2+
// The Hook Programming Language
3+
// geohash.h
4+
//
5+
6+
#ifndef GEOHASH_H
7+
#define GEOHASH_H
8+
9+
#include <hook.h>
10+
11+
HK_LOAD_MODULE_HANDLER(geohash);
12+
13+
#endif // GEOHASH_H

0 commit comments

Comments
 (0)