photoprism-client-go/vendor/github.com/google/open-location-code/go/decode.go

102 lines
3.3 KiB
Go
Raw Normal View History

// Copyright 2015 Tamás Gulácsi. All rights reserved.
//
// 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 olc
import (
"errors"
"math"
"strings"
)
// Decode decodes an Open Location Code into the location coordinates.
// Returns a CodeArea object that includes the coordinates of the bounding
// box - the lower left, center and upper right.
//
// To avoid underflow errors, the precision is limited to 15 digits.
// Longer codes are allowed, but only the first 15 is decoded.
func Decode(code string) (CodeArea, error) {
var area CodeArea
if err := CheckFull(code); err != nil {
return area, err
}
// Strip out separator character (we've already established the code is
// valid so the maximum is one), padding characters and convert to upper
// case.
code = StripCode(code)
if len(code) < 2 {
return area, errors.New("code too short")
}
// Initialise the values for each section. We work them out as integers and
// convert them to floats at the end.
normalLat := -latMax * pairPrecision
normalLng := -lngMax * pairPrecision
extraLat := 0
extraLng := 0
// How many digits do we have to process?
digits := pairCodeLen
if len(code) < digits {
digits = len(code)
}
// Define the place value for the most significant pair.
pv := pairFPV
for i := 0; i < digits-1; i += 2 {
normalLat += strings.IndexByte(Alphabet, code[i]) * pv
normalLng += strings.IndexByte(Alphabet, code[i+1]) * pv
if i < digits-2 {
pv /= encBase
}
}
// Convert the place value to a float in degrees.
latPrecision := float64(pv) / pairPrecision
lngPrecision := float64(pv) / pairPrecision
// Process any extra precision digits.
if len(code) > pairCodeLen {
// Initialise the place values for the grid.
rowpv := gridLatFPV
colpv := gridLngFPV
// How many digits do we have to process?
digits = maxCodeLen
if len(code) < maxCodeLen {
digits = len(code)
}
for i := pairCodeLen; i < digits; i++ {
dval := strings.IndexByte(Alphabet, code[i])
row := dval / gridCols
col := dval % gridCols
extraLat += row * rowpv
extraLng += col * colpv
if i < digits-1 {
rowpv /= gridRows
colpv /= gridCols
}
}
// Adjust the precisions from the integer values to degrees.
latPrecision = float64(rowpv) / finalLatPrecision
lngPrecision = float64(colpv) / finalLngPrecision
}
// Merge the values from the normal and extra precision parts of the code.
// Everything is ints so they all need to be cast to floats.
lat := float64(normalLat)/pairPrecision + float64(extraLat)/finalLatPrecision
lng := float64(normalLng)/pairPrecision + float64(extraLng)/finalLngPrecision
// Round everthing off to 14 places.
return CodeArea{
LatLo: math.Round(lat*1e14) / 1e14,
LngLo: math.Round(lng*1e14) / 1e14,
LatHi: math.Round((lat+latPrecision)*1e14) / 1e14,
LngHi: math.Round((lng+lngPrecision)*1e14) / 1e14,
Len: len(code),
}, nil
}