├── .gitignore ├── PKG-INFO ├── README ├── debian ├── changelog ├── compat ├── control ├── copyright ├── dirs ├── docs ├── pycompat ├── rules └── source │ └── format ├── geohash.py ├── jpgrid.py ├── jpiarea.py ├── quadtree.py ├── setup.py ├── src ├── Makefile ├── geohash.cpp ├── geohash.h └── geohash_sample.c └── test ├── test_decode.py └── test_encode.py /.gitignore: -------------------------------------------------------------------------------- 1 | /.pc 2 | /build 3 | /debian/python-geohash*debhelper* 4 | /debian/*.substvars 5 | /debian/*stamp* 6 | /debian/files 7 | /debian/python-geohash/ -------------------------------------------------------------------------------- /PKG-INFO: -------------------------------------------------------------------------------- 1 | Metadata-Version: 1.0 2 | Name: python-geohash 3 | Version: 0.7 4 | Summary: Fast, accurate python geohashing library 5 | Home-page: http://code.google.com/p/python-geohash/ 6 | Author: Hiroaki Kawai 7 | Author-email: UNKNOWN 8 | License: UNKNOWN 9 | Description: UNKNOWN 10 | Platform: UNKNOWN 11 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | * python-geohash 2 | python-geohash is a fast, accurate python geohashing library. 3 | 4 | python-geohash 0.3 can create C extension. If you want to use python-geohash 5 | without C extension, simply copy geohash.py into your system. geohash.py will 6 | work fine without C extension. 7 | 8 | ** LICENSE 9 | Code is licensed under Apache License 2.0, MIT Licence and NEW BSD License. 10 | You can choose one of the licences above. 11 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | python-geohash (0.7-sg6) lucid; urgency=low 2 | 3 | * UNRELEASED 4 | 5 | -- SimpleGeo Nerds Tue, 19 Oct 2010 18:08:53 +0000 6 | 7 | python-geohash (0.7-sg0) unstable; urgency=low 8 | 9 | * New upstream version. 10 | 11 | -- Wade Simmons Tue, 19 Oct 2010 12:07:13 -0600 12 | 13 | python-geohash (0.6-sg4) lucid; urgency=low 14 | 15 | [ Wade Simmons ] 16 | * disable compilation and use of the C library to fix geohash.encode 17 | We can enable it again once http://code.google.com/p/python- 18 | geohash/issues/detail?id=5 is fixed (although I am fine with leaving 19 | it disabled, as it has been very buggy) 20 | 21 | [ SimpleGeo Nerds ] 22 | 23 | -- SimpleGeo Nerds Thu, 26 Aug 2010 20:02:11 +0000 24 | 25 | python-geohash (0.6-sg3) lucid; urgency=low 26 | 27 | [ Ian Eure ] 28 | * Build-depend on python-dev, otherwise the C extension can’t build. 29 | 30 | [ SimpleGeo Nerds ] 31 | 32 | -- SimpleGeo Nerds Tue, 22 Jun 2010 17:12:07 +0000 33 | 34 | python-geohash (0.6-sg2) lucid; urgency=low 35 | 36 | * UNRELEASED 37 | 38 | -- SimpleGeo Nerds Tue, 22 Jun 2010 17:07:32 +0000 39 | 40 | python-geohash (0.6-sg0) unstable; urgency=low 41 | 42 | * New upstream version. 43 | 44 | -- Ian Eure Tue, 22 Jun 2010 09:55:36 -0700 45 | 46 | python-geohash (0.2-1) unstable; urgency=low 47 | 48 | * Debianized. 49 | 50 | -- Ian Eure Tue, 18 May 2010 14:49:17 -0700 51 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 7 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: python-geohash 2 | Section: python 3 | Priority: extra 4 | Maintainer: SimpleGeo Nerds 5 | Uploaders: Ian Eure 6 | Build-Depends: debhelper (>= 7), cdbs (>= 0.4.59), python (>= 2.6), python-central (>= 0.6.11), python-dev 7 | Standards-Version: 3.8.4 8 | Homepage: http://github.com/simplegeo/python-geohash 9 | XS-Python-Version: 2.6 10 | 11 | Package: python-geohash 12 | Architecture: any 13 | XB-Python-Version: ${python:Versions} 14 | Depends: ${misc:Depends}, ${python:Depends}, ${shlibs:Depends} 15 | Description: Fast, accurate python geohashing library. 16 | This module provides geohash encoding and decoding functionality. It 17 | additionally supports the other 3 grid codes of quadtree, jpgrid (JIS 18 | X 0410) and jpiarea. 19 | 20 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Copyright (C) 2009 Hiroaki Kawai 2 | -------------------------------------------------------------------------------- /debian/dirs: -------------------------------------------------------------------------------- 1 | usr/bin 2 | usr/sbin 3 | -------------------------------------------------------------------------------- /debian/docs: -------------------------------------------------------------------------------- 1 | README 2 | -------------------------------------------------------------------------------- /debian/pycompat: -------------------------------------------------------------------------------- 1 | 2.6 2 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | DEB_PYTHON_SYSTEM=pycentral 4 | 5 | include /usr/share/cdbs/1/rules/debhelper.mk 6 | include /usr/share/cdbs/1/class/python-distutils.mk 7 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) -------------------------------------------------------------------------------- /geohash.py: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | """ 3 | Copyright (C) 2009 Hiroaki Kawai 4 | """ 5 | try: 6 | import _geohash 7 | except ImportError,e: 8 | _geohash = None 9 | 10 | __all__ = ['encode','decode','decode_exactly','bbox', 'neighbors', 'expand'] 11 | 12 | _base32 = '0123456789bcdefghjkmnpqrstuvwxyz' 13 | _base32_map = {} 14 | for i in range(len(_base32)): 15 | _base32_map[_base32[i]] = i 16 | del i 17 | 18 | # def _binstr(b, length=32): 19 | # t = [] 20 | # while length>0: 21 | # if b&1: 22 | # t.append('1') 23 | # else: 24 | # t.append('0') 25 | # b=b>>1 26 | # length-=1 27 | # 28 | # t.reverse() 29 | # return ''.join(t) 30 | 31 | def _encode_i2c(lat,lon,lat_length,lon_length): 32 | precision=(lat_length+lon_length)/5 33 | if lat_length < lon_length: 34 | a = lon 35 | b = lat 36 | else: 37 | a = lat 38 | b = lon 39 | 40 | boost = (0,1,4,5,16,17,20,21) 41 | ret = '' 42 | for i in range(precision): 43 | ret+=_base32[(boost[a&7]+(boost[b&3]<<1))&0x1F] 44 | t = a>>3 45 | a = b>>2 46 | b = t 47 | 48 | return ret[::-1] 49 | 50 | def encode(latitude, longitude, precision=12): 51 | if latitude >= 90.0 or latitude < -90.0: 52 | raise Exception("invalid latitude.") 53 | while longitude < -180.0: 54 | longitude += 360.0 55 | while longitude >= 180.0: 56 | longitude -= 360.0 57 | 58 | if _geohash: 59 | basecode=_geohash.encode(latitude,longitude) 60 | if len(basecode)>precision: 61 | return basecode[0:precision] 62 | return basecode+'0'*(precision-len(basecode)) 63 | 64 | lat = latitude/180.0 65 | lon = longitude/360.0 66 | 67 | xprecision=precision+1 68 | lat_length=lon_length=xprecision*5/2 69 | if xprecision%2==1: 70 | lon_length+=1 71 | 72 | if lat>0: 73 | lat = int((1<0: 78 | lon = int((1<>2)&4 96 | lat += (t>>2)&2 97 | lon += (t>>1)&2 98 | lat += (t>>1)&1 99 | lon += t&1 100 | lon_length+=3 101 | lat_length+=2 102 | else: 103 | lon = lon<<2 104 | lat = lat<<3 105 | lat += (t>>2)&4 106 | lon += (t>>2)&2 107 | lat += (t>>1)&2 108 | lon += (t>>1)&1 109 | lat += t&1 110 | lon_length+=2 111 | lat_length+=3 112 | 113 | bit_length+=5 114 | 115 | return (lat,lon,lat_length,lon_length) 116 | 117 | def decode(hashcode, delta=False): 118 | ''' 119 | decode a hashcode and get center coordinate, and distance between center and outer border 120 | ''' 121 | if _geohash: 122 | (lat,lon,lat_bits,lon_bits) = _geohash.decode(hashcode) 123 | latitude_delta = 180.0/(2<> lat_length: 191 | for tlon in (lon-1, lon, lon+1): 192 | ret.append(_encode_i2c(tlat,tlon,lat_length,lon_length)) 193 | 194 | tlat = lat-1 195 | if tlat >= 0: 196 | for tlon in (lon-1, lon, lon+1): 197 | ret.append(_encode_i2c(tlat,tlon,lat_length,lon_length)) 198 | 199 | return ret 200 | 201 | def expand(hashcode): 202 | ret = neighbors(hashcode) 203 | ret.append(hashcode) 204 | return ret 205 | -------------------------------------------------------------------------------- /jpgrid.py: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | # Coder for Japanese grid square code. (JIS C 6304 / JIS X 0410) 3 | # 行政管理庁告示第143号 http://www.stat.go.jp/data/mesh/ 4 | 5 | def _encode_i2c(lat, lon, base1): 6 | t=[] 7 | while base1>80: 8 | t.append(1 + (lat&1)*2 + (lon&1)) 9 | lat = lat>>1 10 | lon = lon>>1 11 | base1 = base1>>1 12 | 13 | if base1==80: 14 | t.append(lon%10) 15 | t.append(lat%10) 16 | lat = lat/10 17 | lon = lon/10 18 | base1 = base1/10 19 | elif base1==16: # Uni5 20 | t.append(1 + (lat&1)*2 + (lon&1)) 21 | lat = lat>>1 22 | lon = lon>>1 23 | base1 = base1>>1 24 | elif base1==40: # Uni2 25 | t.append(5) 26 | t.append(lon%5*2) 27 | t.append(lat%5*2) 28 | lat = lat/5 29 | lon = lon/5 30 | base1 = base1/5 31 | 32 | if base1==8: 33 | t.append(lon%8) 34 | t.append(lat%8) 35 | lat = lat>>3 36 | lon = lon>>3 37 | base1 = base1>>3 38 | 39 | t.append(lon) 40 | t.append(lat) 41 | t.reverse() 42 | return ''.join([str(i) for i in t]) 43 | 44 | def encode(latitude, longitude, base1=80): 45 | return _encode_i2c(int(latitude*base1*1.5), int((longitude-100)*base1), base1) 46 | 47 | #def _encode_i2c(lat, lon, base1): 48 | def _decode_c2i(gridcode): 49 | base1 = 1 50 | lat = lon = 0 51 | codelen = len(gridcode) 52 | if codelen>0: 53 | lat = int(gridcode[0:2]) 54 | lon = int(gridcode[2:4]) 55 | 56 | if codelen>4: 57 | lat = (lat<<3) + int(gridcode[4:5]) 58 | lon = (lon<<3) + int(gridcode[5:6]) 59 | base1 = base1<<3 60 | 61 | if codelen>6: 62 | if codelen==7: 63 | i = int(gridcode[6:7])-1 64 | lat = (lat<<1) + i/2 65 | lon = (lon<<1) + i%2 66 | base1 = base1<<1 67 | else: 68 | lat = lat*10 + int(gridcode[6:7]) 69 | lon = lon*10 + int(gridcode[7:8]) 70 | base1 = base1*10 71 | 72 | if codelen>8: 73 | if gridcode[8:]=='5': 74 | lat = lat>>1 75 | lon = lon>>1 76 | base1 = base1>>1 77 | else: 78 | for i in gridcode[8:]: 79 | i = int(i)-1 80 | lat = (lat<<1) + i/2 81 | lon = (lon<<1) + i%2 82 | base1 = base1<<1 83 | 84 | return (lat, lon, base1) 85 | 86 | def decode_sw(gridcode, delta=False): 87 | (lat, lon, base1) = _decode_c2i(gridcode) 88 | 89 | lat = lat/(base1*1.5) 90 | lon = lon/float(base1) + 100.0 91 | 92 | if delta: 93 | return (lat, lon, 1.0/(base1*1.5), 1.0/base1) 94 | else: 95 | return (lat, lon) 96 | 97 | def decode(gridcode): 98 | (lat, lon, base1) = _decode_c2i(gridcode) 99 | 100 | lat = (lat<<1) + 1 101 | lon = (lon<<1) + 1 102 | base1 = base1<<1 103 | return (lat/(base1*1.5), lon/float(base1) + 100.0) 104 | 105 | def bbox(gridcode): 106 | (a,b,c,d) = decode_sw(gridcode, True) 107 | return {'w':a, 's':b, 'n':b+d, 'e':a+c} 108 | 109 | 110 | ## short-cut methods 111 | def encodeLv1(lat, lon): 112 | return encode(lat,lon,1) 113 | 114 | def encodeLv2(lat, lon): 115 | return encode(lat,lon,8) 116 | 117 | def encodeLv3(lat, lon): 118 | return encode(lat,lon,80) 119 | 120 | def encodeBase(lat,lon): 121 | return encodeLv3(lat,lon) 122 | 123 | def encodeHalf(lat,lon): 124 | return encode(lat,lon,160) 125 | 126 | def encodeQuarter(lat,lon): 127 | return encode(lat,lon,320) 128 | 129 | def encodeEighth(lat,lon): 130 | return encode(lat,lon,640) 131 | 132 | def encodeUni10(lat,lon): 133 | return encodeLv2(lat,lon) 134 | 135 | def encodeUni5(lat, lon): 136 | return encode(lat,lon,16) 137 | 138 | def encodeUni2(lat, lon): 139 | return encode(lat,lon,40) 140 | 141 | 142 | def neighbors(gridcode): 143 | (lat,lon,base1)=_decode_c2i(gridcode) 144 | ret = [] 145 | for i in ((0,-1),(0,1),(1,-1),(1,0),(1,1),(-1,-1),(-1,0),(-1,1)): 146 | tlat=lat+i[0] 147 | tlon=lon+i[1] 148 | if tlat<0 or tlat>(90*base1): 149 | continue 150 | if tlon<0 or tlon>(100*base1): 151 | continue 152 | 153 | ret.append(_encode_i2c(tlat,tlon,base1)) 154 | 155 | return ret 156 | 157 | def expand(gridcode): 158 | ret = neighbors(gridcode) 159 | ret.append(gridcode) 160 | return ret 161 | -------------------------------------------------------------------------------- /jpiarea.py: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | # Coder for Japanese iarea grid code. 3 | # NTT DoCoMo's Open iArea in Japan use a gridcode which is very similar to 4 | # JIS X 0410, but absolutely different in detail. 5 | 6 | def _encode_i2c(lat,lon,basebits): 7 | t=[] 8 | for i in range(basebits-3): 9 | t.append((lat&1)*2 + (lon&1)) 10 | lat = lat>>1 11 | lon = lon>>1 12 | 13 | if basebits>=3: 14 | t.append(lon&7) 15 | t.append(lat&7) 16 | lat = lat>>3 17 | lon = lon>>3 18 | 19 | t.append(lon) 20 | t.append(lat) 21 | t.reverse() 22 | return ''.join([str(i) for i in t]) 23 | 24 | def encode(lat, lon): 25 | if lat<7 or lon<100: 26 | raise Exception('Unsupported location') 27 | 28 | basebits = 8 29 | return _encode_i2c(int(lat * (1<6: 36 | for i in gridcode[6:]: 37 | lat = (lat<<1) + int(i)/2 38 | lon = (lon<<1) + int(i)%2 39 | base = base<<1 40 | basebits += 1 41 | 42 | if len(gridcode)>4: 43 | lat = int(gridcode[4:5])*base + lat 44 | lon = int(gridcode[5:6])*base + lon 45 | base = base<<3 46 | basebits += 3 47 | 48 | lat = int(gridcode[0:2])*base + lat 49 | lon = int(gridcode[2:4])*base + lon 50 | 51 | return (lat, lon, basebits) 52 | 53 | def decode_sw(gridcode, delta=False): 54 | lat, lon, basebits = _decode_c2i(gridcode) 55 | 56 | if delta: 57 | return (float(lat)/(1.5*(1<(90<(100< 4 | """ 5 | 6 | def _encode_i2c(lat,lon,bitlength): 7 | digits='0123' 8 | r = '' 9 | while bitlength>0: 10 | r += digits[((lat&1)<<1)+(lon&1)] 11 | lat = lat>>1 12 | lon = lon>>1 13 | bitlength -= 1 14 | 15 | return r[::-1] 16 | 17 | def _decode_c2i(treecode): 18 | lat = 0 19 | lon = 0 20 | for i in treecode: 21 | b = ord(i)-48 22 | lat = (lat<<1)+b/2 23 | lon = (lon<<1)+b%2 24 | 25 | return (lat,lon,len(treecode)) 26 | 27 | def encode(lat,lon,precision=12): 28 | b = 1<>bitlength: 55 | for tlon in (lon-1, lon, lon+1): 56 | r.append(_encode_i2c(tlat, tlon, bitlength)) 57 | 58 | tlat = lat-1 59 | if tlat>=0: 60 | for tlon in (lon-1, lon, lon+1): 61 | r.append(_encode_i2c(tlat, tlon, bitlength)) 62 | 63 | return r 64 | 65 | def expand(treecode): 66 | r = neighbors(treecode) 67 | r.append(treecode) 68 | return r 69 | 70 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup, Extension 2 | 3 | c1=Extension('_geohash', 4 | sources=['src/geohash.cpp',], 5 | define_macros = [('PYTHON_MODULE',1),]) 6 | 7 | setup(name='python-geohash', 8 | version='0.7', 9 | description='Fast, accurate python geohashing library', 10 | author='Hiroaki Kawai', 11 | url='http://code.google.com/p/python-geohash/', 12 | py_modules=['geohash','quadtree','jpgrid','jpiarea'], 13 | ext_modules = [c1] 14 | ) 15 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CXX = g++ 3 | CFLAGS = -O3 4 | CXXFLAGS = 5 | TARGET = sample 6 | OBJS = geohash_sample.o geohash.o 7 | 8 | all: $(TARGET) 9 | $(TARGET): $(OBJS) 10 | $(CXX) -o $@ $(OBJS) 11 | clean: 12 | -rm -f $(TARGET) $(OBJS) 13 | .c.o: 14 | $(CC) $(CFLAGS) -c $< 15 | .cpp.o: 16 | $(CXX) $(CXXFLAGS) -c $< 17 | -------------------------------------------------------------------------------- /src/geohash.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "geohash.h" 5 | 6 | #if defined(_MSC_VER) && (_MSC_VER <= 1500) 7 | typedef unsigned __int8 uint8_t; 8 | typedef unsigned __int16 uint16_t; 9 | typedef unsigned __int64 uint64_t; 10 | #define UINT64_C(C) ((uint64_t) C ## ULL) 11 | #else 12 | #define __STDC_CONSTANT_MACROS 1 13 | #include 14 | #endif 15 | 16 | #ifdef _MSC_VER 17 | // http://msdn.microsoft.com/en-us/library/b0084kay(VS.80).aspx 18 | #if defined(_M_IX86) || defined(_M_IA64) || defined(_M_X64) 19 | # define __LITTLE_ENDIAN__ 20 | #endif 21 | #else /* _MSC_VER */ 22 | #include 23 | #if !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__) /* MacOS X style */ 24 | #ifdef __BYTE_ORDER /* Linux style */ 25 | # if __BYTE_ORDER == __LITTLE_ENDIAN 26 | # define __LITTLE_ENDIAN__ 27 | # endif 28 | # if __BYTE_ORDER == __BIG_ENDIAN 29 | # define __BIG_ENDIAN__ 30 | # endif 31 | #endif 32 | #ifdef BYTE_ORDER /* MinGW style */ 33 | # if BYTE_ORDER == LITTLE_ENDIAN 34 | # define __LITTLE_ENDIAN__ 35 | # endif 36 | # if BYTE_ORDER == BIG_ENDIAN 37 | # define __BIG_ENDIAN__ 38 | # endif 39 | #endif 40 | #endif 41 | #endif /* _MSC_VER */ 42 | 43 | #if !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__) 44 | /* I don't have __PDP_ENDIAN machine. Please let me know if you have one. */ 45 | #define __UNSUPPORTED_ENDIAN__ 46 | #endif 47 | 48 | #ifdef __LITTLE_ENDIAN__ 49 | #define B7 7 50 | #define B6 6 51 | #define B5 5 52 | #define B4 4 53 | #define B3 3 54 | #define B2 2 55 | #define B1 1 56 | #define B0 0 57 | #endif 58 | 59 | #ifdef __BIG_ENDIAN__ 60 | #define B7 0 61 | #define B6 1 62 | #define B5 2 63 | #define B4 3 64 | #define B3 4 65 | #define B2 5 66 | #define B1 6 67 | #define B0 7 68 | #endif 69 | 70 | 71 | #ifdef PYTHON_MODULE 72 | #include 73 | static PyObject *py_geohash_encode(PyObject *self, PyObject *args) { 74 | double latitude; 75 | double longitude; 76 | char hashcode[28]; 77 | int ret = GEOHASH_OK; 78 | 79 | if(!PyArg_ParseTuple(args, "dd", &latitude, &longitude)) return NULL; 80 | 81 | if((ret=geohash_encode(latitude,longitude,hashcode,28))!=GEOHASH_OK){ 82 | if(ret==GEOHASH_NOTSUPPORTED) PyErr_SetString(PyExc_EnvironmentError, "unknown endian"); 83 | return NULL; 84 | } 85 | return Py_BuildValue("s",hashcode); 86 | } 87 | static PyObject *py_geohash_decode(PyObject *self, PyObject *args) { 88 | double latitude; 89 | double longitude; 90 | char *hashcode; 91 | int codelen=0; 92 | int ret = GEOHASH_OK; 93 | 94 | if(!PyArg_ParseTuple(args, "s", &hashcode)) return NULL; 95 | 96 | codelen = strlen(hashcode); 97 | if((ret=geohash_decode(hashcode,codelen,&latitude,&longitude))!=GEOHASH_OK){ 98 | PyErr_SetString(PyExc_ValueError,"geohash code is [0123456789bcdefghjkmnpqrstuvwxyz]+"); 99 | return NULL; 100 | } 101 | return Py_BuildValue("(ddii)",latitude,longitude, codelen/2*5+codelen%2*2, codelen/2*5+codelen%2*3); 102 | } 103 | 104 | static PyObject *py_geohash_neighbors(PyObject *self, PyObject *args) { 105 | static const unsigned char mapA[128] = { 106 | '@', '|', '|', '|', '|', '|', '|', '|', 107 | '|', '|', '|', '|', '|', '|', '|', '|', 108 | '|', '|', '|', '|', '|', '|', '|', '|', 109 | '|', '|', '|', '|', '|', '|', '|', '|', 110 | '|', '|', '|', '|', '|', '|', '|', '|', 111 | '|', '|', '|', '|', '|', '|', '|', '|', 112 | 0, 1, 2, 3, 4, 5, 6, 7, 113 | 8, 9, '|', '|', '|', '|', '|', '|', 114 | '|', '|', 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 115 | 0x10, '|', 0x11, 0x12, '|', 0x13, 0x14, '|', 116 | 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 117 | 0x1D, 0x1E, 0x1F, '|', '|', '|', '|', '|', 118 | '|', '|', 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 119 | 0x10, '|', 0x11, 0x12, '|', 0x13, 0x14, '|', 120 | 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 121 | 0x1D, 0x1E, 0x1F, '|', '|', '|', '|', '|', 122 | }; 123 | static const char rmap[33]="0123456789bcdefghjkmnpqrstuvwxyz"; 124 | uint64_t lat, lon; 125 | char *hashcode; 126 | if(!PyArg_ParseTuple(args, "s", &hashcode)) return NULL; 127 | 128 | int length = strlen(hashcode); 129 | if(length>24){ length=24; } // round if the hashcode is too long (over 64bit) 130 | lat=lon=0; 131 | int cshift=0; 132 | while(cshift>2) | ((o1&0x04)>>1) | (o1&0x01); 141 | lat = (lat<<2) | ((o1&0x08)>>2) | ((o1&0x02)>>1); 142 | }else{ 143 | lon = (lon<<2) | ((o1&0x08)>>2) | ((o1&0x02)>>1); 144 | lat = (lat<<3) | ((o1&0x10)>>2) | ((o1&0x04)>>1) | (o1&0x01); 145 | } 146 | cshift++; 147 | } 148 | 149 | char* buffer = (char*)malloc(8*(length+1)*sizeof(char)); 150 | if(buffer==NULL){ 151 | PyErr_NoMemory(); 152 | return NULL; 153 | } 154 | int al=-1; 155 | int au=2; 156 | if(lat==0){ 157 | al=0; au=2; 158 | }else if(lat+1==(UINT64_C(1)<<(length/2*5+length%2*2))){ 159 | al=-1; au=1; 160 | } 161 | int blen=length+1; // block length 162 | int aoff=0; 163 | int a,o; 164 | for(a=al;a=0){ 172 | unsigned char z; 173 | if(cpos%2==0){ 174 | z = ((to&4)<<2)|((to&2)<<1)|(to&1)|((ta&2)<<2)|((ta&1)<<1); 175 | buffer[blen*aoff+cpos]=rmap[z]; 176 | ta=ta>>2; 177 | to=to>>3; 178 | }else{ 179 | z = ((ta&4)<<2)|((ta&2)<<1)|(ta&1)|((to&2)<<2)|((to&1)<<1); 180 | buffer[blen*aoff+cpos]=rmap[z]; 181 | ta=ta>>3; 182 | to=to>>2; 183 | } 184 | cpos--; 185 | } 186 | aoff++; 187 | } 188 | } 189 | PyObject *ret; 190 | if(lat==0){ 191 | ret= Py_BuildValue("[sssss]",&buffer[0],&buffer[blen],&buffer[blen*2],&buffer[blen*3],&buffer[blen*4]); 192 | }else if(lat+1==(UINT64_C(1)<<(cshift/2*5+cshift%2*2))){ 193 | ret= Py_BuildValue("[sssss]",&buffer[0],&buffer[blen],&buffer[blen*2],&buffer[blen*3],&buffer[blen*4]); 194 | }else{ 195 | ret= Py_BuildValue("[ssssssss]",&buffer[0],&buffer[blen],&buffer[blen*2],&buffer[blen*3], 196 | &buffer[blen*4],&buffer[blen*5],&buffer[blen*6],&buffer[blen*7]); 197 | } 198 | free(buffer); 199 | return ret; 200 | } 201 | 202 | static PyMethodDef GeohashMethods[] = { 203 | {"encode", py_geohash_encode, METH_VARARGS, "geohash encoding."}, 204 | {"decode", py_geohash_decode, METH_VARARGS, "geohash decoding."}, 205 | {"neighbors", py_geohash_neighbors, METH_VARARGS, "geohash neighbor codes",}, 206 | {NULL, NULL, 0, NULL} 207 | }; 208 | 209 | PyMODINIT_FUNC init_geohash(void){ 210 | (void)Py_InitModule("_geohash", GeohashMethods); 211 | }; 212 | #endif /* PYTHON_MODULE */ 213 | 214 | static inline uint64_t interleave(uint8_t upper, uint8_t lower){ 215 | static const uint64_t map[256] = { 216 | UINT64_C(0x0000), UINT64_C(0x0001), UINT64_C(0x0004), UINT64_C(0x0005), UINT64_C(0x0010), UINT64_C(0x0011), 217 | UINT64_C(0x0014), UINT64_C(0x0015), UINT64_C(0x0040), UINT64_C(0x0041), UINT64_C(0x0044), UINT64_C(0x0045), 218 | UINT64_C(0x0050), UINT64_C(0x0051), UINT64_C(0x0054), UINT64_C(0x0055), UINT64_C(0x0100), UINT64_C(0x0101), 219 | UINT64_C(0x0104), UINT64_C(0x0105), UINT64_C(0x0110), UINT64_C(0x0111), UINT64_C(0x0114), UINT64_C(0x0115), 220 | UINT64_C(0x0140), UINT64_C(0x0141), UINT64_C(0x0144), UINT64_C(0x0145), UINT64_C(0x0150), UINT64_C(0x0151), 221 | UINT64_C(0x0154), UINT64_C(0x0155), UINT64_C(0x0400), UINT64_C(0x0401), UINT64_C(0x0404), UINT64_C(0x0405), 222 | UINT64_C(0x0410), UINT64_C(0x0411), UINT64_C(0x0414), UINT64_C(0x0415), UINT64_C(0x0440), UINT64_C(0x0441), 223 | UINT64_C(0x0444), UINT64_C(0x0445), UINT64_C(0x0450), UINT64_C(0x0451), UINT64_C(0x0454), UINT64_C(0x0455), 224 | UINT64_C(0x0500), UINT64_C(0x0501), UINT64_C(0x0504), UINT64_C(0x0505), UINT64_C(0x0510), UINT64_C(0x0511), 225 | UINT64_C(0x0514), UINT64_C(0x0515), UINT64_C(0x0540), UINT64_C(0x0541), UINT64_C(0x0544), UINT64_C(0x0545), 226 | UINT64_C(0x0550), UINT64_C(0x0551), UINT64_C(0x0554), UINT64_C(0x0555), UINT64_C(0x1000), UINT64_C(0x1001), 227 | UINT64_C(0x1004), UINT64_C(0x1005), UINT64_C(0x1010), UINT64_C(0x1011), UINT64_C(0x1014), UINT64_C(0x1015), 228 | UINT64_C(0x1040), UINT64_C(0x1041), UINT64_C(0x1044), UINT64_C(0x1045), UINT64_C(0x1050), UINT64_C(0x1051), 229 | UINT64_C(0x1054), UINT64_C(0x1055), UINT64_C(0x1100), UINT64_C(0x1101), UINT64_C(0x1104), UINT64_C(0x1105), 230 | UINT64_C(0x1110), UINT64_C(0x1111), UINT64_C(0x1114), UINT64_C(0x1115), UINT64_C(0x1140), UINT64_C(0x1141), 231 | UINT64_C(0x1144), UINT64_C(0x1145), UINT64_C(0x1150), UINT64_C(0x1151), UINT64_C(0x1154), UINT64_C(0x1155), 232 | UINT64_C(0x1400), UINT64_C(0x1401), UINT64_C(0x1404), UINT64_C(0x1405), UINT64_C(0x1410), UINT64_C(0x1411), 233 | UINT64_C(0x1414), UINT64_C(0x1415), UINT64_C(0x1440), UINT64_C(0x1441), UINT64_C(0x1444), UINT64_C(0x1445), 234 | UINT64_C(0x1450), UINT64_C(0x1451), UINT64_C(0x1454), UINT64_C(0x1455), UINT64_C(0x1500), UINT64_C(0x1501), 235 | UINT64_C(0x1504), UINT64_C(0x1505), UINT64_C(0x1510), UINT64_C(0x1511), UINT64_C(0x1514), UINT64_C(0x1515), 236 | UINT64_C(0x1540), UINT64_C(0x1541), UINT64_C(0x1544), UINT64_C(0x1545), UINT64_C(0x1550), UINT64_C(0x1551), 237 | UINT64_C(0x1554), UINT64_C(0x1555), UINT64_C(0x4000), UINT64_C(0x4001), UINT64_C(0x4004), UINT64_C(0x4005), 238 | UINT64_C(0x4010), UINT64_C(0x4011), UINT64_C(0x4014), UINT64_C(0x4015), UINT64_C(0x4040), UINT64_C(0x4041), 239 | UINT64_C(0x4044), UINT64_C(0x4045), UINT64_C(0x4050), UINT64_C(0x4051), UINT64_C(0x4054), UINT64_C(0x4055), 240 | UINT64_C(0x4100), UINT64_C(0x4101), UINT64_C(0x4104), UINT64_C(0x4105), UINT64_C(0x4110), UINT64_C(0x4111), 241 | UINT64_C(0x4114), UINT64_C(0x4115), UINT64_C(0x4140), UINT64_C(0x4141), UINT64_C(0x4144), UINT64_C(0x4145), 242 | UINT64_C(0x4150), UINT64_C(0x4151), UINT64_C(0x4154), UINT64_C(0x4155), UINT64_C(0x4400), UINT64_C(0x4401), 243 | UINT64_C(0x4404), UINT64_C(0x4405), UINT64_C(0x4410), UINT64_C(0x4411), UINT64_C(0x4414), UINT64_C(0x4415), 244 | UINT64_C(0x4440), UINT64_C(0x4441), UINT64_C(0x4444), UINT64_C(0x4445), UINT64_C(0x4450), UINT64_C(0x4451), 245 | UINT64_C(0x4454), UINT64_C(0x4455), UINT64_C(0x4500), UINT64_C(0x4501), UINT64_C(0x4504), UINT64_C(0x4505), 246 | UINT64_C(0x4510), UINT64_C(0x4511), UINT64_C(0x4514), UINT64_C(0x4515), UINT64_C(0x4540), UINT64_C(0x4541), 247 | UINT64_C(0x4544), UINT64_C(0x4545), UINT64_C(0x4550), UINT64_C(0x4551), UINT64_C(0x4554), UINT64_C(0x4555), 248 | UINT64_C(0x5000), UINT64_C(0x5001), UINT64_C(0x5004), UINT64_C(0x5005), UINT64_C(0x5010), UINT64_C(0x5011), 249 | UINT64_C(0x5014), UINT64_C(0x5015), UINT64_C(0x5040), UINT64_C(0x5041), UINT64_C(0x5044), UINT64_C(0x5045), 250 | UINT64_C(0x5050), UINT64_C(0x5051), UINT64_C(0x5054), UINT64_C(0x5055), UINT64_C(0x5100), UINT64_C(0x5101), 251 | UINT64_C(0x5104), UINT64_C(0x5105), UINT64_C(0x5110), UINT64_C(0x5111), UINT64_C(0x5114), UINT64_C(0x5115), 252 | UINT64_C(0x5140), UINT64_C(0x5141), UINT64_C(0x5144), UINT64_C(0x5145), UINT64_C(0x5150), UINT64_C(0x5151), 253 | UINT64_C(0x5154), UINT64_C(0x5155), UINT64_C(0x5400), UINT64_C(0x5401), UINT64_C(0x5404), UINT64_C(0x5405), 254 | UINT64_C(0x5410), UINT64_C(0x5411), UINT64_C(0x5414), UINT64_C(0x5415), UINT64_C(0x5440), UINT64_C(0x5441), 255 | UINT64_C(0x5444), UINT64_C(0x5445), UINT64_C(0x5450), UINT64_C(0x5451), UINT64_C(0x5454), UINT64_C(0x5455), 256 | UINT64_C(0x5500), UINT64_C(0x5501), UINT64_C(0x5504), UINT64_C(0x5505), UINT64_C(0x5510), UINT64_C(0x5511), 257 | UINT64_C(0x5514), UINT64_C(0x5515), UINT64_C(0x5540), UINT64_C(0x5541), UINT64_C(0x5544), UINT64_C(0x5545), 258 | UINT64_C(0x5550), UINT64_C(0x5551), UINT64_C(0x5554), UINT64_C(0x5555) 259 | }; 260 | return (map[upper]<<1)+map[lower]; 261 | } 262 | 263 | /* 264 | latitude must be in [-90.0, 90.0) and longitude must be in [-180.0 180.0) 265 | */ 266 | int geohash_encode(double latitude, double longitude, char* r, size_t capacity){ 267 | static const char* map="0123456789bcdefghjkmnpqrstuvwxyz"; 268 | union { 269 | double d; // assuming IEEE 754-1985 binary64. This might not be true on some CPU (I don't know which). 270 | // formally, we should use unsigned char for type-punning (see C99 ISO/IEC 9899:201x spec 6.2.6) 271 | uint64_t i64; 272 | } lat, lon; 273 | unsigned short lat_exp, lon_exp; 274 | char lr[27]; 275 | 276 | #ifdef __UNSUPPORTED_ENDIAN__ 277 | if(capacity>0){ r[0]='\0'; } 278 | return GEOHASH_NOTSUPPORTED; 279 | #endif 280 | 281 | lat.d = latitude/180.0; 282 | lon.d = longitude/360.0; 283 | 284 | lat_exp = (lat.i64>>52) & UINT64_C(0x7FF); 285 | if(lat.d!=0.0){ 286 | lat.i64 = (lat.i64 & UINT64_C(0xFFFFFFFFFFFFF)) | UINT64_C(0x10000000000000); 287 | } 288 | lon_exp = (lon.i64>>52) & UINT64_C(0x7FF); 289 | if(lon.d!=0.0){ 290 | lon.i64 = (lon.i64 & UINT64_C(0xFFFFFFFFFFFFF)) | UINT64_C(0x10000000000000); 291 | } 292 | 293 | if(lat_exp<1011){ 294 | lat.i64=lat.i64>>(1011-lat_exp); 295 | }else{ 296 | lat.i64=lat.i64<<(lat_exp-1011); 297 | } 298 | if(lon_exp<1011){ 299 | lon.i64=lon.i64>>(1011-lon_exp); 300 | }else{ 301 | lon.i64=lon.i64<<(lon_exp-1011); 302 | } 303 | 304 | if(latitude>0.0){ 305 | lat.i64 = UINT64_C(0x8000000000000000) + lat.i64; 306 | }else{ 307 | lat.i64 = UINT64_C(0x8000000000000000) - lat.i64; 308 | } 309 | if(longitude>0.0){ 310 | lon.i64 = UINT64_C(0x8000000000000000) + lon.i64; 311 | }else{ 312 | lon.i64 = UINT64_C(0x8000000000000000) - lon.i64; 313 | } 314 | 315 | uint64_t idx0,idx1; 316 | idx0 = idx1 = 0; 317 | idx1 |= interleave((uint8_t)(lon.i64>>56), (uint8_t)(lat.i64>>56))<<48; 318 | idx1 |= interleave((uint8_t)(lon.i64>>48), (uint8_t)(lat.i64>>48))<<32; 319 | idx1 |= interleave((uint8_t)(lon.i64>>40), (uint8_t)(lat.i64>>40))<<16; 320 | idx1 |= interleave((uint8_t)(lon.i64>>32), (uint8_t)(lat.i64>>32)); 321 | idx0 |= interleave((uint8_t)(lon.i64>>24), (uint8_t)(lat.i64>>24))<<48; 322 | idx0 |= interleave((uint8_t)(lon.i64>>16), (uint8_t)(lat.i64>>16))<<32; 323 | idx0 |= interleave((uint8_t)(lon.i64>>8), (uint8_t)(lat.i64>>8))<<16; 324 | idx0 |= interleave((uint8_t)lon.i64, (uint8_t)lat.i64); 325 | lr[0] = map[(idx1>>59)&0x1F]; 326 | lr[1] = map[(idx1>>54)&0x1F]; 327 | lr[2] = map[(idx1>>49)&0x1F]; 328 | lr[3] = map[(idx1>>44)&0x1F]; 329 | lr[4] = map[(idx1>>39)&0x1F]; 330 | lr[5] = map[(idx1>>34)&0x1F]; 331 | lr[6] = map[(idx1>>29)&0x1F]; 332 | lr[7] = map[(idx1>>24)&0x1F]; 333 | lr[8] = map[(idx1>>19)&0x1F]; 334 | lr[9] = map[(idx1>>14)&0x1F]; 335 | lr[10] = map[(idx1>>9)&0x1F]; 336 | lr[11] = map[(idx1>>4)&0x1F]; 337 | lr[12] = map[((idx1<<1)&0x1E)|(idx0>>63)]; 338 | lr[13] = map[(idx0>>58)&0x1F]; 339 | lr[14] = map[(idx0>>53)&0x1F]; 340 | lr[15] = map[(idx0>>48)&0x1F]; 341 | lr[16] = map[(idx0>>43)&0x1F]; 342 | lr[17] = map[(idx0>>38)&0x1F]; 343 | lr[18] = map[(idx0>>33)&0x1F]; 344 | lr[19] = map[(idx0>>28)&0x1F]; 345 | lr[20] = map[(idx0>>23)&0x1F]; 346 | lr[21] = map[(idx0>>18)&0x1F]; 347 | lr[22] = map[(idx0>>13)&0x1F]; 348 | lr[23] = map[(idx0>>8)&0x1F]; 349 | lr[24] = map[(idx0>>3)&0x1F]; 350 | lr[25] = map[(idx0<<2)&0x1F]; 351 | lr[26] = '\0'; 352 | if(025){ length=25; } // round if the hashcode is too long (over 64bit) 387 | union { 388 | double d; // assuming IEEE 754-1985 binary64. This might not be true on some CPU (I don't know which). 389 | unsigned char s[8]; 390 | // formally, we should use unsigned char for type-punning (see C99 ISO/IEC 9899:201x spec 6.2.6) 391 | uint64_t i64; 392 | } lat, lon; 393 | lat.i64 = lon.i64 = 0; 394 | while(cshift>2) | ((o1&0x04)>>1) | (o1&0x01); 402 | lat.i64 = (lat.i64<<2) | ((o1&0x08)>>2) | ((o1&0x02)>>1); 403 | }else{ 404 | lon.i64 = (lon.i64<<2) | ((o1&0x08)>>2) | ((o1&0x02)>>1); 405 | lat.i64 = (lat.i64<<3) | ((o1&0x10)>>2) | ((o1&0x04)>>1) | (o1&0x01); 406 | } 407 | cshift++; 408 | } 409 | if(cshift==0){ 410 | // cshift=1; // no input equals to '0' 411 | // but we do know the result. 412 | *latitude = -90.0; 413 | *longitude = -180.0; 414 | return GEOHASH_OK; 415 | } 416 | 417 | int lat_neg=0,lon_neg=0; 418 | uint64_t lat_h,lon_h; 419 | lat_h=UINT64_C(1)<<(5*(cshift/2) + 2*(cshift%2)-1); 420 | lon_h=UINT64_C(1)<<(5*(cshift/2) + 3*(cshift%2)-1); 421 | 422 | if(lat.i64>=lat_h){ 423 | lat.i64=lat.i64-lat_h; 424 | }else{ 425 | lat.i64=lat_h-lat.i64; 426 | lat_neg=1; 427 | } 428 | if(lon.i64>=lon_h){ 429 | lon.i64=lon.i64-lon_h; 430 | }else{ 431 | lon.i64=lon_h-lon.i64; 432 | lon_neg=1; 433 | } 434 | 435 | // rounding to double representation 436 | int i,lat_i=-1,lon_i=-1; 437 | for(i=0;i<64;i++){ 438 | if(lat.i64>>i){ lat_i=i; } 439 | if(lon.i64>>i){ lon_i=i; } 440 | } 441 | 442 | if(lat_i==-1){ 443 | lat_i = 0; 444 | }else{ 445 | if(lat_i>52){ 446 | lat.i64=lat.i64>>(lat_i-52); 447 | }else{ 448 | lat.i64=lat.i64<<(52-lat_i); 449 | } 450 | lat_i = 1023 + lat_i - 5*(cshift/2) - 2*(cshift%2); 451 | } 452 | if(lon_i==-1){ 453 | lon_i = 0; 454 | }else{ 455 | if(lon_i>52){ 456 | lon.i64=lon.i64>>(lon_i-52); 457 | }else{ 458 | lon.i64=lon.i64<<(52-lon_i); 459 | } 460 | lon_i = 1023 + lon_i - 5*(cshift/2) - 3*(cshift%2); 461 | } 462 | 463 | lat.s[B7]=(lat_neg<<7)|((lat_i>>4)&0x7F); 464 | lon.s[B7]=(lon_neg<<7)|((lon_i>>4)&0x7F); 465 | lat.s[B6]=((lat_i<<4)&0xF0)|(lat.s[B6]&0x0F); 466 | lon.s[B6]=((lon_i<<4)&0xF0)|(lon.s[B6]&0x0F); 467 | 468 | *latitude = 180.0*lat.d; 469 | *longitude = 360.0*lon.d; 470 | return GEOHASH_OK; 471 | } 472 | -------------------------------------------------------------------------------- /src/geohash.h: -------------------------------------------------------------------------------- 1 | #ifdef __cplusplus 2 | extern "C" { 3 | #endif 4 | 5 | enum { 6 | GEOHASH_OK, 7 | GEOHASH_NOTSUPPORTED, 8 | GEOHASH_INVALIDCODE, 9 | }; 10 | 11 | int geohash_encode(double latitude, double longitude, char* r, size_t capacity); 12 | int geohash_decode(char* r, size_t length, double *latitude, double *longitude); 13 | 14 | #ifdef __cplusplus 15 | } 16 | #endif 17 | 18 | 19 | /* 20 | int main(int argc,char* argv[]){ 21 | char r[23]; 22 | double lat,lon; 23 | if(geohash_encode(35.0, 135.0, r, 23)==GEOHASH_OK){ 24 | printf("%s\n",r); 25 | } 26 | if(geohash_decode(r,23,&lat,&lon)==GEOHASH_OK){ 27 | printf("%f %f\n",lat,lon); 28 | } 29 | } 30 | */ 31 | -------------------------------------------------------------------------------- /src/geohash_sample.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "geohash.h" 3 | 4 | int main(int argc,char* argv[]){ 5 | char r[20]; 6 | double lat,lon; 7 | int res; 8 | if((res=geohash_encode(35.0, 135.0, r, 20))==GEOHASH_OK){ 9 | printf("%s\n",r); 10 | }else{ 11 | printf("error with %d\n",res); 12 | } 13 | if((res=geohash_decode(r,23,&lat,&lon))==GEOHASH_OK){ 14 | printf("%f %f\n",lat,lon); 15 | }else{ 16 | printf("error with %d\n",res); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/test_decode.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import geohash 3 | 4 | class TestDecode(unittest.TestCase): 5 | def test_empty(self): 6 | self.assertEqual( 7 | geohash.bbox(''), 8 | {'s':-90.0, 'n':90.0, 'w':-180.0, 'e':180.0}) 9 | 10 | def test_one(self): 11 | seq = '0123456789bcdefghjkmnpqrstuvwxyz' 12 | sws = [ 13 | (-90.0, -180.0), 14 | (-90.0, -135.0), 15 | (-45.0, -180.0), 16 | (-45.0, -135.0), 17 | (-90.0, -90.0), 18 | (-90.0, -45.0), 19 | (-45.0, -90.0), 20 | (-45.0, -45.0), 21 | (0.0, -180.0), 22 | (0.0, -135.0), 23 | (45.0, -180.0), 24 | (45.0, -135.0), 25 | (0.0, -90.0), 26 | (0.0, -45.0), 27 | (45.0, -90.0), 28 | (45.0, -45.0), 29 | (-90.0, 0.0), 30 | (-90.0, 45.0), 31 | (-45.0, 0.0), 32 | (-45.0, 45.0), 33 | (-90.0, 90.0), 34 | (-90.0, 135.0), 35 | (-45.0, 90.0), 36 | (-45.0, 135.0), 37 | (0.0, 0.0), 38 | (0.0, 45.0), 39 | (45.0, 0.0), 40 | (45.0, 45.0), 41 | (0.0, 90.0), 42 | (0.0, 135.0), 43 | (45.0, 90.0), 44 | (45.0, 135.0) 45 | ] 46 | for i in zip(seq, sws): 47 | x = geohash.bbox(i[0]) 48 | self.assertEqual((x['s'], x['w']), i[1]) 49 | self.assertEqual(x['n']-x['s'], 45) 50 | self.assertEqual(x['e']-x['w'], 45) 51 | 52 | def test_ezs42(self): 53 | x=geohash.bbox('ezs42') 54 | self.assertEqual(round(x['s'],3), 42.583) 55 | self.assertEqual(round(x['n'],3), 42.627) 56 | 57 | # if __name__=='__main__': 58 | # unittest.main() 59 | -------------------------------------------------------------------------------- /test/test_encode.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import geohash 3 | 4 | class TestEncode(unittest.TestCase): 5 | def test_cycle(self): 6 | for code in ["000000000000","zzzzzzzzzzzz","bgr96qxvpd46",]: 7 | self.assertEqual(code, geohash.encode(*geohash.decode(code))) 8 | 9 | # if __name__=='__main__': 10 | # unittest.main() 11 | --------------------------------------------------------------------------------