forked from sams96/rgeo
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathfeatures.go
120 lines (104 loc) · 3.06 KB
/
features.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
package rgeo
import (
"bytes"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"io"
"github.com/golang/geo/s2"
"github.com/twpayne/go-geom/encoding/geojson"
)
type FeatureCollection []Feature
func (fc *FeatureCollection) Encode(w io.Writer) error {
for _, f := range *fc {
if err := f.Encode(w); err != nil {
return err
}
}
return nil
}
type Feature struct {
Location Location
Polygon *s2.Polygon
}
func (f *Feature) Encode(w io.Writer) error {
// Neither JSON nor s2.Polygon have self-terminating encoding implementations, meh.
// Format is thus <len><location json> <len><polygon> for each feature.
if locBuf, err := json.Marshal(f.Location); err != nil {
return fmt.Errorf("encode location: %w", err)
} else if err := binary.Write(w, binary.LittleEndian, uint32(len(locBuf))); err != nil {
return fmt.Errorf("write size: %w", err)
} else if _, err := w.Write(locBuf); err != nil {
return fmt.Errorf("write location: %w", err)
}
polyBuf := bytes.NewBuffer(nil)
if err := f.Polygon.Encode(polyBuf); err != nil {
return fmt.Errorf("encode polygon: %w", err)
} else if err := binary.Write(w, binary.LittleEndian, uint32(polyBuf.Len())); err != nil {
return fmt.Errorf("write size: %w", err)
} else if _, err := polyBuf.WriteTo(w); err != nil {
return fmt.Errorf("write polygon: %w", err)
}
return nil
}
func (f *Feature) Decode(r io.Reader) error {
var l uint32
if err := binary.Read(r, binary.LittleEndian, &l); err != nil {
return fmt.Errorf("read location length: %w", err)
}
locBuf := make([]byte, l)
if _, err := io.ReadFull(r, locBuf); err != nil {
return fmt.Errorf("read location: %w", unexpectedEOF(err))
}
if err := json.Unmarshal(locBuf, &f.Location); err != nil {
return fmt.Errorf("decode location: %w", unexpectedEOF(err))
}
if err := binary.Read(r, binary.LittleEndian, &l); err != nil {
return fmt.Errorf("read polygon length: %w", unexpectedEOF(err))
}
polyBuf := make([]byte, l)
if _, err := io.ReadFull(r, polyBuf); err != nil {
return fmt.Errorf("read polygon: %w", unexpectedEOF(err))
}
f.Polygon = &s2.Polygon{}
if err := f.Polygon.Decode(bytes.NewReader(polyBuf)); err != nil {
return fmt.Errorf("bad polygon in geometry: %w", unexpectedEOF(err))
}
return nil
}
func LoadEncoded(r io.Reader) ([]Feature, error) {
var result []Feature
for i := 0; ; i++ {
var f Feature
if err := f.Decode(r); err != nil {
if errors.Is(err, io.EOF) {
break
}
return nil, fmt.Errorf("decode feature %d: %w", i, err)
}
result = append(result, f)
}
return result, nil
}
func LoadGeoJSON(fc geojson.FeatureCollection) (FeatureCollection, error) {
features := make(FeatureCollection, 0, len(fc.Features))
for _, f := range fc.Features {
poly, err := polygonFromGeometry(f.Geometry)
if err != nil {
return nil, fmt.Errorf("bad polygon in geometry: %w", err)
}
features = append(features, Feature{
Location: getLocationStrings(f.Properties),
Polygon: poly,
})
}
return features, nil
}
func unexpectedEOF(err error) error {
if err == io.EOF {
return io.ErrUnexpectedEOF
} else {
return err
}
}