-
Notifications
You must be signed in to change notification settings - Fork 4
/
feature.go
159 lines (136 loc) · 3.64 KB
/
feature.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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package geo
import (
"database/sql/driver"
"encoding/json"
"fmt"
)
var (
featureJSONPrefix = []byte(`{"type":"Feature","geometry":`)
propertiesJSONKey = []byte(`,"properties":`)
)
// Feature is a GeoJSON feature.
type Feature struct {
Geometry Geometry `json:"geometry"`
Properties interface{} `json:"properties,omitempty"`
}
// Equal compares one feature to another.
// Note that this method does not compare properties.
func (f Feature) Equal(g Geometry) bool {
other, ok := g.(*Feature)
if !ok {
return false
}
return f.Geometry.Equal(other.Geometry)
}
// Contains determines if the feature's geometry contains the point.
func (f Feature) Contains(p Point) bool {
return f.Geometry.Contains(p)
}
// MarshalJSON marshals the feature to GeoJSON.
func (f Feature) MarshalJSON() ([]byte, error) {
geom, err := f.Geometry.MarshalJSON()
if err != nil {
return nil, err
}
props, err := json.Marshal(f.Properties)
if err != nil {
return nil, err
}
buf := append(featureJSONPrefix, geom...)
buf = append(buf, propertiesJSONKey...)
buf = append(buf, props...)
return append(buf, '}'), nil
}
// Scan scans a feature from well known text.
func (f *Feature) Scan(src interface{}) error {
return scan(f, src)
}
// scan scans a feature from well known text.
func (f *Feature) scan(s string) error {
geom, err := ScanGeometry(s)
if err != nil {
return err
}
f.Geometry = geom
return nil
}
// String converts the feature to a WKT string.
func (f Feature) String() string {
return f.Geometry.String()
}
// feature is a utility type used to unmarshal geojson Feature's.
type feature struct {
Type string `json:"type"`
Geometry json.RawMessage `json:"geometry"`
Properties interface{} `json:"properties"`
BBox []float64 `json:"bbox"`
}
// ToFeature converts the private feature type to the public one.
func (f *feature) ToFeature() (*Feature, error) {
g := geometry{}
if err := json.Unmarshal(f.Geometry, &g); err != nil {
return nil, err
}
// Unmarshal the coordinates into one of our Geometry types.
geom, err := g.unmarshalCoordinates()
if err != nil {
return nil, err
}
feat := &Feature{}
feat.Geometry = geom
feat.Properties = f.Properties
return feat, nil
}
// UnmarshalJSON unmarshals a feature from JSON.
func (f *Feature) UnmarshalJSON(data []byte) error {
ff, _, err := unmarshalFeature(data)
if err != nil {
return err
}
*f = *ff
return nil
}
// Value returns well known text for the feature.
func (f Feature) Value() (driver.Value, error) {
return f.Geometry.Value()
}
// Transform transforms the geometry point by point.
func (f *Feature) Transform(t Transformer) {
f.Geometry.Transform(t)
}
// VisitCoordinates visits each point in the geometry.
func (f Feature) VisitCoordinates(v Visitor) {
f.Geometry.VisitCoordinates(v)
}
func unmarshalFeature(data []byte) (*Feature, *feature, error) {
feat := feature{}
// Never fails because data is always valid JSON.
_ = json.Unmarshal(data, &feat)
// Check the type.
if expected, got := FeatureType, feat.Type; expected != got {
return nil, nil, fmt.Errorf("expected type %s, got %s", expected, got)
}
g := geometry{}
if err := json.Unmarshal(feat.Geometry, &g); err != nil {
return nil, nil, err
}
// Unmarshal the coordinates into one of our Geometry types.
geom, err := g.unmarshalCoordinates()
if err != nil {
return nil, nil, err
}
f := &Feature{}
f.Geometry = geom
f.Properties = feat.Properties
return f, &feat, nil
}
func unmarshalFeatureBBox(data []byte) (Geometry, error) {
f, ff, err := unmarshalFeature(data)
if err != nil {
return nil, err
}
if len(ff.BBox) > 0 {
return WithBBox(ff.BBox, f), nil
}
return f, nil
}