|
- // Copyright (c) 2017 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.
-
- package geo
-
- import (
- "fmt"
- "math"
- "strconv"
- "strings"
- )
-
- type distanceUnit struct {
- conv float64
- suffixes []string
- }
-
- var inch = distanceUnit{0.0254, []string{"in", "inch"}}
- var yard = distanceUnit{0.9144, []string{"yd", "yards"}}
- var feet = distanceUnit{0.3048, []string{"ft", "feet"}}
- var kilom = distanceUnit{1000, []string{"km", "kilometers"}}
- var nauticalm = distanceUnit{1852.0, []string{"nm", "nauticalmiles"}}
- var millim = distanceUnit{0.001, []string{"mm", "millimeters"}}
- var centim = distanceUnit{0.01, []string{"cm", "centimeters"}}
- var miles = distanceUnit{1609.344, []string{"mi", "miles"}}
- var meters = distanceUnit{1, []string{"m", "meters"}}
-
- var distanceUnits = []*distanceUnit{
- &inch, &yard, &feet, &kilom, &nauticalm, &millim, ¢im, &miles, &meters,
- }
-
- // ParseDistance attempts to parse a distance string and return distance in
- // meters. Example formats supported:
- // "5in" "5inch" "7yd" "7yards" "9ft" "9feet" "11km" "11kilometers"
- // "3nm" "3nauticalmiles" "13mm" "13millimeters" "15cm" "15centimeters"
- // "17mi" "17miles" "19m" "19meters"
- // If the unit cannot be determined, the entire string is parsed and the
- // unit of meters is assumed.
- // If the number portion cannot be parsed, 0 and the parse error are returned.
- func ParseDistance(d string) (float64, error) {
- for _, unit := range distanceUnits {
- for _, unitSuffix := range unit.suffixes {
- if strings.HasSuffix(d, unitSuffix) {
- parsedNum, err := strconv.ParseFloat(d[0:len(d)-len(unitSuffix)], 64)
- if err != nil {
- return 0, err
- }
- return parsedNum * unit.conv, nil
- }
- }
- }
- // no unit matched, try assuming meters?
- parsedNum, err := strconv.ParseFloat(d, 64)
- if err != nil {
- return 0, err
- }
- return parsedNum, nil
- }
-
- // ParseDistanceUnit attempts to parse a distance unit and return the
- // multiplier for converting this to meters. If the unit cannot be parsed
- // then 0 and the error message is returned.
- func ParseDistanceUnit(u string) (float64, error) {
- for _, unit := range distanceUnits {
- for _, unitSuffix := range unit.suffixes {
- if u == unitSuffix {
- return unit.conv, nil
- }
- }
- }
- return 0, fmt.Errorf("unknown distance unit: %s", u)
- }
-
- // Haversin computes the distance between two points.
- // This implemenation uses the sloppy math implemenations which trade off
- // accuracy for performance. The distance returned is in kilometers.
- func Haversin(lon1, lat1, lon2, lat2 float64) float64 {
- x1 := lat1 * degreesToRadian
- x2 := lat2 * degreesToRadian
- h1 := 1 - cos(x1-x2)
- h2 := 1 - cos((lon1-lon2)*degreesToRadian)
- h := (h1 + cos(x1)*cos(x2)*h2) / 2
- avgLat := (x1 + x2) / 2
- diameter := earthDiameter(avgLat)
-
- return diameter * asin(math.Min(1, math.Sqrt(h)))
- }
|