1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
|
// 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) (point Point, err error) {
var (
lnglat = [2]float64{
-180.0,
-90.0,
}
j int
)
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
}
}
point = NewPoint(lnglat[1], lnglat[0])
return
}
|