forked from simplegeo/libgeohash
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathpylibgeohash.c
140 lines (126 loc) · 5.05 KB
/
pylibgeohash.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/*
* Copyright (c) 2020 Bauman
* under the same BSD license as the library itself
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "geohash.h"
static PyObject*
to_hash(PyObject* self, PyObject* args)
{
float longitude, latitude;
int precision;
if (!PyArg_ParseTuple(args, "ffi", &latitude, &longitude, &precision)){
PyErr_SetString(PyExc_TypeError, "parameters are float lat, float lon, int precision");
return NULL;
}
if (latitude < MIN_LAT || latitude > MAX_LAT || longitude > MAX_LONG || longitude < MIN_LONG){
PyErr_SetString(PyExc_ValueError, "latitude must be within -90 to 90 and longitude must be within -180 to 180");
return NULL;
}
if (precision < MIN_PRECISION || precision > MAX_PRECISION){
PyErr_SetString(PyExc_ValueError, "precision must be between 1 and 12");
return NULL;
}
char* hash = geohash_encode(latitude, longitude, precision);
if (!hash){
PyErr_SetString(PyExc_ArithmeticError, "unable to generate hash");
return NULL;
}
size_t hash_size = strlen(hash);
if (hash_size != (size_t)precision){
precision = 0; // set precision to zero to protect from returning bad data
}
PyObject * result = Py_BuildValue("s#", hash, precision);
free(hash);
return result;
}
static PyObject*
from_hash(PyObject* self, PyObject* args)
{
const char * hash;
Py_ssize_t hashlen, maxlen=MAX_PRECISION, minlen=MIN_PRECISION;
if (!PyArg_ParseTuple(args, "s#", &hash, &hashlen)){
PyErr_SetString(PyExc_TypeError, "parameter must be a hash string");
return NULL;
}
if (hashlen < minlen || hashlen > maxlen) {
PyErr_SetString(PyExc_ValueError, "hash string must be between 1 and 12 characters long");
return NULL;
}
GeoCoord coord = geohash_decode((char *) hash);
PyObject * result = Py_BuildValue(
"{s:f,s:f,s:f,s:f,s:f,s:f,s:{s:f,s:f}}",
"latitude", coord.latitude,
"longitude", coord.longitude,
"north", coord.north,
"east", coord.east,
"south", coord.south,
"west", coord.west,
"dimension",
"height", coord.dimension.height,
"width", coord.dimension.width
);
return result;
}
static PyObject*
neighbors(PyObject* self, PyObject* args) {
const char *hash;
Py_ssize_t hashlen, maxlen=MAX_PRECISION, minlen=MIN_PRECISION;
int i = 0;
// INPUT SANITY CHECKING
if (!PyArg_ParseTuple(args, "s#", &hash, &hashlen)) {
PyErr_SetString(PyExc_TypeError, "parameter must be a hash string");
return NULL;
}
if (hashlen < minlen || hashlen > maxlen) {
PyErr_SetString(PyExc_ValueError, "hash string must be between 1 and 12 characters long");
return NULL;
}
// THE CALL TO COMPUTE
char** neighbors = geohash_neighbors(hash);
// COMPLETED THE CALL TO COMPUTE
// OUTPUT SANITY CHECKING
if (!neighbors) {
PyErr_SetString(PyExc_ArithmeticError, "unable to compute neighbors");
return NULL;
}
for (i=0; i<8; i++){
if(!neighbors[i]){
PyErr_SetString(PyExc_ArithmeticError, "problem computing at least one of the neighbors");
geohash_free_neighbors(neighbors);
return NULL;
}
}
// LOOKS SANE - PASSING BACK TO PYTHON
PyObject * result = Py_BuildValue(
"(s,s,s,s,s,s,s,s)",
neighbors[0], neighbors[1], neighbors[2], neighbors[3],
neighbors[4], neighbors[5], neighbors[6], neighbors[7]
);
geohash_free_neighbors(neighbors);
return result;
}
static PyMethodDef geohashHelperMethods[] =
{
{"to_hash", to_hash, METH_VARARGS, "Converts latitude / longitude to a string hash \nto_hash(float lat, float lon, int precision)\n"},
{"geohash_encode", to_hash, METH_VARARGS, "Converts latitude / longitude to a string hash \nto_hash(float lat, float lon, int precision)\n"},
{"from_hash", from_hash, METH_VARARGS, "Converts a string hash to a latitude / longitude \nfrom_hash(string hash)\n"},
{"geohash_decode", from_hash, METH_VARARGS, "Converts a string hash to a latitude / longitude \nfrom_hash(string hash)\n"},
{"neighbors", neighbors, METH_VARARGS, "calculates the 8 neighboring boxes\nBox is as follows\n\t7 0 1\n\t6 x 2\n\t5 4 3\n"},
{"geohash_neighbors", neighbors, METH_VARARGS, "calculates the 8 neighboring boxes\nBox is as follows\n\t7 0 1\n\t6 x 2\n\t5 4 3\n"},
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef pygeohashdef =
{
PyModuleDef_HEAD_INIT,
"", /* name of module */
"converts from lon/lat to hash or hash to lon/lat\n", /* module documentation, may be NULL */
-1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */
geohashHelperMethods
};
PyMODINIT_FUNC
PyInit_pylibgeohash(void)
{
return PyModule_Create(&pygeohashdef);
}