-
Notifications
You must be signed in to change notification settings - Fork 1
/
polygon.go
108 lines (90 loc) · 2.58 KB
/
polygon.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
package geojson
import (
"encoding/json"
"fmt"
)
// Polygon is a set of linear rings (closed LineStrings).
type Polygon [][]Position
// NewPolygon returns a new Polygon from the supplied linear rings.
func NewPolygon(rings ...[]Position) *Feature {
return &Feature{
Geometry: (*Polygon)(&rings),
}
}
// Type returns the geometry type.
func (p Polygon) Type() GeometryType {
return PolygonGeometryType
}
// Validate the Polygon.
func (p Polygon) Validate() error {
for i, ring := range p {
if len(ring) < 4 {
return fmt.Errorf("polygon ring is too short - must contain at least 4 positions")
} else if ring[len(ring)-1] != ring[0] {
return fmt.Errorf("polygon ring must be closed")
}
if angle := LoopToS2(ring).TurningAngle(); i == 0 && angle >= 0 { // CCW
return fmt.Errorf("exterior ring must be clockwise but angle is %f", angle)
} else if i > 0 && angle <= 0 { // CW
return fmt.Errorf("interior ring must be counter-clockwise but angle is %f", angle)
}
}
return nil
}
// MarshalJSON returns the JSON encoding of the Polygon.
func (p Polygon) MarshalJSON() ([]byte, error) {
return json.Marshal(geometry{
Type: PolygonGeometryType,
Coordinates: [][]Position(p),
})
}
// UnmarshalJSON parses the JSON-encoded data and stores the result.
func (p *Polygon) UnmarshalJSON(data []byte) error {
var geo struct {
Coordinates [][]Position `json:"coordinates"`
}
if err := json.Unmarshal(data, &geo); err != nil {
return err
}
*p = Polygon(geo.Coordinates)
return nil
}
// MultiPolygon is a set of Polygons.
type MultiPolygon [][][]Position
// NewMultiPolygon returns a new MultiPolygon from the supplied polygons.
func NewMultiPolygon(p ...[][]Position) *Feature {
return &Feature{
Geometry: (*MultiPolygon)(&p),
}
}
// Type returns the geometry type.
func (m MultiPolygon) Type() GeometryType {
return MultiPolygonGeometryType
}
// Validate the MultiPolygon.
func (m MultiPolygon) Validate() error {
for _, polygon := range m {
if err := Polygon(polygon).Validate(); err != nil {
return err
}
}
return nil
}
// MarshalJSON returns the JSON encoding of the MultiPolygon.
func (m MultiPolygon) MarshalJSON() ([]byte, error) {
return json.Marshal(geometry{
Type: MultiPolygonGeometryType,
Coordinates: [][][]Position(m),
})
}
// UnmarshalJSON parses the JSON-encoded data and stores the result.
func (m *MultiPolygon) UnmarshalJSON(data []byte) error {
var geo struct {
Coordinates [][][]Position `json:"coordinates"`
}
if err := json.Unmarshal(data, &geo); err != nil {
return err
}
*m = MultiPolygon(geo.Coordinates)
return nil
}