* update bleve to master b17287a86f6cac923a5d886e10618df994eeb54b6724eac2e3b8dde89cfbe3a2 * remove unused pkg from dep file * change bleve from master to recent revision
		
			
				
	
	
		
			175 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			175 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // The code here was obtained from:
 | |
| //   https://github.com/mmcloughlin/geohash
 | |
| 
 | |
| // The MIT License (MIT)
 | |
| // Copyright (c) 2015 Michael McLoughlin
 | |
| // Permission is hereby granted, free of charge, to any person obtaining a copy
 | |
| // of this software and associated documentation files (the "Software"), to deal
 | |
| // in the Software without restriction, including without limitation the rights
 | |
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | |
| // copies of the Software, and to permit persons to whom the Software is
 | |
| // furnished to do so, subject to the following conditions:
 | |
| 
 | |
| // The above copyright notice and this permission notice shall be included in all
 | |
| // copies or substantial portions of the Software.
 | |
| 
 | |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | |
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | |
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | |
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | |
| // SOFTWARE.
 | |
| 
 | |
| package geo
 | |
| 
 | |
| import (
 | |
| 	"math"
 | |
| )
 | |
| 
 | |
| // encoding encapsulates an encoding defined by a given base32 alphabet.
 | |
| type encoding struct {
 | |
| 	enc string
 | |
| 	dec [256]byte
 | |
| }
 | |
| 
 | |
| // newEncoding constructs a new encoding defined by the given alphabet,
 | |
| // which must be a 32-byte string.
 | |
| func newEncoding(encoder string) *encoding {
 | |
| 	e := new(encoding)
 | |
| 	e.enc = encoder
 | |
| 	for i := 0; i < len(e.dec); i++ {
 | |
| 		e.dec[i] = 0xff
 | |
| 	}
 | |
| 	for i := 0; i < len(encoder); i++ {
 | |
| 		e.dec[encoder[i]] = byte(i)
 | |
| 	}
 | |
| 	return e
 | |
| }
 | |
| 
 | |
| // Decode string into bits of a 64-bit word. The string s may be at most 12
 | |
| // characters.
 | |
| func (e *encoding) decode(s string) uint64 {
 | |
| 	x := uint64(0)
 | |
| 	for i := 0; i < len(s); i++ {
 | |
| 		x = (x << 5) | uint64(e.dec[s[i]])
 | |
| 	}
 | |
| 	return x
 | |
| }
 | |
| 
 | |
| // Encode bits of 64-bit word into a string.
 | |
| func (e *encoding) encode(x uint64) string {
 | |
| 	b := [12]byte{}
 | |
| 	for i := 0; i < 12; i++ {
 | |
| 		b[11-i] = e.enc[x&0x1f]
 | |
| 		x >>= 5
 | |
| 	}
 | |
| 	return string(b[:])
 | |
| }
 | |
| 
 | |
| // Base32Encoding with the Geohash alphabet.
 | |
| var base32encoding = newEncoding("0123456789bcdefghjkmnpqrstuvwxyz")
 | |
| 
 | |
| // BoundingBox returns the region encoded by the given string geohash.
 | |
| func geoBoundingBox(hash string) geoBox {
 | |
| 	bits := uint(5 * len(hash))
 | |
| 	inthash := base32encoding.decode(hash)
 | |
| 	return geoBoundingBoxIntWithPrecision(inthash, bits)
 | |
| }
 | |
| 
 | |
| // Box represents a rectangle in latitude/longitude space.
 | |
| type geoBox struct {
 | |
| 	minLat float64
 | |
| 	maxLat float64
 | |
| 	minLng float64
 | |
| 	maxLng float64
 | |
| }
 | |
| 
 | |
| // Round returns a point inside the box, making an effort to round to minimal
 | |
| // precision.
 | |
| func (b geoBox) round() (lat, lng float64) {
 | |
| 	x := maxDecimalPower(b.maxLat - b.minLat)
 | |
| 	lat = math.Ceil(b.minLat/x) * x
 | |
| 	x = maxDecimalPower(b.maxLng - b.minLng)
 | |
| 	lng = math.Ceil(b.minLng/x) * x
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // precalculated for performance
 | |
| var exp232 = math.Exp2(32)
 | |
| 
 | |
| // errorWithPrecision returns the error range in latitude and longitude for in
 | |
| // integer geohash with bits of precision.
 | |
| func errorWithPrecision(bits uint) (latErr, lngErr float64) {
 | |
| 	b := int(bits)
 | |
| 	latBits := b / 2
 | |
| 	lngBits := b - latBits
 | |
| 	latErr = math.Ldexp(180.0, -latBits)
 | |
| 	lngErr = math.Ldexp(360.0, -lngBits)
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // minDecimalPlaces returns the minimum number of decimal places such that
 | |
| // there must exist an number with that many places within any range of width
 | |
| // r. This is intended for returning minimal precision coordinates inside a
 | |
| // box.
 | |
| func maxDecimalPower(r float64) float64 {
 | |
| 	m := int(math.Floor(math.Log10(r)))
 | |
| 	return math.Pow10(m)
 | |
| }
 | |
| 
 | |
| // Encode the position of x within the range -r to +r as a 32-bit integer.
 | |
| func encodeRange(x, r float64) uint32 {
 | |
| 	p := (x + r) / (2 * r)
 | |
| 	return uint32(p * exp232)
 | |
| }
 | |
| 
 | |
| // Decode the 32-bit range encoding X back to a value in the range -r to +r.
 | |
| func decodeRange(X uint32, r float64) float64 {
 | |
| 	p := float64(X) / exp232
 | |
| 	x := 2*r*p - r
 | |
| 	return x
 | |
| }
 | |
| 
 | |
| // Squash the even bitlevels of X into a 32-bit word. Odd bitlevels of X are
 | |
| // ignored, and may take any value.
 | |
| func squash(X uint64) uint32 {
 | |
| 	X &= 0x5555555555555555
 | |
| 	X = (X | (X >> 1)) & 0x3333333333333333
 | |
| 	X = (X | (X >> 2)) & 0x0f0f0f0f0f0f0f0f
 | |
| 	X = (X | (X >> 4)) & 0x00ff00ff00ff00ff
 | |
| 	X = (X | (X >> 8)) & 0x0000ffff0000ffff
 | |
| 	X = (X | (X >> 16)) & 0x00000000ffffffff
 | |
| 	return uint32(X)
 | |
| }
 | |
| 
 | |
| // Deinterleave the bits of X into 32-bit words containing the even and odd
 | |
| // bitlevels of X, respectively.
 | |
| func deinterleave(X uint64) (uint32, uint32) {
 | |
| 	return squash(X), squash(X >> 1)
 | |
| }
 | |
| 
 | |
| // BoundingBoxIntWithPrecision returns the region encoded by the integer
 | |
| // geohash with the specified precision.
 | |
| func geoBoundingBoxIntWithPrecision(hash uint64, bits uint) geoBox {
 | |
| 	fullHash := hash << (64 - bits)
 | |
| 	latInt, lngInt := deinterleave(fullHash)
 | |
| 	lat := decodeRange(latInt, 90)
 | |
| 	lng := decodeRange(lngInt, 180)
 | |
| 	latErr, lngErr := errorWithPrecision(bits)
 | |
| 	return geoBox{
 | |
| 		minLat: lat,
 | |
| 		maxLat: lat + latErr,
 | |
| 		minLng: lng,
 | |
| 		maxLng: lng + lngErr,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // ----------------------------------------------------------------------
 | |
| 
 | |
| // Decode the string geohash to a (lat, lng) point.
 | |
| func GeoHashDecode(hash string) (lat, lng float64) {
 | |
| 	box := geoBoundingBox(hash)
 | |
| 	return box.round()
 | |
| }
 |