├── MANIFEST.in ├── README ├── geohash.py ├── jpgrid.py ├── jpiarea.py ├── quadtree.py ├── setup.py ├── src ├── Makefile ├── geohash.cpp ├── geohash.h └── geohash_sample.c └── test ├── perf.py ├── test_geohash.py ├── test_jpgrid.py ├── test_jpiarea.py └── test_uint64.py /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include src * 2 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | * python-geohash 2 | python-geohash is a fast, accurate python geohashing library. 3 | 4 | ** History 5 | python-geohash 0.8 introduced uint64 representation. 6 | 7 | python-geohash 0.7.1 starts supporting python3k. 8 | 9 | python-geohash 0.3 can create C extension. If you want to use python-geohash 10 | without C extension, simply copy geohash.py into your system. geohash.py will 11 | work fine without C extension. 12 | 13 | ** LICENSE 14 | Code is licensed under Apache License 2.0, MIT Licence and NEW BSD License. 15 | You can choose one of these licences. Declarations follow: 16 | 17 | *** Apache License 2.0 18 | Copyright 2011 Hiroaki Kawai 19 | 20 | Licensed under the Apache License, Version 2.0 (the "License"); 21 | you may not use this file except in compliance with the License. 22 | You may obtain a copy of the License at 23 | 24 | http://www.apache.org/licenses/LICENSE-2.0 25 | 26 | Unless required by applicable law or agreed to in writing, software 27 | distributed under the License is distributed on an "AS IS" BASIS, 28 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 29 | See the License for the specific language governing permissions and 30 | limitations under the License. 31 | 32 | *** MIT License 33 | Permission is hereby granted, free of charge, to any person obtaining a copy 34 | of this software and associated documentation files (the "Software"), to deal 35 | in the Software without restriction, including without limitation the rights 36 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 37 | copies of the Software, and to permit persons to whom the Software is 38 | furnished to do so, subject to the following conditions: 39 | 40 | The above copyright notice and this permission notice shall be included in 41 | all copies or substantial portions of the Software. 42 | 43 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 44 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 45 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 46 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 47 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 48 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 49 | THE SOFTWARE. 50 | 51 | *** New BSD License 52 | Copyright (c) 2011, Hiroaki Kawai 53 | All rights reserved. 54 | 55 | Redistribution and use in source and binary forms, with or without 56 | modification, are permitted provided that the following conditions are met: 57 | * Redistributions of source code must retain the above copyright 58 | notice, this list of conditions and the following disclaimer. 59 | * Redistributions in binary form must reproduce the above copyright 60 | notice, this list of conditions and the following disclaimer in the 61 | documentation and/or other materials provided with the distribution. 62 | * Neither the name of the python-geohash nor the 63 | names of its contributors may be used to endorse or promote products 64 | derived from this software without specific prior written permission. 65 | 66 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 67 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 68 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 69 | DISCLAIMED. IN NO EVENT SHALL Hiroaki Kawai BE LIABLE FOR ANY 70 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 71 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 72 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 73 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 74 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 75 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 76 | 77 | -------------------------------------------------------------------------------- /geohash.py: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | """ 3 | Copyright (C) 2009 Hiroaki Kawai 4 | """ 5 | try: 6 | import _geohash 7 | except ImportError: 8 | _geohash = None 9 | 10 | __version__ = "0.8.5" 11 | __all__ = ['encode','decode','decode_exactly','bbox', 'neighbors', 'expand'] 12 | 13 | _base32 = '0123456789bcdefghjkmnpqrstuvwxyz' 14 | _base32_map = {} 15 | for i in range(len(_base32)): 16 | _base32_map[_base32[i]] = i 17 | del i 18 | 19 | LONG_ZERO = 0 20 | import sys 21 | if sys.version_info[0] < 3: 22 | LONG_ZERO = long(0) 23 | 24 | def _float_hex_to_int(f): 25 | if f<-1.0 or f>=1.0: 26 | return None 27 | 28 | if f==0.0: 29 | return 1,1 30 | 31 | h = f.hex() 32 | x = h.find("0x1.") 33 | assert(x>=0) 34 | p = h.find("p") 35 | assert(p>0) 36 | 37 | half_len = len(h[x+4:p])*4-int(h[p+1:]) 38 | if x==0: 39 | r = (1<= half: 52 | i = i-half 53 | return float.fromhex(("0x0.%0"+str(s)+"xp1") % (i<<(s*4-l),)) 54 | else: 55 | i = half-i 56 | return float.fromhex(("-0x0.%0"+str(s)+"xp1") % (i<<(s*4-l),)) 57 | 58 | def _encode_i2c(lat,lon,lat_length,lon_length): 59 | precision = int((lat_length+lon_length)/5) 60 | if lat_length < lon_length: 61 | a = lon 62 | b = lat 63 | else: 64 | a = lat 65 | b = lon 66 | 67 | boost = (0,1,4,5,16,17,20,21) 68 | ret = '' 69 | for i in range(precision): 70 | ret+=_base32[(boost[a&7]+(boost[b&3]<<1))&0x1F] 71 | t = a>>3 72 | a = b>>2 73 | b = t 74 | 75 | return ret[::-1] 76 | 77 | def encode(latitude, longitude, precision=12): 78 | if latitude >= 90.0 or latitude < -90.0: 79 | raise Exception("invalid latitude.") 80 | while longitude < -180.0: 81 | longitude += 360.0 82 | while longitude >= 180.0: 83 | longitude -= 360.0 84 | 85 | if _geohash: 86 | basecode=_geohash.encode(latitude,longitude) 87 | if len(basecode)>precision: 88 | return basecode[0:precision] 89 | return basecode+'0'*(precision-len(basecode)) 90 | 91 | xprecision=precision+1 92 | lat_length = lon_length = int(xprecision*5/2) 93 | if xprecision%2==1: 94 | lon_length+=1 95 | 96 | if hasattr(float, "fromhex"): 97 | a = _float_hex_to_int(latitude/90.0) 98 | o = _float_hex_to_int(longitude/180.0) 99 | if a[1] > lat_length: 100 | ai = a[0]>>(a[1]-lat_length) 101 | else: 102 | ai = a[0]<<(lat_length-a[1]) 103 | 104 | if o[1] > lon_length: 105 | oi = o[0]>>(o[1]-lon_length) 106 | else: 107 | oi = o[0]<<(lon_length-o[1]) 108 | 109 | return _encode_i2c(ai, oi, lat_length, lon_length)[:precision] 110 | 111 | lat = latitude/180.0 112 | lon = longitude/360.0 113 | 114 | if lat>0: 115 | lat = int((1<0: 120 | lon = int((1<>2)&4 138 | lat += (t>>2)&2 139 | lon += (t>>1)&2 140 | lat += (t>>1)&1 141 | lon += t&1 142 | lon_length+=3 143 | lat_length+=2 144 | else: 145 | lon = lon<<2 146 | lat = lat<<3 147 | lat += (t>>2)&4 148 | lon += (t>>2)&2 149 | lat += (t>>1)&2 150 | lon += (t>>1)&1 151 | lat += t&1 152 | lon_length+=2 153 | lat_length+=3 154 | 155 | bit_length+=5 156 | 157 | return (lat,lon,lat_length,lon_length) 158 | 159 | def decode(hashcode, delta=False): 160 | ''' 161 | decode a hashcode and get center coordinate, and distance between center and outer border 162 | ''' 163 | if _geohash: 164 | (lat,lon,lat_bits,lon_bits) = _geohash.decode(hashcode) 165 | latitude_delta = 90.0/(1<> lat_length: 252 | for tlon in (lon-1, lon, lon+1): 253 | ret.append(_encode_i2c(tlat,tlon,lat_length,lon_length)) 254 | 255 | tlat = lat-1 256 | if tlat >= 0: 257 | for tlon in (lon-1, lon, lon+1): 258 | ret.append(_encode_i2c(tlat,tlon,lat_length,lon_length)) 259 | 260 | return ret 261 | 262 | def expand(hashcode): 263 | ret = neighbors(hashcode) 264 | ret.append(hashcode) 265 | return ret 266 | 267 | def _uint64_interleave(lat32, lon32): 268 | intr = 0 269 | boost = (0,1,4,5,16,17,20,21,64,65,68,69,80,81,84,85) 270 | for i in range(8): 271 | intr = (intr<<8) + (boost[(lon32>>(28-i*4))%16]<<1) + boost[(lat32>>(28-i*4))%16] 272 | 273 | return intr 274 | 275 | def _uint64_deinterleave(ui64): 276 | lat = lon = 0 277 | boost = ((0,0),(0,1),(1,0),(1,1),(0,2),(0,3),(1,2),(1,3), 278 | (2,0),(2,1),(3,0),(3,1),(2,2),(2,3),(3,2),(3,3)) 279 | for i in range(16): 280 | p = boost[(ui64>>(60-i*4))%16] 281 | lon = (lon<<2) + p[0] 282 | lat = (lat<<2) + p[1] 283 | 284 | return (lat, lon) 285 | 286 | def encode_uint64(latitude, longitude): 287 | if latitude >= 90.0 or latitude < -90.0: 288 | raise ValueError("Latitude must be in the range of (-90.0, 90.0)") 289 | while longitude < -180.0: 290 | longitude += 360.0 291 | while longitude >= 180.0: 292 | longitude -= 360.0 293 | 294 | if _geohash: 295 | ui128 = _geohash.encode_int(latitude,longitude) 296 | if _geohash.intunit == 64: 297 | return ui128[0] 298 | elif _geohash.intunit == 32: 299 | return (ui128[0]<<32) + ui128[1] 300 | elif _geohash.intunit == 16: 301 | return (ui128[0]<<48) + (ui128[1]<<32) + (ui128[2]<<16) + ui128[3] 302 | 303 | lat = int(((latitude + 90.0)/180.0)*(1<<32)) 304 | lon = int(((longitude+180.0)/360.0)*(1<<32)) 305 | return _uint64_interleave(lat, lon) 306 | 307 | def decode_uint64(ui64): 308 | if _geohash: 309 | latlon = _geohash.decode_int(ui64 % 0xFFFFFFFFFFFFFFFF, LONG_ZERO) 310 | if latlon: 311 | return latlon 312 | 313 | lat,lon = _uint64_deinterleave(ui64) 314 | return (180.0*lat/(1<<32) - 90.0, 360.0*lon/(1<<32) - 180.0) 315 | 316 | def expand_uint64(ui64, precision=50): 317 | ui64 = ui64 & (0xFFFFFFFFFFFFFFFF << (64-precision)) 318 | lat,lon = _uint64_deinterleave(ui64) 319 | lat_grid = 1<<(32-int(precision/2)) 320 | lon_grid = lat_grid>>(precision%2) 321 | 322 | if precision<=2: # expand becomes to the whole range 323 | return [] 324 | 325 | ranges = [] 326 | if lat & lat_grid: 327 | if lon & lon_grid: 328 | ui64 = _uint64_interleave(lat-lat_grid, lon-lon_grid) 329 | ranges.append((ui64, ui64 + (1<<(64-precision+2)))) 330 | if precision%2==0: 331 | # lat,lon = (1, 1) and even precision 332 | ui64 = _uint64_interleave(lat-lat_grid, lon+lon_grid) 333 | ranges.append((ui64, ui64 + (1<<(64-precision+1)))) 334 | 335 | if lat + lat_grid < 0xFFFFFFFF: 336 | ui64 = _uint64_interleave(lat+lat_grid, lon-lon_grid) 337 | ranges.append((ui64, ui64 + (1<<(64-precision)))) 338 | ui64 = _uint64_interleave(lat+lat_grid, lon) 339 | ranges.append((ui64, ui64 + (1<<(64-precision)))) 340 | ui64 = _uint64_interleave(lat+lat_grid, lon+lon_grid) 341 | ranges.append((ui64, ui64 + (1<<(64-precision)))) 342 | else: 343 | # lat,lon = (1, 1) and odd precision 344 | if lat + lat_grid < 0xFFFFFFFF: 345 | ui64 = _uint64_interleave(lat+lat_grid, lon-lon_grid) 346 | ranges.append((ui64, ui64 + (1<<(64-precision+1)))) 347 | 348 | ui64 = _uint64_interleave(lat+lat_grid, lon+lon_grid) 349 | ranges.append((ui64, ui64 + (1<<(64-precision)))) 350 | 351 | ui64 = _uint64_interleave(lat, lon+lon_grid) 352 | ranges.append((ui64, ui64 + (1<<(64-precision)))) 353 | ui64 = _uint64_interleave(lat-lat_grid, lon+lon_grid) 354 | ranges.append((ui64, ui64 + (1<<(64-precision)))) 355 | else: 356 | ui64 = _uint64_interleave(lat-lat_grid, lon) 357 | ranges.append((ui64, ui64 + (1<<(64-precision+2)))) 358 | if precision%2==0: 359 | # lat,lon = (1, 0) and odd precision 360 | ui64 = _uint64_interleave(lat-lat_grid, lon-lon_grid) 361 | ranges.append((ui64, ui64 + (1<<(64-precision+1)))) 362 | 363 | if lat + lat_grid < 0xFFFFFFFF: 364 | ui64 = _uint64_interleave(lat+lat_grid, lon-lon_grid) 365 | ranges.append((ui64, ui64 + (1<<(64-precision)))) 366 | ui64 = _uint64_interleave(lat+lat_grid, lon) 367 | ranges.append((ui64, ui64 + (1<<(64-precision)))) 368 | ui64 = _uint64_interleave(lat+lat_grid, lon+lon_grid) 369 | ranges.append((ui64, ui64 + (1<<(64-precision)))) 370 | else: 371 | # lat,lon = (1, 0) and odd precision 372 | if lat + lat_grid < 0xFFFFFFFF: 373 | ui64 = _uint64_interleave(lat+lat_grid, lon) 374 | ranges.append((ui64, ui64 + (1<<(64-precision+1)))) 375 | 376 | ui64 = _uint64_interleave(lat+lat_grid, lon-lon_grid) 377 | ranges.append((ui64, ui64 + (1<<(64-precision)))) 378 | ui64 = _uint64_interleave(lat, lon-lon_grid) 379 | ranges.append((ui64, ui64 + (1<<(64-precision)))) 380 | ui64 = _uint64_interleave(lat-lat_grid, lon-lon_grid) 381 | ranges.append((ui64, ui64 + (1<<(64-precision)))) 382 | else: 383 | if lon & lon_grid: 384 | ui64 = _uint64_interleave(lat, lon-lon_grid) 385 | ranges.append((ui64, ui64 + (1<<(64-precision+2)))) 386 | if precision%2==0: 387 | # lat,lon = (0, 1) and even precision 388 | ui64 = _uint64_interleave(lat, lon+lon_grid) 389 | ranges.append((ui64, ui64 + (1<<(64-precision+1)))) 390 | 391 | if lat > 0: 392 | ui64 = _uint64_interleave(lat-lat_grid, lon-lon_grid) 393 | ranges.append((ui64, ui64 + (1<<(64-precision)))) 394 | ui64 = _uint64_interleave(lat-lat_grid, lon) 395 | ranges.append((ui64, ui64 + (1<<(64-precision)))) 396 | ui64 = _uint64_interleave(lat-lat_grid, lon+lon_grid) 397 | ranges.append((ui64, ui64 + (1<<(64-precision)))) 398 | else: 399 | # lat,lon = (0, 1) and odd precision 400 | if lat > 0: 401 | ui64 = _uint64_interleave(lat-lat_grid, lon-lon_grid) 402 | ranges.append((ui64, ui64 + (1<<(64-precision+1)))) 403 | 404 | ui64 = _uint64_interleave(lat-lat_grid, lon+lon_grid) 405 | ranges.append((ui64, ui64 + (1<<(64-precision)))) 406 | ui64 = _uint64_interleave(lat, lon+lon_grid) 407 | ranges.append((ui64, ui64 + (1<<(64-precision)))) 408 | ui64 = _uint64_interleave(lat+lat_grid, lon+lon_grid) 409 | ranges.append((ui64, ui64 + (1<<(64-precision)))) 410 | else: 411 | ui64 = _uint64_interleave(lat, lon) 412 | ranges.append((ui64, ui64 + (1<<(64-precision+2)))) 413 | if precision%2==0: 414 | # lat,lon = (0, 0) and even precision 415 | ui64 = _uint64_interleave(lat, lon-lon_grid) 416 | ranges.append((ui64, ui64 + (1<<(64-precision+1)))) 417 | 418 | if lat > 0: 419 | ui64 = _uint64_interleave(lat-lat_grid, lon-lon_grid) 420 | ranges.append((ui64, ui64 + (1<<(64-precision)))) 421 | ui64 = _uint64_interleave(lat-lat_grid, lon) 422 | ranges.append((ui64, ui64 + (1<<(64-precision)))) 423 | ui64 = _uint64_interleave(lat-lat_grid, lon+lon_grid) 424 | ranges.append((ui64, ui64 + (1<<(64-precision)))) 425 | else: 426 | # lat,lon = (0, 0) and odd precision 427 | if lat > 0: 428 | ui64 = _uint64_interleave(lat-lat_grid, lon) 429 | ranges.append((ui64, ui64 + (1<<(64-precision+1)))) 430 | 431 | ui64 = _uint64_interleave(lat-lat_grid, lon-lon_grid) 432 | ranges.append((ui64, ui64 + (1<<(64-precision)))) 433 | ui64 = _uint64_interleave(lat, lon-lon_grid) 434 | ranges.append((ui64, ui64 + (1<<(64-precision)))) 435 | ui64 = _uint64_interleave(lat+lat_grid, lon-lon_grid) 436 | ranges.append((ui64, ui64 + (1<<(64-precision)))) 437 | 438 | ranges.sort() 439 | 440 | # merge the conditions 441 | shrink = [] 442 | prev = None 443 | for i in ranges: 444 | if prev: 445 | if prev[1] != i[0]: 446 | shrink.append(prev) 447 | prev = i 448 | else: 449 | prev = (prev[0], i[1]) 450 | else: 451 | prev = i 452 | 453 | shrink.append(prev) 454 | 455 | ranges = [] 456 | for i in shrink: 457 | a,b=i 458 | if a == 0: 459 | a = None # we can remove the condition because it is the lowest value 460 | if b == 0x10000000000000000: 461 | b = None # we can remove the condition because it is the highest value 462 | 463 | ranges.append((a,b)) 464 | 465 | return ranges 466 | -------------------------------------------------------------------------------- /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 = int(lat/10) 17 | lon = int(lon/10) 18 | base1 = int(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 = int(lat/5) 29 | lon = int(lon/5) 30 | base1 = int(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*base1-100.0*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) + int(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) + int(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 | # center position of the meshcode. 101 | lat = (lat<<1) + 1 102 | lon = (lon<<1) + 1 103 | base1 = base1<<1 104 | return (lat/(base1*1.5), lon/float(base1) + 100.0) 105 | 106 | def bbox(gridcode): 107 | (a,b,c,d) = decode_sw(gridcode, True) 108 | return {'w':a, 's':b, 'n':b+d, 'e':a+c} 109 | 110 | 111 | ## short-cut methods 112 | def encodeLv1(lat, lon): 113 | return encode(lat,lon,1) 114 | 115 | def encodeLv2(lat, lon): 116 | return encode(lat,lon,8) 117 | 118 | def encodeLv3(lat, lon): 119 | return encode(lat,lon,80) 120 | 121 | def encodeBase(lat,lon): 122 | return encodeLv3(lat,lon) 123 | 124 | def encodeHalf(lat,lon): 125 | return encode(lat,lon,160) 126 | 127 | def encodeQuarter(lat,lon): 128 | return encode(lat,lon,320) 129 | 130 | def encodeEighth(lat,lon): 131 | return encode(lat,lon,640) 132 | 133 | def encodeUni10(lat,lon): 134 | return encodeLv2(lat,lon) 135 | 136 | def encodeUni5(lat, lon): 137 | return encode(lat,lon,16) 138 | 139 | def encodeUni2(lat, lon): 140 | return encode(lat,lon,40) 141 | 142 | 143 | def neighbors(gridcode): 144 | (lat,lon,base1)=_decode_c2i(gridcode) 145 | ret = [] 146 | for i in ((0,-1),(0,1),(1,-1),(1,0),(1,1),(-1,-1),(-1,0),(-1,1)): 147 | tlat=lat+i[0] 148 | tlon=lon+i[1] 149 | if tlat<0 or tlat>(90*base1): 150 | continue 151 | if tlon<0 or tlon>(100*base1): 152 | continue 153 | 154 | ret.append(_encode_i2c(tlat,tlon,base1)) 155 | 156 | return ret 157 | 158 | def expand(gridcode): 159 | ret = neighbors(gridcode) 160 | ret.append(gridcode) 161 | return ret 162 | -------------------------------------------------------------------------------- /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(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 | try: 6 | import _geohash 7 | except ImportError: 8 | _geohash = None 9 | 10 | def _encode_i2c(lat,lon,bitlength): 11 | digits='0123' 12 | r = '' 13 | while bitlength>0: 14 | r += digits[((lat&1)<<1)+(lon&1)] 15 | lat = lat>>1 16 | lon = lon>>1 17 | bitlength -= 1 18 | 19 | return r[::-1] 20 | 21 | def _decode_c2i(treecode): 22 | lat = 0 23 | lon = 0 24 | for i in treecode: 25 | b = ord(i)-48 26 | lat = (lat<<1)+int(b/2) 27 | lon = (lon<<1)+b%2 28 | 29 | return (lat,lon,len(treecode)) 30 | 31 | def encode(lat,lon,precision=12): 32 | if _geohash and precision<=64: 33 | ints = _geohash.encode_int(lat, lon) 34 | ret = "" 35 | for intu in ints: 36 | for i in range(int(_geohash.intunit/2)): 37 | if len(ret) > precision: 38 | break 39 | ret += "0213"[(intu>>(_geohash.intunit-2-i*2))&0x03] 40 | 41 | return ret[:precision] 42 | 43 | b = 1<>bitlength: 100 | for tlon in (lon-1, lon, lon+1): 101 | r.append(_encode_i2c(tlat, tlon, bitlength)) 102 | 103 | tlat = lat-1 104 | if tlat>=0: 105 | for tlon in (lon-1, lon, lon+1): 106 | r.append(_encode_i2c(tlat, tlon, bitlength)) 107 | 108 | return r 109 | 110 | def expand(treecode): 111 | r = neighbors(treecode) 112 | r.append(treecode) 113 | return r 114 | 115 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | try: 2 | from setuptools import setup, Extension 3 | except ImportError: 4 | from distutils.core import setup, Extension 5 | 6 | # requiring C++ here for Windows support. 7 | c1=Extension('_geohash', 8 | sources=['src/geohash.cpp',], 9 | define_macros = [('PYTHON_MODULE',1),]) 10 | 11 | setup(name='python-geohash', 12 | version='0.8.5', 13 | description='Fast, accurate python geohashing library', 14 | author='Hiroaki Kawai', 15 | url='https://github.com/hkwi/python-geohash', 16 | py_modules=['geohash','quadtree','jpgrid','jpiarea'], 17 | ext_modules = [c1] 18 | ) 19 | -------------------------------------------------------------------------------- /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 | extern "C" { 2 | #include 3 | #include 4 | #include 5 | 6 | // on Windows, __STDC_IEC_559__ not defined 7 | 8 | #if defined(_MSC_VER) && (_MSC_VER <= 1500) 9 | typedef unsigned __int8 uint8_t; 10 | typedef unsigned __int16 uint16_t; 11 | typedef unsigned __int64 uint64_t; 12 | #define UINT64_C(C) ((uint64_t) C ## ULL) 13 | #else 14 | #define __STDC_CONSTANT_MACROS 1 15 | #include 16 | #endif 17 | 18 | } 19 | #include "geohash.h" 20 | 21 | static inline uint16_t interleave(uint8_t upper, uint8_t lower){ 22 | static const uint16_t map[256] = { 23 | 0x0000, 0x0001, 0x0004, 0x0005, 0x0010, 0x0011, 24 | 0x0014, 0x0015, 0x0040, 0x0041, 0x0044, 0x0045, 25 | 0x0050, 0x0051, 0x0054, 0x0055, 0x0100, 0x0101, 26 | 0x0104, 0x0105, 0x0110, 0x0111, 0x0114, 0x0115, 27 | 0x0140, 0x0141, 0x0144, 0x0145, 0x0150, 0x0151, 28 | 0x0154, 0x0155, 0x0400, 0x0401, 0x0404, 0x0405, 29 | 0x0410, 0x0411, 0x0414, 0x0415, 0x0440, 0x0441, 30 | 0x0444, 0x0445, 0x0450, 0x0451, 0x0454, 0x0455, 31 | 0x0500, 0x0501, 0x0504, 0x0505, 0x0510, 0x0511, 32 | 0x0514, 0x0515, 0x0540, 0x0541, 0x0544, 0x0545, 33 | 0x0550, 0x0551, 0x0554, 0x0555, 0x1000, 0x1001, 34 | 0x1004, 0x1005, 0x1010, 0x1011, 0x1014, 0x1015, 35 | 0x1040, 0x1041, 0x1044, 0x1045, 0x1050, 0x1051, 36 | 0x1054, 0x1055, 0x1100, 0x1101, 0x1104, 0x1105, 37 | 0x1110, 0x1111, 0x1114, 0x1115, 0x1140, 0x1141, 38 | 0x1144, 0x1145, 0x1150, 0x1151, 0x1154, 0x1155, 39 | 0x1400, 0x1401, 0x1404, 0x1405, 0x1410, 0x1411, 40 | 0x1414, 0x1415, 0x1440, 0x1441, 0x1444, 0x1445, 41 | 0x1450, 0x1451, 0x1454, 0x1455, 0x1500, 0x1501, 42 | 0x1504, 0x1505, 0x1510, 0x1511, 0x1514, 0x1515, 43 | 0x1540, 0x1541, 0x1544, 0x1545, 0x1550, 0x1551, 44 | 0x1554, 0x1555, 0x4000, 0x4001, 0x4004, 0x4005, 45 | 0x4010, 0x4011, 0x4014, 0x4015, 0x4040, 0x4041, 46 | 0x4044, 0x4045, 0x4050, 0x4051, 0x4054, 0x4055, 47 | 0x4100, 0x4101, 0x4104, 0x4105, 0x4110, 0x4111, 48 | 0x4114, 0x4115, 0x4140, 0x4141, 0x4144, 0x4145, 49 | 0x4150, 0x4151, 0x4154, 0x4155, 0x4400, 0x4401, 50 | 0x4404, 0x4405, 0x4410, 0x4411, 0x4414, 0x4415, 51 | 0x4440, 0x4441, 0x4444, 0x4445, 0x4450, 0x4451, 52 | 0x4454, 0x4455, 0x4500, 0x4501, 0x4504, 0x4505, 53 | 0x4510, 0x4511, 0x4514, 0x4515, 0x4540, 0x4541, 54 | 0x4544, 0x4545, 0x4550, 0x4551, 0x4554, 0x4555, 55 | 0x5000, 0x5001, 0x5004, 0x5005, 0x5010, 0x5011, 56 | 0x5014, 0x5015, 0x5040, 0x5041, 0x5044, 0x5045, 57 | 0x5050, 0x5051, 0x5054, 0x5055, 0x5100, 0x5101, 58 | 0x5104, 0x5105, 0x5110, 0x5111, 0x5114, 0x5115, 59 | 0x5140, 0x5141, 0x5144, 0x5145, 0x5150, 0x5151, 60 | 0x5154, 0x5155, 0x5400, 0x5401, 0x5404, 0x5405, 61 | 0x5410, 0x5411, 0x5414, 0x5415, 0x5440, 0x5441, 62 | 0x5444, 0x5445, 0x5450, 0x5451, 0x5454, 0x5455, 63 | 0x5500, 0x5501, 0x5504, 0x5505, 0x5510, 0x5511, 64 | 0x5514, 0x5515, 0x5540, 0x5541, 0x5544, 0x5545, 65 | 0x5550, 0x5551, 0x5554, 0x5555 66 | }; 67 | return (map[upper]<<1)+map[lower]; 68 | } 69 | 70 | static inline void deinterleave(uint16_t interleaved, uint8_t *upper, uint8_t *lower){ 71 | *upper = *lower = 0; 72 | for(int i=7; i>=0; i--){ 73 | *upper = (*upper<<1) + ((interleaved>>(i*2+1))&0x01); 74 | *lower = (*lower<<1) + ((interleaved>>(i*2))&0x01); 75 | } 76 | } 77 | 78 | /** 79 | * map double[-1.0, 1.0) into uint64_t 80 | */ 81 | static inline int double_to_i64(double in, uint64_t *out){ 82 | if(in<-1.0 || 1.0<=in){ 83 | return 0; 84 | } 85 | union { 86 | double d; // assuming IEEE 754-1985 binary64. This might not be true on some CPU (I don't know which). 87 | // formally, we should use unsigned char for type-punning (see C99 ISO/IEC 9899:201x spec 6.2.6) 88 | uint64_t i64; 89 | } x; 90 | x.d = in; 91 | int sign = x.i64 >> 63; 92 | int exp = (x.i64 >> 52) & 0x7FF; 93 | if(exp==0){ 94 | *out = UINT64_C(0x8000000000000000); 95 | return !0; 96 | }else if(exp==0x7FF){ 97 | return 0; 98 | } 99 | 100 | x.i64 &= UINT64_C(0x000FFFFFFFFFFFFF); 101 | x.i64 |= UINT64_C(0x0010000000000000); 102 | int shift = exp - 0x3FF + 11; 103 | if(shift > 0){ 104 | x.i64 <<= shift; 105 | }else{ 106 | x.i64 >>= -shift; 107 | } 108 | if(sign){ 109 | x.i64 = UINT64_C(0x8000000000000000) - x.i64; 110 | }else{ 111 | x.i64 += UINT64_C(0x8000000000000000); 112 | } 113 | *out = x.i64; 114 | 115 | return !0; 116 | } 117 | 118 | /** 119 | * map uint64_t into double[-1.0, 1.0) 120 | */ 121 | static inline void i64_to_double(uint64_t in, double *out){ 122 | union { 123 | double d; // assuming IEEE 754-1985 binary64. This might not be true on some CPU (I don't know which). 124 | // formally, we should use unsigned char for type-punning (see C99 ISO/IEC 9899:201x spec 6.2.6) 125 | uint64_t i64; 126 | } x; 127 | if(in==UINT64_C(0x8000000000000000)){ 128 | *out = 0.0; 129 | return; 130 | } 131 | int sign = 0; 132 | if(in < UINT64_C(0x8000000000000000)){ 133 | sign = 1; // negative. -1.0 -- 0.0 134 | in = UINT64_C(0x8000000000000000) - in; 135 | }else{ 136 | in -= UINT64_C(0x8000000000000000); 137 | } 138 | int i; 139 | for(i=0;i<64;i++){ 140 | if(in>>(63-i)){ break; } 141 | } 142 | if(i>11){ 143 | x.i64 = in<<(i-11); 144 | }else{ 145 | x.i64 = in>>(11-i); 146 | } 147 | x.i64 = ((UINT64_C(0x3FF) - i)<<52) + (x.i64 & UINT64_C(0x000FFFFFFFFFFFFF)); 148 | if(sign){ 149 | x.i64 |= UINT64_C(0x8000000000000000); 150 | } 151 | *out = x.d; 152 | } 153 | 154 | static int interleaved_to_geohashstr(uint16_t *interleaved, size_t length, char* dst, size_t dst_length){ 155 | static const char* map="0123456789bcdefghjkmnpqrstuvwxyz"; 156 | if(dst_length*5 < length*16){ 157 | return GEOHASH_INTERNALERROR; 158 | } 159 | 160 | unsigned char *w = (unsigned char*)dst; 161 | uint16_t *i = interleaved; 162 | for(unsigned int j=0; j>11); 164 | w[ 1] = (unsigned char)( i[0]>>6); 165 | w[ 2] = (unsigned char)( i[0]>>1); 166 | w[ 3] = (unsigned char)((i[1]>>12) + (i[0]<<4)); 167 | w[ 4] = (unsigned char)( i[1]>>7); 168 | w[ 5] = (unsigned char)( i[1]>>2); 169 | w[ 6] = (unsigned char)((i[2]>>13) + (i[1]<<3)); 170 | w[ 7] = (unsigned char)( i[2]>>8); 171 | w[ 8] = (unsigned char)( i[2]>>3); 172 | w[ 9] = (unsigned char)((i[3]>>14) + (i[2]<<2)); 173 | w[10] = (unsigned char)( i[3]>>9); 174 | w[11] = (unsigned char)( i[3]>>4); 175 | w[12] = (unsigned char)((i[4]>>15) + (i[3]<<1)); 176 | w[13] = (unsigned char)( i[4]>>10); 177 | w[14] = (unsigned char)( i[4]>>5); 178 | w[15] = (unsigned char)( i[4]); 179 | i+=5; 180 | w+=16; 181 | } 182 | for(unsigned int j=0; j>11); 184 | if(j== 1) w[ 1] = (unsigned char)( i[0]>>6); 185 | if(j== 2) w[ 2] = (unsigned char)( i[0]>>1); 186 | if(j== 3) w[ 3] = (unsigned char)((i[1]>>12) + (i[0]<<4)); 187 | if(j== 4) w[ 4] = (unsigned char)( i[1]>>7); 188 | if(j== 5) w[ 5] = (unsigned char)( i[1]>>2); 189 | if(j== 6) w[ 6] = (unsigned char)((i[2]>>13) + (i[1]<<3)); 190 | if(j== 7) w[ 7] = (unsigned char)( i[2]>>8); 191 | if(j== 8) w[ 8] = (unsigned char)( i[2]>>3); 192 | if(j== 9) w[ 9] = (unsigned char)((i[3]>>14) + (i[2]<<2)); 193 | if(j==10) w[10] = (unsigned char)( i[3]>>9); 194 | if(j==11) w[11] = (unsigned char)( i[3]>>4); 195 | if(j==12) w[12] = (unsigned char)((i[4]>>15) + (i[3]<<1)); 196 | if(j==13) w[13] = (unsigned char)( i[4]>>10); 197 | if(j==14) w[14] = (unsigned char)( i[4]>>5); 198 | if(j==15) w[15] = (unsigned char)( i[4]); 199 | } 200 | for(unsigned int j=0; j>(i*8)), (uint8_t)(lat64>>(i*8))); 219 | } 220 | 221 | int ret = GEOHASH_OK; 222 | if((ret=interleaved_to_geohashstr(interleaved, 8, lr, 26)) != GEOHASH_OK){ 223 | return ret; 224 | } 225 | lr[26] = '\0'; 226 | 227 | if(0>4); 282 | i[1] = (map[c[ 3]]<<12) + (map[c[ 4]]<< 7) + (map[c[ 5]]<<2) + (map[c[ 6]]>>3); 283 | i[2] = (map[c[ 6]]<<13) + (map[c[ 7]]<< 8) + (map[c[ 8]]<<3) + (map[c[ 9]]>>2); 284 | i[3] = (map[c[ 9]]<<14) + (map[c[10]]<< 9) + (map[c[11]]<<4) + (map[c[12]]>>1); 285 | i[4] = (map[c[12]]<<15) + (map[c[13]]<<10) + (map[c[14]]<<5) + (map[c[15]]>>0); 286 | i+=5; 287 | c+=16; 288 | } 289 | for(unsigned int j=0; j> 4; 294 | if(j== 3) i[1] = map[c[ 3]]<<12; 295 | if(j== 4) i[1] += map[c[ 4]]<< 7; 296 | if(j== 5) i[1] += map[c[ 5]]<< 2; 297 | if(j== 6) i[1] += map[c[ 6]]>> 3; 298 | if(j== 6) i[2] = map[c[ 6]]<<13; 299 | if(j== 7) i[2] += map[c[ 7]]<< 8; 300 | if(j== 8) i[2] += map[c[ 8]]<< 3; 301 | if(j== 9) i[2] += map[c[ 9]]>> 2; 302 | if(j== 9) i[3] = map[c[ 9]]<<14; 303 | if(j==10) i[3] += map[c[10]]<< 9; 304 | if(j==11) i[3] += map[c[11]]<< 4; 305 | if(j==12) i[3] += map[c[12]]>> 1; 306 | if(j==12) i[4] = map[c[12]]<<15; 307 | if(j==13) i[4] += map[c[13]]<<10; 308 | if(j==14) i[4] += map[c[14]]<< 5; 309 | if(j==15) i[4] += map[c[15]]>> 0; 310 | } 311 | return GEOHASH_OK; 312 | } 313 | 314 | /* 315 | (latitude, longitude) will be that of south west point. 316 | */ 317 | static int geohash_decode_impl(char* r, size_t length, double *latitude, double *longitude){ 318 | uint16_t intr_auto[8]; 319 | uint16_t *interleaved = intr_auto; 320 | size_t intr_length = length*5/16+1; 321 | int intr_free = 0; 322 | if(intr_length > 8){ 323 | interleaved = (uint16_t*)malloc(sizeof(uint16_t)*intr_length); 324 | if(!interleaved){ 325 | return GEOHASH_NOMEMORY; 326 | } 327 | intr_free = 1; 328 | }else{ 329 | intr_length = 8; 330 | } 331 | int ret = GEOHASH_OK; 332 | if((ret=geohashstr_to_interleaved(r, length, interleaved, intr_length)) != GEOHASH_OK){ 333 | return ret; 334 | } 335 | uint64_t lat64=0; 336 | uint64_t lon64=0; 337 | for(int i=0; i<8; i++){ 338 | uint8_t upper, lower; 339 | deinterleave(interleaved[i], &upper, &lower); 340 | lon64 = (lon64<<8)+upper; 341 | lat64 = (lat64<<8)+lower; 342 | } 343 | if(intr_free){ 344 | free(interleaved); 345 | } 346 | 347 | double t; 348 | 349 | i64_to_double(lat64, &t); 350 | *latitude = t*90.0; 351 | 352 | i64_to_double(lon64, &t); 353 | *longitude = t*180.0; 354 | 355 | return GEOHASH_OK; 356 | } 357 | int geohash_decode(char* r, size_t length, double *latitude, double *longitude){ 358 | return geohash_decode_impl(r, length, latitude, longitude); 359 | } 360 | 361 | /** 362 | * compare two uint8_t array of variable sized integers. 363 | */ 364 | static int uint8s_cmp(uint8_t *src, uint8_t *dst, size_t length){ 365 | if(length==0){ return 0; } 366 | unsigned int i=0; 367 | for(i=0; i<(length-1)/8; i++){ 368 | if(src[i] != dst[i]){ 369 | return (int)(src[i] - dst[i]); 370 | } 371 | } 372 | uint8_t cell_offset = (8-length%8)%8; 373 | return (int)((src[i]>>cell_offset) - (dst[i]>>cell_offset)); 374 | } 375 | 376 | /** 377 | * plus minus operations for uint8_t array of variable sized integer. 378 | */ 379 | static int uint8s_plus_minus(uint8_t *src, uint8_t *dst, size_t length, int plus){ 380 | if(length==0){ 381 | return 0; 382 | } 383 | unsigned int cell = (length-1)/8; 384 | for(unsigned int i=0; i0 && uint8s_cmp(lats[i-1], lats[i], lat_len)==0){ 459 | continue; 460 | } 461 | for(int j=0;j<3;j++){ 462 | if(j>0 && uint8s_cmp(lons[j-1], lons[j], lon_len)==0){ 463 | continue; 464 | } 465 | if(i==0 && j==0){ 466 | continue; 467 | } 468 | 469 | for(unsigned int k=0; k 539 | static void set_error(int status){ 540 | if(status==GEOHASH_NOTSUPPORTED) PyErr_SetString(PyExc_EnvironmentError, "Unknown endian"); 541 | if(status==GEOHASH_INVALIDCODE) PyErr_SetString(PyExc_ValueError, "geohash code is [0123456789bcdefghjkmnpqrstuvwxyz]+"); 542 | if(status==GEOHASH_INVALIDARGUMENT) PyErr_SetString(PyExc_ValueError, "Invalid argument"); 543 | if(status==GEOHASH_INTERNALERROR) PyErr_SetString(PyExc_EnvironmentError, "Internal error"); 544 | if(status==GEOHASH_NOMEMORY) PyErr_NoMemory(); 545 | } 546 | 547 | static PyObject *py_geohash_encode(PyObject *self, PyObject *args) { 548 | double latitude; 549 | double longitude; 550 | char hashcode[28]; 551 | int ret = GEOHASH_OK; 552 | 553 | if(!PyArg_ParseTuple(args, "dd", &latitude, &longitude)) return NULL; 554 | 555 | if((ret=geohash_encode_impl(latitude,longitude,hashcode,28))!=GEOHASH_OK){ 556 | set_error(ret); 557 | return NULL; 558 | } 559 | return Py_BuildValue("s",hashcode); 560 | } 561 | 562 | static PyObject *py_geohash_decode(PyObject *self, PyObject *args) { 563 | double latitude; 564 | double longitude; 565 | char *hashcode; 566 | int codelen=0; 567 | int ret = GEOHASH_OK; 568 | 569 | if(!PyArg_ParseTuple(args, "s", &hashcode)) return NULL; 570 | 571 | codelen = strlen(hashcode); 572 | if((ret=geohash_decode_impl(hashcode,codelen,&latitude,&longitude))!=GEOHASH_OK){ 573 | set_error(ret); 574 | return NULL; 575 | } 576 | return Py_BuildValue("(ddii)",latitude,longitude, codelen/2*5+codelen%2*2, codelen/2*5+codelen%2*3); 577 | } 578 | 579 | static PyObject *py_geohash_neighbors(PyObject *self, PyObject *args) { 580 | PyObject *obj = NULL; 581 | char *hashcode; 582 | if(!PyArg_ParseTuple(args, "s", &hashcode)) return NULL; 583 | 584 | size_t blen = strlen(hashcode)+1; 585 | size_t buffer_sz = blen*8; 586 | char *buffer = (char*)malloc(sizeof(char)*buffer_sz); 587 | if(buffer==NULL){ 588 | set_error(GEOHASH_NOMEMORY); 589 | return NULL; 590 | } 591 | int ret; 592 | int string_count = 0; 593 | if((ret = geo_neighbors_impl(hashcode, buffer, buffer_sz, &string_count)) != GEOHASH_OK){ 594 | set_error(ret); 595 | } 596 | 597 | if(string_count==0){ 598 | obj= Py_BuildValue("[]"); 599 | }else if(string_count==1){ 600 | obj= Py_BuildValue("[s]", buffer); 601 | }else if(string_count==3){ 602 | obj= Py_BuildValue("[sss]", buffer, buffer+blen, buffer+blen*2); 603 | }else if(string_count==5){ 604 | obj= Py_BuildValue("[sssss]", buffer, buffer+blen, buffer+blen*2, buffer+blen*3, buffer+blen*4); 605 | }else if(string_count==8){ 606 | obj= Py_BuildValue("[ssssssss]", buffer, buffer+blen, buffer+blen*2, buffer+blen*3, 607 | buffer+blen*4, buffer+blen*5, buffer+blen*6, buffer+blen*7); 608 | }else{ 609 | set_error(GEOHASH_INTERNALERROR); 610 | } 611 | free(buffer); 612 | 613 | return obj; 614 | } 615 | 616 | static PyObject *py_geoint_encode(PyObject *self, PyObject *args){ 617 | double latitude; 618 | double longitude; 619 | if(!PyArg_ParseTuple(args, "dd", &latitude, &longitude)) return NULL; 620 | 621 | uint64_t lat64, lon64; 622 | if(!double_to_i64(latitude/90.0, &lat64) || !double_to_i64(longitude/180.0, &lon64)){ 623 | return NULL; 624 | } 625 | uint16_t interleaved[8]; 626 | for(int i=0; i<8; i++){ 627 | interleaved[7-i] = interleave((uint8_t)(lon64>>(i*8)), (uint8_t)(lat64>>(i*8))); 628 | } 629 | 630 | PyObject *ret = NULL; 631 | #if UINT64_MAX <= ULLONG_MAX 632 | ret = PyTuple_New(2); 633 | unsigned PY_LONG_LONG li; 634 | li = ((unsigned PY_LONG_LONG)interleaved[0]<<48) + ((unsigned PY_LONG_LONG)interleaved[1]<<32) + ((unsigned PY_LONG_LONG)interleaved[2]<<16) + (unsigned PY_LONG_LONG)interleaved[3]; 635 | PyTuple_SET_ITEM(ret, 0, PyLong_FromUnsignedLongLong(li)); 636 | li = ((unsigned PY_LONG_LONG)interleaved[4]<<48) + ((unsigned PY_LONG_LONG)interleaved[5]<<32) + ((unsigned PY_LONG_LONG)interleaved[6]<<16) + (unsigned PY_LONG_LONG)interleaved[7]; 637 | PyTuple_SET_ITEM(ret, 1, PyLong_FromUnsignedLongLong(li)); 638 | #elif UINT32_MAX <= ULLONG_MAX 639 | ret = PyTuple_New(4); 640 | unsigned PY_LONG_LONG li; 641 | li = ((unsigned PY_LONG_LONG)interleaved[0]<<16) + (unsigned PY_LONG_LONG)interleaved[1]; 642 | PyTuple_SET_ITEM(ret, 0, PyLong_FromUnsignedLongLong(li)); 643 | li = ((unsigned PY_LONG_LONG)interleaved[1]<<16) + (unsigned PY_LONG_LONG)interleaved[3]; 644 | PyTuple_SET_ITEM(ret, 1, PyLong_FromUnsignedLongLong(li)); 645 | li = ((unsigned PY_LONG_LONG)interleaved[2]<<16) + (unsigned PY_LONG_LONG)interleaved[5]; 646 | PyTuple_SET_ITEM(ret, 2, PyLong_FromUnsignedLongLong(li)); 647 | li = ((unsigned PY_LONG_LONG)interleaved[3]<<16) + (unsigned PY_LONG_LONG)interleaved[7]; 648 | PyTuple_SET_ITEM(ret, 3, PyLong_FromUnsignedLongLong(li)); 649 | #elif UINT16_MAX <= ULLONG_MAX 650 | ret = PyTuple_New(8); 651 | PyTuple_SET_ITEM(ret, 0, PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG)interleaved[0])); 652 | PyTuple_SET_ITEM(ret, 1, PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG)interleaved[1])); 653 | PyTuple_SET_ITEM(ret, 2, PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG)interleaved[2])); 654 | PyTuple_SET_ITEM(ret, 3, PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG)interleaved[3])); 655 | PyTuple_SET_ITEM(ret, 4, PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG)interleaved[4])); 656 | PyTuple_SET_ITEM(ret, 5, PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG)interleaved[5])); 657 | PyTuple_SET_ITEM(ret, 6, PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG)interleaved[6])); 658 | PyTuple_SET_ITEM(ret, 7, PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG)interleaved[7])); 659 | #else 660 | #error "This platform not supported" 661 | #endif // UINT64_MAX <= ULLONG_MAX 662 | return ret; 663 | } 664 | 665 | static PyObject *py_geoint_decode(PyObject *self, PyObject *args){ 666 | uint16_t interleaved[8]; 667 | #if PY_MAJOR_VERSION >= 3 || (PY_MAJOR_VERSION >=2 && PY_MINOR_VERSION >= 5) 668 | Py_ssize_t sz = PyTuple_GET_SIZE(args); 669 | #else 670 | int sz = PyTuple_GET_SIZE(args); 671 | #endif 672 | if(sz==2){ 673 | unsigned PY_LONG_LONG lo; 674 | lo = PyLong_AsUnsignedLongLong(PyTuple_GET_ITEM(args,0)); 675 | interleaved[0] = (uint16_t)(lo>>48); 676 | interleaved[1] = (uint16_t)(lo>>32); 677 | interleaved[2] = (uint16_t)(lo>>16); 678 | interleaved[3] = (uint16_t)lo; 679 | lo = PyLong_AsUnsignedLongLong(PyTuple_GET_ITEM(args,1)); 680 | interleaved[4] = (uint16_t)(lo>>48); 681 | interleaved[5] = (uint16_t)(lo>>32); 682 | interleaved[6] = (uint16_t)(lo>>16); 683 | interleaved[7] = (uint16_t)lo; 684 | }else if(sz==4){ 685 | unsigned PY_LONG_LONG lo; 686 | lo = PyLong_AsUnsignedLongLong(PyTuple_GET_ITEM(args,0)); 687 | interleaved[0] = (uint16_t)(lo>>16); 688 | interleaved[1] = (uint16_t)lo; 689 | lo = PyLong_AsUnsignedLongLong(PyTuple_GET_ITEM(args,1)); 690 | interleaved[2] = (uint16_t)(lo>>16); 691 | interleaved[3] = (uint16_t)lo; 692 | lo = PyLong_AsUnsignedLongLong(PyTuple_GET_ITEM(args,2)); 693 | interleaved[4] = (uint16_t)(lo>>16); 694 | interleaved[5] = (uint16_t)lo; 695 | lo = PyLong_AsUnsignedLongLong(PyTuple_GET_ITEM(args,3)); 696 | interleaved[6] = (uint16_t)(lo>>16); 697 | interleaved[7] = (uint16_t)lo; 698 | }else if(sz==8){ 699 | interleaved[0] = (uint16_t)PyLong_AsUnsignedLongLong(PyTuple_GET_ITEM(args,0)); 700 | interleaved[1] = (uint16_t)PyLong_AsUnsignedLongLong(PyTuple_GET_ITEM(args,1)); 701 | interleaved[2] = (uint16_t)PyLong_AsUnsignedLongLong(PyTuple_GET_ITEM(args,2)); 702 | interleaved[3] = (uint16_t)PyLong_AsUnsignedLongLong(PyTuple_GET_ITEM(args,3)); 703 | interleaved[4] = (uint16_t)PyLong_AsUnsignedLongLong(PyTuple_GET_ITEM(args,4)); 704 | interleaved[5] = (uint16_t)PyLong_AsUnsignedLongLong(PyTuple_GET_ITEM(args,5)); 705 | interleaved[6] = (uint16_t)PyLong_AsUnsignedLongLong(PyTuple_GET_ITEM(args,6)); 706 | interleaved[7] = (uint16_t)PyLong_AsUnsignedLongLong(PyTuple_GET_ITEM(args,7)); 707 | }else{ 708 | PyErr_SetString(PyExc_ValueError, "Argument must be 2, 4 or 8 integers."); 709 | return NULL; 710 | } 711 | 712 | uint64_t lat64=0; 713 | uint64_t lon64=0; 714 | for(int i=0; i<8; i++){ 715 | uint8_t upper, lower; 716 | deinterleave(interleaved[i], &upper, &lower); 717 | lon64 = (lon64<<8)+upper; 718 | lat64 = (lat64<<8)+lower; 719 | } 720 | double tlat, tlon; 721 | i64_to_double(lat64, &tlat); 722 | i64_to_double(lon64, &tlon); 723 | 724 | return Py_BuildValue("(dd)", tlat*90.0, tlon*180.0); 725 | } 726 | 727 | static PyMethodDef GeohashMethods[] = { 728 | {"encode", py_geohash_encode, METH_VARARGS, "geohash encoding."}, 729 | {"decode", py_geohash_decode, METH_VARARGS, "geohash decoding."}, 730 | {"neighbors", py_geohash_neighbors, METH_VARARGS, "geohash neighbor codes",}, 731 | {"encode_int", py_geoint_encode, METH_VARARGS, "encode geometric coordinates into 128bit interleaved integer(divided into some integers)"}, 732 | {"decode_int", py_geoint_decode, METH_VARARGS, "decode 128bit interleaved integer(divided into some integers) into geometric coordinates"}, 733 | {NULL, NULL, 0, NULL} 734 | }; 735 | 736 | #ifdef Py_InitModule 737 | PyMODINIT_FUNC init_geohash(void){ 738 | PyObject *mod = Py_InitModule("_geohash", GeohashMethods); 739 | #if UINT64_MAX <= ULLONG_MAX 740 | PyModule_AddIntConstant(mod, "intunit", 64); 741 | #elif UINT32_MAX <= ULLONG_MAX 742 | PyModule_AddIntConstant(mod, "intunit", 32); 743 | #elif UINT16_MAX <= ULLONG_MAX 744 | PyModule_AddIntConstant(mod, "intunit", 16); 745 | #else 746 | #error "This platform not supported" 747 | #endif // UINT64_MAX <= ULLONG_MAX 748 | return (void)mod; 749 | } 750 | #else 751 | PyDoc_STRVAR(module_doc, "geohash speedups"); 752 | 753 | static struct PyModuleDef geohash_moduledef = { 754 | PyModuleDef_HEAD_INIT, 755 | "_geohash", 756 | module_doc, 757 | -1, 758 | GeohashMethods, 759 | NULL, 760 | NULL, 761 | NULL, 762 | NULL 763 | }; 764 | PyMODINIT_FUNC PyInit__geohash(void){ 765 | PyObject *mod = PyModule_Create(&geohash_moduledef); 766 | #if UINT64_MAX <= ULLONG_MAX 767 | PyModule_AddIntConstant(mod, "intunit", 64); 768 | #elif UINT32_MAX <= ULLONG_MAX 769 | PyModule_AddIntConstant(mod, "intunit", 32); 770 | #elif UINT16_MAX <= ULLONG_MAX 771 | PyModule_AddIntConstant(mod, "intunit", 16); 772 | #else 773 | #error "This platform not supported" 774 | #endif // UINT64_MAX <= ULLONG_MAX 775 | return mod; 776 | }; 777 | #endif /* Py_InitModule */ 778 | 779 | #endif /* PYTHON_MODULE */ 780 | -------------------------------------------------------------------------------- /src/geohash.h: -------------------------------------------------------------------------------- 1 | #ifdef __cplusplus 2 | extern "C" { 3 | #endif 4 | 5 | enum { 6 | GEOHASH_OK, 7 | GEOHASH_NOTSUPPORTED, 8 | GEOHASH_INVALIDCODE, 9 | GEOHASH_INVALIDARGUMENT, 10 | GEOHASH_INTERNALERROR, 11 | GEOHASH_NOMEMORY 12 | }; 13 | 14 | int geohash_encode(double latitude, double longitude, char* r, size_t capacity); 15 | int geohash_decode(char* r, size_t length, double *latitude, double *longitude); 16 | int geo_neighbors(char *hashcode, char* dst, size_t dst_length, int *string_count); 17 | 18 | #ifdef __cplusplus 19 | } 20 | #endif 21 | 22 | 23 | /* 24 | int main(int argc,char* argv[]){ 25 | char r[23]; 26 | double lat,lon; 27 | if(geohash_encode(35.0, 135.0, r, 23)==GEOHASH_OK){ 28 | printf("%s\n",r); 29 | } 30 | if(geohash_decode(r,23,&lat,&lon)==GEOHASH_OK){ 31 | printf("%f %f\n",lat,lon); 32 | } 33 | } 34 | */ 35 | -------------------------------------------------------------------------------- /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/perf.py: -------------------------------------------------------------------------------- 1 | import random 2 | import geohash 3 | import time 4 | import sys 5 | 6 | os = [] 7 | for i in range(100000): 8 | o = ((random.random()*2 - 1.0)*90.0, (random.random()*2 - 1.0)*180.0) 9 | os.append(o) 10 | 11 | ds = [] 12 | tmstart = time.time() 13 | for i in range(100000): 14 | ds.append(geohash.encode(*os[i])) 15 | 16 | sys.stdout.write("encode %f sec\n" % (time.time()-tmstart,)) 17 | 18 | tmstart = time.time() 19 | for i in range(100000): 20 | geohash.decode(ds[i]) 21 | 22 | sys.stdout.write("decode %f sec\n" % (time.time()-tmstart,)) 23 | -------------------------------------------------------------------------------- /test/test_geohash.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 | class TestDecode(unittest.TestCase): 10 | def test_empty(self): 11 | self.assertEqual( 12 | geohash.bbox(''), 13 | {'s':-90.0, 'n':90.0, 'w':-180.0, 'e':180.0}) 14 | 15 | def test_one(self): 16 | seq = '0123456789bcdefghjkmnpqrstuvwxyz' 17 | sws = [ 18 | (-90.0, -180.0), 19 | (-90.0, -135.0), 20 | (-45.0, -180.0), 21 | (-45.0, -135.0), 22 | (-90.0, -90.0), 23 | (-90.0, -45.0), 24 | (-45.0, -90.0), 25 | (-45.0, -45.0), 26 | (0.0, -180.0), 27 | (0.0, -135.0), 28 | (45.0, -180.0), 29 | (45.0, -135.0), 30 | (0.0, -90.0), 31 | (0.0, -45.0), 32 | (45.0, -90.0), 33 | (45.0, -45.0), 34 | (-90.0, 0.0), 35 | (-90.0, 45.0), 36 | (-45.0, 0.0), 37 | (-45.0, 45.0), 38 | (-90.0, 90.0), 39 | (-90.0, 135.0), 40 | (-45.0, 90.0), 41 | (-45.0, 135.0), 42 | (0.0, 0.0), 43 | (0.0, 45.0), 44 | (45.0, 0.0), 45 | (45.0, 45.0), 46 | (0.0, 90.0), 47 | (0.0, 135.0), 48 | (45.0, 90.0), 49 | (45.0, 135.0) 50 | ] 51 | for i in zip(seq, sws): 52 | x = geohash.bbox(i[0]) 53 | self.assertEqual((x['s'], x['w']), i[1]) 54 | self.assertEqual(x['n']-x['s'], 45) 55 | self.assertEqual(x['e']-x['w'], 45) 56 | 57 | def test_ezs42(self): 58 | x=geohash.bbox('ezs42') 59 | self.assertEqual(round(x['s'],3), 42.583) 60 | self.assertEqual(round(x['n'],3), 42.627) 61 | 62 | def test_issue12(self): 63 | ll=geohash.decode(geohash.encode(51.566141,-0.009434,24)) 64 | self.assertAlmostEqual(ll[0], 51.566141) 65 | self.assertAlmostEqual(ll[1], -0.009434) 66 | 67 | class TestNeighbors(unittest.TestCase): 68 | def test_empty(self): 69 | self.assertEqual([], geohash.neighbors("")) 70 | 71 | def test_one(self): 72 | self.assertEqual(set(['1', '2', '3', 'p', 'r']), set(geohash.neighbors("0"))) 73 | self.assertEqual(set(['w', 'x', 'y', '8', 'b']), set(geohash.neighbors("z"))) 74 | self.assertEqual(set(['2', '6', '1', '0', '4', '9', '8', 'd']), set(geohash.neighbors("3"))) 75 | 76 | if __name__=='__main__': 77 | unittest.main() 78 | -------------------------------------------------------------------------------- /test/test_jpgrid.py: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | import unittest 3 | import jpgrid 4 | 5 | class TestReference(unittest.TestCase): 6 | def test_lv1(self): 7 | self.assertEqual("5438", jpgrid.encodeLv1(36,138)) 8 | 9 | def test_prefectures(self): 10 | # http://www.stat.go.jp/data/mesh/pdf/gaiyo1.pdf 11 | # page. 15 12 | def dms(d,m,s): 13 | return float(d) + (float(m) + float(s)/60)/60.0 14 | 15 | dataset = [ 16 | ("北海道", "札幌市", "6441-42-77", ("43","03","30"), ("141","20","15")), 17 | ("青森県", "青森市", "6140-15-89", ("40","49","00"), ("140","44","15")), 18 | ("岩手県", "盛岡市", "5941-41-42", ("39","42","00"), ("141","09","00")), 19 | ("宮城県", "仙台市", "5740-36-29", ("38","16","00"), ("140","51","45")), 20 | ("秋田県", "秋田市", "5940-40-68", ("39","43","00"), ("140","06","00")), 21 | ("山形県", "山形市", "5740-22-89", ("38","14","00"), ("140","21","45")), 22 | ("福島県", "福島市", "5640-53-07", ("37","45","00"), ("140","27","45")), 23 | ("茨城県", "水戸市", "5440-43-15", ("36","20","30"), ("140","26","15")), 24 | ("栃木県", "宇都宮市", "5439-67-70", ("36","33","30"), ("139","52","30")), 25 | ("群馬県", "前橋市", "5439-40-64", ("36","23","00"), ("139","03","00")), 26 | ("埼玉県", "さいたま市", "5339-65-21", ("35","51","00"), ("139","38","15")), 27 | ("千葉県", "千葉市", "5340-30-29", ("35","36","00"), ("140","06","45")), 28 | ("東京都", "新宿区", "5339-45-25", ("35","41","00"), ("139","41","15")), 29 | ("神奈川県", "横浜市", "5339-15-31", ("35","26","30"), ("139","38","15")), 30 | ("新潟県", "新潟市", "5639-60-81", ("37","54","00"), ("139","00","45")), 31 | ("富山県", "富山市", "5537-01-36", ("36","41","30"), ("137","12","00")), 32 | ("石川県", "金沢市", "5436-75-10", ("36","35","30"), ("136","37","30")), 33 | ("福井県", "福井市", "5436-01-77", ("36","03","30"), ("136","12","45")), 34 | ("山梨県", "甲府市", "5338-34-95", ("35","39","30"), ("138","33","45")), 35 | ("長野県", "長野市", "5438-71-84", ("36","39","00"), ("138","10","30")), 36 | ("岐阜県", "岐阜市", "5336-05-67", ("35","23","00"), ("136","42","45")), 37 | ("静岡県", "静岡市", "5238-33-70", ("34","58","30"), ("138","22","30")), 38 | ("愛知県", "名古屋市", "5236-67-12", ("35","10","30"), ("136","54","00")), 39 | ("三重県", "津市", "5236-04-70", ("34","43","30"), ("136","30","00")), 40 | ("滋賀県", "大津市", "5235-46-09", ("35","00","00"), ("135","51","45")), 41 | ("京都府", "京都市", "5235-46-20", ("35","01","00"), ("135","45","00")), 42 | ("大阪府", "大阪市", "5235-04-21", ("34","41","00"), ("135","30","45")), 43 | ("兵庫県", "神戸市", "5235-01-24", ("34","41","00"), ("135","10","30")), 44 | ("奈良県", "奈良市", "5235-06-26", ("34","41","00"), ("135","49","30")), 45 | ("和歌山県", "和歌山市", "5135-21-73", ("34","13","30"), ("135","09","45")), 46 | ("鳥取県", "鳥取市", "5334-21-09", ("35","30","00"), ("134","14","15")), 47 | ("島根県", "松江市", "5333-10-64", ("35","28","00"), ("133","03","00")), 48 | ("岡山県", "岡山市", "5133-77-94", ("34","39","30"), ("133","55","30")), 49 | ("広島県", "広島市", "5132-43-76", ("34","23","30"), ("132","27","00")), 50 | ("山口県", "山口市", "5131-23-27", ("34","11","00"), ("131","27","45")), 51 | ("徳島県", "徳島市", "5134-04-74", ("34","03","30"), ("134","33","00")), 52 | ("香川県", "高松市", "5134-40-03", ("34","20","00"), ("134","02","15")), 53 | ("愛媛県", "松山市", "5032-66-01", ("33","50","00"), ("132","45","45")), 54 | ("高知県", "高知市", "5033-24-72", ("33","33","30"), ("133","31","30")), 55 | ("福岡県", "福岡市", "5030-33-23", ("33","36","00"), ("130","24","45")), 56 | ("佐賀県", "佐賀市", "4930-62-93", ("33","14","30"), ("130","17","15")), 57 | ("長崎県", "長崎市", "4929-06-99", ("32","44","30"), ("129","51","45")), 58 | ("熊本県", "熊本市", "4930-15-49", ("32","47","00"), ("130","44","15")), 59 | ("大分県", "大分市", "4931-64-89", ("33","14","00"), ("131","36","45")), 60 | ("宮崎県", "宮崎市", "4731-63-93", ("31","54","30"), ("131","24","45")), 61 | ("鹿児島県", "鹿児島市", "4730-24-74", ("31","33","30"), ("130","33","00")), 62 | ("沖縄県", "那覇市", "3927-25-54", ("26","12","30"), ("127","40","30")) 63 | ] 64 | 65 | for data in dataset: 66 | # encode 67 | self.assertEqual(data[2].replace("-",""), jpgrid.encode(dms(*data[3]), dms(*data[4]))) 68 | t = jpgrid.decode(data[2].replace("-","")) 69 | # decode 70 | self.assertTrue(t[0] > dms(*data[3])) 71 | self.assertTrue(t[0] < dms(*data[3]) + 1.5/80.0) 72 | self.assertTrue(t[1] > dms(*data[4])) 73 | self.assertTrue(t[1] < dms(*data[4]) + 1.0/80.0) 74 | 75 | if __name__=='__main__': 76 | unittest.main() 77 | -------------------------------------------------------------------------------- /test/test_jpiarea.py: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | import unittest 3 | import jpiarea 4 | 5 | def dms(d,m,s): 6 | return float(d) + (float(m) + float(s)/60)/60.0 7 | 8 | class TestReference(unittest.TestCase): 9 | # hash code examples from open iarea document 10 | # http://www.nttdocomo.co.jp/service/imode/make/content/iarea/domestic/index.html 11 | def test_lv1(self): 12 | self.assertEqual("5438", jpiarea.encode(36,138)[0:4]) 13 | self.assertEqual("5637", jpiarea.encode(dms(37,20,0),137)[0:4]) 14 | 15 | def test_lv2(self): 16 | p = jpiarea.bbox("533946") 17 | self.assertAlmostEqual(503100000, p["s"]*3600*1000) 18 | self.assertAlmostEqual(503550000, p["n"]*3600*1000) 19 | self.assertAlmostEqual(128400000, p["w"]*3600*1000) 20 | self.assertAlmostEqual(128700000, p["e"]*3600*1000) 21 | 22 | def test_lv3(self): 23 | p = jpiarea.bbox("5339463") 24 | self.assertAlmostEqual(503325000, p["s"]*3600*1000) 25 | self.assertAlmostEqual(503550000, p["n"]*3600*1000) 26 | self.assertAlmostEqual(128550000, p["w"]*3600*1000) 27 | self.assertAlmostEqual(128700000, p["e"]*3600*1000) 28 | 29 | def test_lvN(self): 30 | self.assertEqual("53394600300",jpiarea.encode(dms(35,40,41), dms(139,46,9.527))) 31 | 32 | if __name__=='__main__': 33 | unittest.main() 34 | -------------------------------------------------------------------------------- /test/test_uint64.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import geohash 3 | 4 | class TestUint64(unittest.TestCase): 5 | def test_one(self): 6 | dataset = [ 7 | (0x0000000000000000, -90.0, -180.0), 8 | (0x0800000000000000, -90.0, -135.0), 9 | (0x1000000000000000, -45.0, -180.0), 10 | (0x1800000000000000, -45.0, -135.0), 11 | (0x2000000000000000, -90.0, -90.0), 12 | (0x2800000000000000, -90.0, -45.0), 13 | (0x3000000000000000, -45.0, -90.0), 14 | (0x3800000000000000, -45.0, -45.0), 15 | (0x4000000000000000, 0.0, -180.0), 16 | (0x4800000000000000, 0.0, -135.0), 17 | (0x5000000000000000, 45.0, -180.0), 18 | (0x5800000000000000, 45.0, -135.0), 19 | (0x6000000000000000, 0.0, -90.0), 20 | (0x6800000000000000, 0.0, -45.0), 21 | (0x7000000000000000, 45.0, -90.0), 22 | (0x7800000000000000, 45.0, -45.0), 23 | (0x8000000000000000, -90.0, 0.0), 24 | (0x8800000000000000, -90.0, 45.0), 25 | (0x9000000000000000, -45.0, 0.0), 26 | (0x9800000000000000, -45.0, 45.0), 27 | (0xA000000000000000, -90.0, 90.0), 28 | (0xA800000000000000, -90.0, 135.0), 29 | (0xB000000000000000, -45.0, 90.0), 30 | (0xB800000000000000, -45.0, 135.0), 31 | (0xC000000000000000, 0.0, 0.0), 32 | (0xC800000000000000, 0.0, 45.0), 33 | (0xD000000000000000, 45.0, 0.0), 34 | (0xD800000000000000, 45.0, 45.0), 35 | (0xE000000000000000, 0.0, 90.0), 36 | (0xE800000000000000, 0.0, 135.0), 37 | (0xF000000000000000, 45.0, 90.0), 38 | (0xF800000000000000, 45.0, 135.0) 39 | ] 40 | for data in dataset: 41 | self.assertEqual(data[0], geohash.encode_uint64(data[1], data[2])) 42 | latlon = geohash.decode_uint64(data[0]) 43 | self.assertEqual(latlon[0], data[1]) 44 | self.assertEqual(latlon[1], data[2]) 45 | 46 | if __name__=='__main__': 47 | unittest.main() 48 | --------------------------------------------------------------------------------