-
Notifications
You must be signed in to change notification settings - Fork 49
/
Copy pathpoint.go
140 lines (108 loc) · 2.73 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
package geoindex
import (
"fmt"
"math"
)
var (
earthRadius = Km(6371.0)
)
type Direction int
const (
NorthEast Direction = iota
East
SouthEast
South
SouthWest
West
NorthWest
North
)
type Point interface {
Id() string
Lat() float64
Lon() float64
}
// Point implementation.
type GeoPoint struct {
Pid string `json:"Id"`
Plat float64 `json:"Lat"`
Plon float64 `json:"Lon"`
}
func NewGeoPoint(id string, lat, lon float64) *GeoPoint {
return &GeoPoint{id, lat, lon}
}
func (p *GeoPoint) String() string {
return fmt.Sprintf("%s %f %f", p.Id(), p.Lat(), p.Lon())
}
func (p *GeoPoint) Id() string {
return p.Pid
}
func (p *GeoPoint) Lat() float64 {
return p.Plat
}
func (p *GeoPoint) Lon() float64 {
return p.Plon
}
// DirectionTo returns the direction from p1 to p2
func DirectionTo(p1, p2 Point) Direction {
bearing := BearingTo(p1, p2)
index := bearing - 22.5
if index < 0 {
index += 360
}
indexInt := int(index / 45.0)
return Direction(indexInt)
}
// BearingTo returns the bearing from p1 to p2
func BearingTo(p1, p2 Point) float64 {
dLon := toRadians(p2.Lon() - p1.Lon())
lat1 := toRadians(p1.Lat())
lat2 := toRadians(p2.Lat())
y := math.Sin(dLon) * math.Cos(lat2)
x := math.Cos(lat1)*math.Sin(lat2) -
math.Sin(lat1)*math.Cos(lat2)*math.Cos(dLon)
brng := toDegrees(math.Atan2(y, x))
return brng
}
func Distance(p1, p2 Point) Meters {
return distance(p1, p2)
}
func toDegrees(x float64) float64 {
return x * 180.0 / math.Pi
}
func toRadians(x float64) float64 {
return x * math.Pi / 180.0
}
func distance(p1, p2 Point) Meters {
dLat := toRadians(p2.Lat() - p1.Lat())
dLng := toRadians(p2.Lon() - p1.Lon())
sindLat := math.Sin(dLat / 2)
sindLng := math.Sin(dLng / 2)
a := math.Pow(sindLat, 2) + math.Pow(sindLng, 2)*math.Cos(toRadians(p1.Lat()))*math.Cos(toRadians(p2.Lat()))
c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
dist := float64(earthRadius) * c
return Meters(dist)
}
type lonDegreeDistance map[int]Meters
func (lonDist lonDegreeDistance) get(lat float64) Meters {
latIndex := int(lat * 10)
latRounded := float64(latIndex) / 10
if value, ok := lonDist[latIndex]; ok {
return value
} else {
dist := distance(&GeoPoint{"", latRounded, 0.0}, &GeoPoint{"", latRounded, 1.0})
lonDist[latIndex] = dist
return dist
}
}
var (
lonLength = lonDegreeDistance{}
)
// Calculates approximate distance between two points using euclidian distance. The assumption here
// is that the points are relatively close to each other.
func approximateSquareDistance(p1, p2 Point) Meters {
avgLat := (p1.Lat() + p2.Lat()) / 2.0
latLen := math.Abs(p1.Lat()-p2.Lat()) * float64(latDegreeLength)
lonLen := math.Abs(p1.Lon()-p2.Lon()) * float64(lonLength.get(avgLat))
return Meters(latLen*latLen + lonLen*lonLen)
}