2019-11-27 12:23:33 +03:00
|
|
|
// Copyright (c) 2019 Couchbase, Inc.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
// This implementation is inspired from the geohash-js
|
|
|
|
// ref: https://github.com/davetroy/geohash-js
|
2019-02-18 03:50:26 +03:00
|
|
|
|
|
|
|
package geo
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2019-11-27 12:23:33 +03:00
|
|
|
// base32encoding with the Geohash alphabet.
|
2019-02-18 03:50:26 +03:00
|
|
|
var base32encoding = newEncoding("0123456789bcdefghjkmnpqrstuvwxyz")
|
|
|
|
|
2019-11-27 12:23:33 +03:00
|
|
|
var masks = []uint64{16, 8, 4, 2, 1}
|
|
|
|
|
|
|
|
// DecodeGeoHash decodes the string geohash faster with
|
|
|
|
// higher precision. This api is in experimental phase.
|
|
|
|
func DecodeGeoHash(geoHash string) (float64, float64) {
|
|
|
|
even := true
|
|
|
|
lat := []float64{-90.0, 90.0}
|
|
|
|
lon := []float64{-180.0, 180.0}
|
|
|
|
|
|
|
|
for i := 0; i < len(geoHash); i++ {
|
|
|
|
cd := uint64(base32encoding.dec[geoHash[i]])
|
|
|
|
for j := 0; j < 5; j++ {
|
|
|
|
if even {
|
|
|
|
if cd&masks[j] > 0 {
|
|
|
|
lon[0] = (lon[0] + lon[1]) / 2
|
|
|
|
} else {
|
|
|
|
lon[1] = (lon[0] + lon[1]) / 2
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if cd&masks[j] > 0 {
|
|
|
|
lat[0] = (lat[0] + lat[1]) / 2
|
|
|
|
} else {
|
|
|
|
lat[1] = (lat[0] + lat[1]) / 2
|
|
|
|
}
|
|
|
|
}
|
|
|
|
even = !even
|
|
|
|
}
|
|
|
|
}
|
2019-02-18 03:50:26 +03:00
|
|
|
|
2019-11-27 12:23:33 +03:00
|
|
|
return (lat[0] + lat[1]) / 2, (lon[0] + lon[1]) / 2
|
2019-02-18 03:50:26 +03:00
|
|
|
}
|
|
|
|
|
2019-11-27 12:23:33 +03:00
|
|
|
func EncodeGeoHash(lat, lon float64) string {
|
|
|
|
even := true
|
|
|
|
lats := []float64{-90.0, 90.0}
|
|
|
|
lons := []float64{-180.0, 180.0}
|
|
|
|
precision := 12
|
|
|
|
var ch, bit uint64
|
|
|
|
var geoHash string
|
|
|
|
|
|
|
|
for len(geoHash) < precision {
|
|
|
|
if even {
|
|
|
|
mid := (lons[0] + lons[1]) / 2
|
|
|
|
if lon > mid {
|
|
|
|
ch |= masks[bit]
|
|
|
|
lons[0] = mid
|
|
|
|
} else {
|
|
|
|
lons[1] = mid
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mid := (lats[0] + lats[1]) / 2
|
|
|
|
if lat > mid {
|
|
|
|
ch |= masks[bit]
|
|
|
|
lats[0] = mid
|
|
|
|
} else {
|
|
|
|
lats[1] = mid
|
|
|
|
}
|
|
|
|
}
|
|
|
|
even = !even
|
|
|
|
if bit < 4 {
|
|
|
|
bit++
|
|
|
|
} else {
|
|
|
|
geoHash += string(base32encoding.enc[ch])
|
|
|
|
ch = 0
|
|
|
|
bit = 0
|
|
|
|
}
|
2019-02-18 03:50:26 +03:00
|
|
|
}
|
|
|
|
|
2019-11-27 12:23:33 +03:00
|
|
|
return geoHash
|
2019-02-18 03:50:26 +03:00
|
|
|
}
|