-
Notifications
You must be signed in to change notification settings - Fork 1
/
maidenhead.go
117 lines (102 loc) · 3.55 KB
/
maidenhead.go
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
package maidenhead
import (
"errors"
"fmt"
"math"
"strconv"
"strings"
)
func GetGrid(latitude, longitude float64) (string, error) {
// Shift latitude and longitude
shiftedLatitude := latitude + 90
shiftedLongitude := longitude + 180
// Calculate field part
fieldLatitude := int(math.Floor(shiftedLatitude / 10))
fieldLongitude := int(math.Floor(shiftedLongitude / 20))
fieldLatitudeChar, err := numToLetter(fieldLatitude, true)
if err != nil {
return "", fmt.Errorf("error calculating field latitude: %v", err)
}
fieldLongitudeChar, err := numToLetter(fieldLongitude, true)
if err != nil {
return "", fmt.Errorf("error calculating field longitude: %v", err)
}
field := fieldLongitudeChar + fieldLatitudeChar
// Calculate square part
squareLatitude := int(math.Floor((shiftedLatitude/10 - float64(fieldLatitude)) * 10))
squareLongitude := int(math.Floor((shiftedLongitude/20 - float64(fieldLongitude)) * 10))
square := strconv.Itoa(squareLongitude) + strconv.Itoa(squareLatitude)
// Calculate subsquare part
subsquareLatitude := int(math.Floor(((shiftedLatitude/10-float64(fieldLatitude))*10 - float64(squareLatitude)) * 24))
subsquareLongitude := int(math.Floor(((shiftedLongitude/20-float64(fieldLongitude))*10 - float64(squareLongitude)) * 24))
subsquareLatitudeChar, err := numToLetter(subsquareLatitude, false)
if err != nil {
return "", fmt.Errorf("error calculating subsquare latitude: %v", err)
}
subsquareLongitudeChar, err := numToLetter(subsquareLongitude, false)
if err != nil {
return "", fmt.Errorf("error calculating subsquare longitude: %v", err)
}
subsquare := subsquareLongitudeChar + subsquareLatitudeChar
// Concatenate field, square, and subsquare parts
locator := field + square + subsquare
return locator, nil
}
func GetCoordinates(location string) (float64, float64, error) {
if len(location) != 4 && len(location) != 6 {
return 0, 0, fmt.Errorf("grid location must be either 4 or 6 digits")
}
location = strings.ToLower(location)
l := make([]int, 6)
var err error
l[0], err = letterToNum(string(location[0]))
if err != nil {
return 0, 0, fmt.Errorf("longitude field value: %w", err)
}
l[1], err = letterToNum(string(location[1]))
if err != nil {
return 0, 0, fmt.Errorf("latitude field value: %w", err)
}
l[2], err = strconv.Atoi(string(location[2]))
if err != nil {
return 0, 0, fmt.Errorf("longitude sqare value: %w", err)
}
l[3], err = strconv.Atoi(string(location[3]))
if err != nil {
return 0, 0, fmt.Errorf("latitude sqare value: %w", err)
}
if len(location) == 6 {
l[4], err = letterToNum(string(location[4]))
if err != nil {
return 0, 0, fmt.Errorf("longitude subsquare value: %w", err)
}
l[5], err = letterToNum(string(location[5]))
if err != nil {
return 0, 0, fmt.Errorf("latitude subsquare value: %w", err)
}
}
long := (float64(l[0]) * 20) + (float64(l[2]) * 2) + (float64(l[4]) / 12) - 180
lat := (float64(l[1]) * 10) + float64(l[3]) + (float64(l[5]) / 24) - 90
return lat, long, nil
}
func letterToNum(input string) (output int, err error) {
if len(input) != 1 {
return 0, errors.New("invalid input: input must be a single character")
}
if input < "a" || input > "x" {
return 0, errors.New("invalid input: input must be a letter between a and x")
}
output = int(input[0] - 'a')
return
}
func numToLetter(input int, capital bool) (output string, err error) {
if input < 0 || input > 23 {
return "", errors.New("invalid input: input must be a number between 0 and 23")
}
if capital {
output = string(rune(input + 'A'))
} else {
output = string(rune(input + 'a'))
}
return
}