This repository has been archived by the owner on Feb 5, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmisc.go
141 lines (128 loc) · 4.59 KB
/
misc.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
package turfgo
import (
"errors"
"math"
"github.com/hammerheadnav/turfgo/turfgoMath"
)
const invalidBearing = -1234.0
// PointOnLine takes a Point and a LineString and calculates the closest Point on the LineString.
func PointOnLine(point *Point, lineString *LineString, units string) (*Point, float64, int, error) {
closestPt := &Point{infinity, infinity}
closestDistance := float64(infinity)
index := -1
coords := lineString.Points
for i := 0; i < len(coords)-1; i++ {
start := coords[i]
stop := coords[i+1]
startDist, err := Distance(point, start, units)
if err != nil {
return nil, -1, -1, err
}
stopDist, _ := Distance(point, stop, units)
direction := Bearing(start, stop)
height := math.Max(stopDist, startDist)
perpendicularPt1, _ := Destination(point, height, direction+90, units)
perpendicularPt2, _ := Destination(point, height, direction-90, units)
intersect := lineIntersects(perpendicularPt1, perpendicularPt2, start, stop)
intersectD := float64(infinity)
if intersect != nil {
intersectD, _ = Distance(point, intersect, units)
}
if startDist < closestDistance {
closestPt = start
closestDistance = startDist
index = i
}
if stopDist < closestDistance {
closestPt = stop
closestDistance = stopDist
index = i
}
if intersectD < closestDistance {
closestPt = intersect
closestDistance = intersectD
index = i
}
}
return closestPt, closestDistance, index, nil
}
// TriangularProjection calculate the projection of given point on the lineString, base angles for projection should be acute.
// If bearing should also be considerd, pass in a previous point also, otherwise it should be nil
func TriangularProjection(point *Point, previousPoint *Point, lineString *LineString, unit string) (*Point, float64, int, error) {
bearing := invalidBearing
if previousPoint != nil {
bearing = Bearing(previousPoint, point)
for bearing < 0 {
bearing += 360
}
}
for i := 0; i < len(lineString.Points)-1; i++ {
start := lineString.Points[i]
end := lineString.Points[i+1]
if !isAnyBaseAngleObtuse(point, start, end) {
bearingLs := Bearing(start, end)
for bearingLs < 0 {
bearingLs += 360
}
bearingDiff := bearing - bearingLs
if bearing != invalidBearing && (bearingDiff < -45 || bearingDiff > +45) {
continue
}
projection, distance, _, err := PointOnLine(point, NewLineString([]*Point{start, end}), unit)
if err != nil {
return nil, -1, -1, err
}
return projection, distance, i, nil
}
}
return nil, -1, -1, errors.New("No Projection found")
}
func lineIntersects(line1Start *Point, line1End *Point, line2Start *Point, line2End *Point) *Point {
// if the lines intersect, the result contains the x and y of the intersection (treating the lines as infinite) and booleans for whether line segment 1 or line segment 2 contain the point
// denominator = ((line2EndY - line2StartY) * (line1EndX - line1StartX)) - ((line2EndX - line2StartX) * (line1EndY - line1StartY));
denominator := ((line2End.Lng - line2Start.Lng) * (line1End.Lat - line1Start.Lat)) - ((line2End.Lat - line2Start.Lat) * (line1End.Lng - line1Start.Lng))
if denominator == 0 {
return nil
}
a := line1Start.Lng - line2Start.Lng
b := line1Start.Lat - line2Start.Lat
numerator1 := ((line2End.Lat - line2Start.Lat) * a) - ((line2End.Lng - line2Start.Lng) * b)
numerator2 := ((line1End.Lat - line1Start.Lat) * a) - ((line1End.Lng - line1Start.Lng) * b)
a = numerator1 / denominator
b = numerator2 / denominator
// if we cast these lines infinitely in both directions, they intersect here:
lat := line1Start.Lat + (a * (line1End.Lat - line1Start.Lat))
lng := line1Start.Lng + (a * (line1End.Lng - line1Start.Lng))
onLine1 := false
onLine2 := false
// if line1 is a segment and line2 is infinite, they intersect if:
if a > 0 && a < 1 {
onLine1 = true
}
// if line2 is a segment and line1 is infinite, they intersect if:
if b > 0 && b < 1 {
onLine2 = true
}
// if line1 and line2 are segments, they intersect if both of the above are true
if onLine1 && onLine2 {
return &Point{lat, lng}
}
return nil
}
func isAnyBaseAngleObtuse(point *Point, start *Point, end *Point) bool {
alpha, _ := Distance(point, start, "mi")
beta, _ := Distance(point, end, "mi")
gamma, _ := Distance(start, end, "mi")
if gamma == 0 {
return true
}
if turfgoMath.IsEqualFloat(alpha+beta, gamma, turfgoMath.TwelveDecimalPlaces) {
return false
}
cosineA := ((alpha * alpha) + (gamma * gamma) - (beta * beta)) / (2 * alpha * gamma)
cosineB := ((beta * beta) + (gamma * gamma) - (alpha * alpha)) / (2 * beta * gamma)
if cosineA < 0 || cosineB < 0 {
return true
}
return false
}