summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/blevesearch/bleve/geo/geohash.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/blevesearch/bleve/geo/geohash.go')
-rw-r--r--vendor/github.com/blevesearch/bleve/geo/geohash.go174
1 files changed, 174 insertions, 0 deletions
diff --git a/vendor/github.com/blevesearch/bleve/geo/geohash.go b/vendor/github.com/blevesearch/bleve/geo/geohash.go
new file mode 100644
index 0000000000..35db720c0f
--- /dev/null
+++ b/vendor/github.com/blevesearch/bleve/geo/geohash.go
@@ -0,0 +1,174 @@
+// 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()
+}