forked from paulmach/orb
-
Notifications
You must be signed in to change notification settings - Fork 0
/
area.go
112 lines (93 loc) · 2.05 KB
/
area.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
// Package geo computes properties on geometries assuming they are lon/lat data.
package geo
import (
"fmt"
"math"
"github.com/paulmach/orb"
)
// Area returns the area of the geometry on the earth.
func Area(g orb.Geometry) float64 {
if g == nil {
return 0
}
switch g := g.(type) {
case orb.Point, orb.MultiPoint, orb.LineString, orb.MultiLineString:
return 0
case orb.Ring:
return math.Abs(ringArea(g))
case orb.Polygon:
return polygonArea(g)
case orb.MultiPolygon:
return multiPolygonArea(g)
case orb.Collection:
return collectionArea(g)
case orb.Bound:
return Area(g.ToRing())
}
panic(fmt.Sprintf("geometry type not supported: %T", g))
}
// SignedArea will return the signed area of the ring.
// Will return negative if the ring is in the clockwise direction.
// Will implicitly close the ring.
func SignedArea(r orb.Ring) float64 {
return ringArea(r)
}
func ringArea(r orb.Ring) float64 {
if len(r) < 3 {
return 0
}
var lo, mi, hi int
l := len(r)
if r[0] != r[len(r)-1] {
// if not a closed ring, add an implicit calc for that last point.
l++
}
// To support implicit closing of ring, replace references to
// the last point in r to the first 1.
area := 0.0
for i := 0; i < l; i++ {
if i == l-3 { // i = N-3
lo = l - 3
mi = l - 2
hi = 0
} else if i == l-2 { // i = N-2
lo = l - 2
mi = 0
hi = 0
} else if i == l-1 { // i = N-1
lo = 0
mi = 0
hi = 1
} else { // i = 0 to N-3
lo = i
mi = i + 1
hi = i + 2
}
area += (deg2rad(r[hi][0]) - deg2rad(r[lo][0])) * math.Sin(deg2rad(r[mi][1]))
}
return -area * orb.EarthRadius * orb.EarthRadius / 2
}
func polygonArea(p orb.Polygon) float64 {
if len(p) == 0 {
return 0
}
sum := math.Abs(ringArea(p[0]))
for i := 1; i < len(p); i++ {
sum -= math.Abs(ringArea(p[i]))
}
return sum
}
func multiPolygonArea(mp orb.MultiPolygon) float64 {
sum := 0.0
for _, p := range mp {
sum += polygonArea(p)
}
return sum
}
func collectionArea(c orb.Collection) float64 {
area := 0.0
for _, g := range c {
area += Area(g)
}
return area
}