Initial import
This commit is contained in:
148
util/maidenhead/maidenhead.go
Normal file
148
util/maidenhead/maidenhead.go
Normal file
@@ -0,0 +1,148 @@
|
||||
// Package maidenhead implements the Maidenhead Locator System, a geographic
|
||||
// coordinate system used by amataur radio (HAM) operators.
|
||||
package maidenhead
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Precision of the computed locator.
|
||||
const (
|
||||
FieldPrecision = iota + 1
|
||||
SquarePrecision
|
||||
SubSquarePrecision
|
||||
ExtendedSquarePrecision
|
||||
)
|
||||
|
||||
var (
|
||||
upper = "ABCDEFGHIJKLMNOPQRSTUVWX"
|
||||
lower = "abcdefghijklmnopqrstuvwx"
|
||||
digit = "0123456789"
|
||||
)
|
||||
|
||||
// locator computes the Maidenhead Locator for a given position.
|
||||
func locator(p Point, precision int) (string, error) {
|
||||
if math.IsNaN(p.Latitude) {
|
||||
return "", errors.New("maidenhead: latitude is not a digit")
|
||||
}
|
||||
if math.IsInf(p.Latitude, 0) {
|
||||
return "", errors.New("maidenhead: latitude is infinite")
|
||||
}
|
||||
if math.IsNaN(p.Longitude) {
|
||||
return "", errors.New("maidenhead: longitude is not a digit")
|
||||
}
|
||||
if math.IsInf(p.Longitude, 0) {
|
||||
return "", errors.New("maidenhead: longitude is infinite")
|
||||
}
|
||||
if math.Abs(p.Latitude) == 90.0 {
|
||||
return "", errors.New("maidenhead: grid square invalid at poles")
|
||||
} else if math.Abs(p.Latitude) > 90.0 {
|
||||
return "", fmt.Errorf("maidenhead: invalid latitude %.04f", p.Latitude)
|
||||
} else if math.Abs(p.Longitude) > 180.0 {
|
||||
return "", fmt.Errorf("maidenhead: invalid longitude %.05f", p.Longitude)
|
||||
}
|
||||
|
||||
var (
|
||||
lat = p.Latitude + 90.0
|
||||
lng = p.Longitude + 180.0
|
||||
loc string
|
||||
)
|
||||
|
||||
lat = lat/10.0 + 0.0000001
|
||||
lng = lng/20.0 + 0.0000001
|
||||
loc = loc + string(upper[int(lng)]) + string(upper[int(lat)])
|
||||
if precision == 1 {
|
||||
return loc, nil
|
||||
}
|
||||
lat = 10 * (lat - math.Floor(lat))
|
||||
lng = 10 * (lng - math.Floor(lng))
|
||||
loc = loc + fmt.Sprintf("%d%d", int(lng)%10, int(lat)%10)
|
||||
if precision == 2 {
|
||||
return loc, nil
|
||||
}
|
||||
lat = 24 * (lat - math.Floor(lat))
|
||||
lng = 24 * (lng - math.Floor(lng))
|
||||
loc = loc + string(upper[int(lng)]) + string(upper[int(lat)])
|
||||
if precision == 3 {
|
||||
return loc, nil
|
||||
}
|
||||
lat = 10 * (lat - math.Floor(lat))
|
||||
lng = 10 * (lng - math.Floor(lng))
|
||||
loc = loc + fmt.Sprintf("%d%d", int(lng)%10, int(lat)%10)
|
||||
if precision == 4 {
|
||||
return loc, nil
|
||||
}
|
||||
lat = 24 * (lat - math.Floor(lat))
|
||||
lng = 24 * (lng - math.Floor(lng))
|
||||
loc = loc + string(lower[int(lng)]) + string(lower[int(lat)])
|
||||
return loc, nil
|
||||
}
|
||||
|
||||
var parseLocatorMult = []struct {
|
||||
s, p string
|
||||
mult float64
|
||||
}{
|
||||
{upper[:18], lower[:18], 20.0},
|
||||
{upper[:18], lower[:18], 10.0},
|
||||
{digit[:10], digit[:10], 20.0 / 10.0},
|
||||
{digit[:10], digit[:10], 10.0 / 10.0},
|
||||
{upper[:24], lower[:24], 20.0 / (10.0 * 24.0)},
|
||||
{upper[:24], lower[:24], 10.0 / (10.0 * 24.0)},
|
||||
{digit[:10], digit[:10], 20.0 / (10.0 * 24.0 * 10.0)},
|
||||
{digit[:10], digit[:10], 10.0 / (10.0 * 24.0 * 10.0)},
|
||||
{lower[:24], lower[:24], 20.0 / (10.0 * 24.0 * 10.0 * 24.0)},
|
||||
{lower[:24], lower[:24], 10.0 / (10.0 * 24.0 * 10.0 * 24.0)},
|
||||
}
|
||||
|
||||
var maxLocatorLength = len(parseLocatorMult)
|
||||
|
||||
func parseLocator(locator string, strict bool, centered bool) (point Point, err error) {
|
||||
var (
|
||||
lnglat = [2]float64{
|
||||
-180.0,
|
||||
-90.0,
|
||||
}
|
||||
i, j int
|
||||
char rune
|
||||
)
|
||||
|
||||
if len(locator) > maxLocatorLength {
|
||||
err = fmt.Errorf("maidenhead: locator is too long (%d characters, maximum %d characters allowed)",
|
||||
len(locator), maxLocatorLength)
|
||||
return
|
||||
}
|
||||
|
||||
if len(locator)%2 != 0 {
|
||||
err = fmt.Errorf("maidenhead: locator has odd number of characters")
|
||||
return
|
||||
}
|
||||
|
||||
if strict {
|
||||
for i, char = range locator {
|
||||
if j = strings.Index(parseLocatorMult[i].s, string(char)); j < 0 {
|
||||
err = fmt.Errorf("maidenhead: invalid character at offset %d", i)
|
||||
return
|
||||
}
|
||||
lnglat[i%2] += float64(j) * parseLocatorMult[i].mult
|
||||
}
|
||||
} else {
|
||||
for i, char = range strings.ToLower(locator) {
|
||||
if j = strings.Index(parseLocatorMult[i].p, string(char)); j < 0 {
|
||||
err = fmt.Errorf("maidenhead: invalid character at offset %d", i)
|
||||
return
|
||||
}
|
||||
lnglat[i%2] += float64(j) * parseLocatorMult[i].mult
|
||||
}
|
||||
}
|
||||
|
||||
if centered {
|
||||
lnglat[0] += parseLocatorMult[i-1].mult / 2.0
|
||||
lnglat[1] += parseLocatorMult[i].mult / 2.0
|
||||
}
|
||||
|
||||
point = NewPoint(lnglat[1], lnglat[0])
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user