123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111 |
- // 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
- 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
- }
- // base32encoding with the Geohash alphabet.
- var base32encoding = newEncoding("0123456789bcdefghjkmnpqrstuvwxyz")
- 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
- }
- }
- return (lat[0] + lat[1]) / 2, (lon[0] + lon[1]) / 2
- }
- 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
- }
- }
- return geoHash
- }
|