-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathpoint.go
162 lines (138 loc) · 4.1 KB
/
point.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
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package maidenhead
import (
"fmt"
"math"
)
const (
// Earth radius
r = 6371
)
var compassBearing = []struct {
label string
start, ended float64
}{
{"N", 000.00, 011.25}, {"NNE", 011.25, 033.75}, {"NE", 033.75, 056.25}, {"ENE", 056.25, 078.75},
{"E", 078.75, 101.25}, {"ESE", 101.25, 123.75}, {"SE", 123.75, 146.25}, {"SSE", 146.25, 168.75},
{"S", 168.75, 191.25}, {"SSW", 191.25, 213.75}, {"SW", 213.75, 236.25}, {"WSW", 236.25, 258.75},
{"W", 258.75, 281.25}, {"WNW", 281.25, 303.75}, {"NW", 303.75, 326.25}, {"NNW", 326.25, 348.75},
{"N", 348.75, 360.00},
}
// Point is a geographical point on the map.
type Point struct {
Latitude float64
Longitude float64
}
// NewPoint returns a new Point structure with given latitude and longitude.
func NewPoint(latitude, longitude float64) Point {
return Point{latitude, longitude}
}
// ParseLocator parses a Maidenhead Locator with permissive rule matching.
func ParseLocator(locator string) (Point, error) {
return parseLocator(locator, false, false)
}
// ParseLocatorStrict parses a Maidenhead Locator with strict rule matching.
func ParseLocatorStrict(locator string) (Point, error) {
return parseLocator(locator, true, false)
}
// ParseLocatorCentered parses a Maidenhead Locator with permissive rule matching.
// Returns Points structure with coordinates of the square center
func ParseLocatorCentered(locator string) (Point, error) {
return parseLocator(locator, false, true)
}
// ParseLocatorStrictCentered parses a Maidenhead Locator with strict rule matching.
// Returns Points structure with coordinates of the square center
func ParseLocatorStrictCentered(locator string) (Point, error) {
return parseLocator(locator, true, true)
}
// EqualTo returns true if the coordinates point to the same geographical location.
func (p Point) EqualTo(other Point) bool {
var (
dlat = p.Latitude - other.Latitude
dlng = p.Longitude - other.Longitude
)
for dlat < -180.0 {
dlat += 360.0
}
for dlat > 180.0 {
dlat -= 360.0
}
for dlng < -90.0 {
dlng += 90.0
}
for dlng > 90.0 {
dlng -= 90.0
}
return dlat == 0.0 && dlng == 0.0
}
// Bearing calculates the (approximate) bearing to another heading.
func (p Point) Bearing(heading Point) float64 {
var (
hn = p.Latitude / 180 * math.Pi
he = p.Longitude / 180 * math.Pi
n = heading.Latitude / 180 * math.Pi
e = heading.Longitude / 180 * math.Pi
co = math.Cos(he-e)*math.Cos(hn)*math.Cos(n) + math.Sin(hn)*math.Sin(n)
ca = math.Atan(math.Abs(math.Sqrt(1-co*co) / co))
)
if co < 0.0 {
ca = math.Pi - ca
}
var si = math.Sin(e-he) * math.Cos(n) * math.Cos(hn)
co = math.Sin(n) - math.Sin(hn)*math.Cos(ca)
var az = math.Atan(math.Abs(si / co))
if co < 0.0 {
az = math.Pi - az
}
if si < 0.0 {
az = -az
}
if az < 0.0 {
az = az + 2.0*math.Pi
}
return az * 180 / math.Pi
}
// CompassBearing returns the compass bearing to a heading.
func (p Point) CompassBearing(heading Point) string {
bearing := p.Bearing(heading)
for bearing < 0.0 {
bearing += 360.0
}
for bearing > 360.0 {
bearing -= 360.0
}
for _, compass := range compassBearing {
if bearing >= compass.start && bearing <= compass.ended {
return compass.label
}
}
// Should never reach
return ""
}
// Distance calculates the (approximate) distance to another point in km.
func (p Point) Distance(other Point) float64 {
var (
hn = p.Latitude / 180 * math.Pi
he = p.Longitude / 180 * math.Pi
n = other.Latitude / 180 * math.Pi
e = other.Longitude / 180 * math.Pi
co = math.Cos(he-e)*math.Cos(hn)*math.Cos(n) + math.Sin(hn)*math.Sin(n)
ca = math.Atan(math.Abs(math.Sqrt(1-co*co) / co))
)
if co < 0.0 {
ca = math.Pi - ca
}
return r * ca
}
// GridSquare returns a Maidenhead Locator for the point coordinates.
func (p Point) GridSquare() (string, error) {
return locator(p, SubSquarePrecision)
}
// Locator returns a Maidenhead Locator for the point coordinates with
// specified precision
func (p Point) Locator(precision int) (string, error) {
return locator(p, precision)
}
// String returns a stringified Point structure.
func (p Point) String() string {
return fmt.Sprintf("Point(%f, %f)", p.Latitude, p.Longitude)
}