-
-
Notifications
You must be signed in to change notification settings - Fork 191
/
subject.go
101 lines (85 loc) · 2.34 KB
/
subject.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
package subject
import (
"errors"
"log"
"github.com/go-spatial/tegola/container/singlelist/point/list"
"github.com/go-spatial/tegola/maths"
)
// ErrInvalidCoordsNumber is the error produced when the number of coordinates provided is not even or large enough to from a linestring.
var ErrInvalidCoordsNumber = errors.New("Event number of coords expected.")
type Subject struct {
winding maths.WindingOrder
list.List
}
func New(coords []float64) (*Subject, error) {
return new(Subject).Init(coords)
}
func (s *Subject) Init(coords []float64) (*Subject, error) {
if len(coords)%2 != 0 {
return nil, ErrInvalidCoordsNumber
}
s.winding = maths.WindingOrderOf(coords)
for x, y := 0, 1; x < len(coords); x, y = x+2, y+2 {
s.PushBack(list.NewPoint(coords[x], coords[y]))
}
return s, nil
}
func (s *Subject) Winding() maths.WindingOrder { return s.winding }
func (s *Subject) FirstPair() *Pair {
if s == nil {
return nil
}
var first, last *list.Pt
var ok bool
l, f := s.Back(), s.Front()
if last, ok = l.(*list.Pt); !ok {
return nil
}
if first, ok = f.(*list.Pt); !ok {
return nil
}
return &Pair{
l: &(s.List),
pts: [2]*list.Pt{last, first},
}
}
func (s *Subject) GetPair(idx int) *Pair {
p := s.FirstPair()
log.Println(p)
for i := 0; i < idx; i++ {
p = p.Next()
if p == nil {
return p
}
}
return p
}
// Contains will test to see if the point if fully contained by the subject. If the point is on the broader it is not considered as contained.
func (s *Subject) Contains(pt maths.Pt) bool {
line := maths.Line{pt, maths.Pt{pt.X - 1, pt.Y}}
count := 0
var lpt maths.Pt
var haveLpt bool
for p := s.FirstPair(); p != nil; p = p.Next() {
pline := p.AsLine()
if ipt, ok := maths.Intersect(line, pline); ok {
ipt = ipt.Truncate()
if haveLpt {
if lpt.IsEqual(ipt) {
// skip repeat intersections points. These may be an intersection point that lies on the end pt.
continue
}
}
//log.Println("Got Intersect ", count, ipt, pline.InBetween(ipt) && ipt.X < pt.X, pt.X, pline)
// We only care about intersect points that are left of the point being tested.
if pline.InBetween(ipt) && ipt.X < pt.X {
count++
}
lpt = ipt
haveLpt = true
}
}
log.Println("Contains Count:", count)
// If it's odd then it's inside of the polygon, otherwise it's outside of the polygon.
return count%2 != 0
}