surrealpatch/util/geof/geohash.go

112 lines
2.1 KiB
Go
Raw Normal View History

// Copyright © 2016 Abcum Ltd
//
// 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.
package geof
import (
"bytes"
"github.com/abcum/surreal/sql"
)
var bits = []int{16, 8, 4, 2, 1}
var latmax = []float64{-90, 90}
var lngmax = []float64{-180, 180}
var base32 = []byte("0123456789bcdefghjkmnpqrstuvwxyz")
func refine(interval []float64, cd, mask int) []float64 {
if cd&mask > 0 {
interval[0] = (interval[0] + interval[1]) / 2
} else {
interval[1] = (interval[0] + interval[1]) / 2
}
return interval
}
func GeohashDecode(hash string) *sql.Point {
isEven := true
lat := latmax
lng := lngmax
latErr := float64(90)
lngErr := float64(180)
var c string
var cd int
for i := 0; i < len(hash); i++ {
c = hash[i : i+1]
cd = bytes.Index(base32, []byte(c))
for j := 0; j < 5; j++ {
if isEven {
lngErr /= 2
lng = refine(lng, cd, bits[j])
} else {
latErr /= 2
lat = refine(lat, cd, bits[j])
}
isEven = !isEven
}
}
return sql.NewPoint(
(lat[0]+lat[1])/2,
(lng[0]+lng[1])/2,
)
}
func GeohashEncode(point *sql.Point, precision int64) string {
isEven := true
lat := []float64{-90, 90}
lng := []float64{-180, 180}
bit := 0
ch := 0
var geohash bytes.Buffer
var mid float64
for geohash.Len() < int(precision) {
if isEven {
mid = (lng[0] + lng[1]) / 2
if point.LO > mid {
ch |= bits[bit]
lng[0] = mid
} else {
lng[1] = mid
}
} else {
mid = (lat[0] + lat[1]) / 2
if point.LA > mid {
ch |= bits[bit]
lat[0] = mid
} else {
lat[1] = mid
}
}
isEven = !isEven
if bit < 4 {
bit++
} else {
geohash.WriteByte(base32[ch])
bit = 0
ch = 0
}
}
return geohash.String()
}