// 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() }