112 lines
2.1 KiB
Go
112 lines
2.1 KiB
Go
|
// 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()
|
||
|
|
||
|
}
|