-
Notifications
You must be signed in to change notification settings - Fork 4
/
line.go
110 lines (96 loc) · 2.57 KB
/
line.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
package geo
import (
"database/sql/driver"
"fmt"
)
const (
lineWKTEmpty = `LINESTRING EMPTY`
lineWKTPrefix = `LINESTRING`
lineJSONPrefix = `{"type":"LineString","coordinates":[`
lineJSONSuffix = `]}`
)
// Line is a line.
type Line [][3]float64
// Equal compares one line to another.
func (line Line) Equal(g Geometry) bool {
ls, ok := g.(*Line)
if !ok {
return false
}
return pointsEqual(line, *ls)
}
// Contains determines if the line contains a point.
func (line Line) Contains(p Point) bool {
return pointsContain(line, p)
}
// segmentContains returns true if p lies on the line segment that connects s1 and s2.
func segmentContains(s1, s2, p [3]float64) bool {
// Return false if p is outside of the bounding box around s1 and s2.
if (p[0] > s1[0] && p[0] > s2[0]) || (p[0] < s1[0] && p[0] < s2[0]) {
return false
}
if (p[1] > s1[1] && p[1] > s2[1]) || (p[1] < s1[1] && p[1] < s2[1]) {
return false
}
// Equal the slope of the segment between s1 and p
// to the slope of the segment between s1 and s2.
var (
segmentSlope = (s2[1] - s1[1]) / (s2[0] - s1[0])
pSlope = (p[1] - s1[1]) / (p[0] - s1[0])
)
return segmentSlope == pSlope
}
// MarshalJSON marshals the line to JSON.
func (line Line) MarshalJSON() ([]byte, error) {
return pointsMarshalJSON(line, lineJSONPrefix, lineJSONSuffix), nil
}
// Scan scans a line from Well Known Text.
func (line *Line) Scan(src interface{}) error {
return scan(line, src)
}
// scan scans a line from a Well Known Text string.
func (line *Line) scan(s string) error {
if len(s) <= len(lineWKTPrefix) {
return fmt.Errorf("could not scan line from %s", s)
}
points, err := pointsScan(s[len(lineWKTPrefix):])
if err != nil {
return err
}
*line = points
return nil
}
// String converts the line to a string.
func (line Line) String() string {
if len(line) == 0 {
return lineWKTEmpty
}
return lineWKTPrefix + pointsString(line)
}
// UnmarshalJSON unmarshals a line from GeoJSON.
func (line *Line) UnmarshalJSON(data []byte) error {
pts, err := pointsUnmarshal(data, LineType)
if err != nil {
return err
}
*line = pts
return nil
}
// Value returns a driver Value.
func (line Line) Value() (driver.Value, error) {
return line.String(), nil
}
// Transform transforms the geometry point by point.
func (line *Line) Transform(t Transformer) {
nl := make([][3]float64, len(*line))
for i, point := range *line {
nl[i] = [3]float64(t.Transform(point))
}
*line = nl
}
// VisitCoordinates visits each point in the geometry.
func (line Line) VisitCoordinates(v Visitor) {
for _, point := range line {
v.Visit(point)
}
}