-
Notifications
You must be signed in to change notification settings - Fork 0
/
feature.go
164 lines (141 loc) · 3.98 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
160
161
162
163
164
//
// Copyright (C) 2021 Dmitry Kolesnikov
//
// This file may be modified and distributed under the terms
// of the MIT license. See the LICENSE file for details.
// https://github.com/fogfish/geojson
//
package geojson
import (
"encoding/json"
"github.com/fogfish/curie"
)
// Feature object represents a spatially bounded thing.
// This object contains geometry, a common identifier, and properties.
// The value of the properties is any JSON object, typically defined by
// an application.
//
// The library uses a type safe notation for the feature's property
// definition instead of generic interface{} type. It uses type tagging
// technique (or embedding):
//
// type MyType struct {
// geojson.Feature
// Name string `json:"name,omitempty"`
// }
type Feature struct {
ID curie.IRI `json:"-"`
Geometry Geometry `json:"-"`
}
func (fea Feature) BoundingBox() BoundingBox { return fea.Geometry.BoundingBox() }
// EncodeGeoJSON is a helper function to implement GeoJSON codec
//
// func (x MyType) MarshalJSON() ([]byte, error) {
// type tStruct MyType
// return x.Feature.EncodeGeoJSON(tStruct(x))
// }
func (fea Feature) EncodeGeoJSON(props interface{}) ([]byte, error) {
properties, err := json.Marshal(props)
if err != nil {
return nil, err
}
any := struct {
Type string `json:"type"`
BBox BoundingBox `json:"bbox,omitempty"`
ID curie.IRI `json:"id,omitempty"`
Geometry Geometry `json:"geometry,omitempty"`
Properties json.RawMessage `json:"properties,omitempty"`
}{
ID: fea.ID,
Type: "Feature",
BBox: fea.Geometry.BoundingBox(),
Geometry: fea.Geometry,
Properties: properties,
}
return json.Marshal(any)
}
// anyGeoJSON is an internal type used for decode of GeoJSON
type anyGeoJSON struct {
Type string `json:"type"`
ID curie.IRI `json:"id,omitempty"`
Geometry json.RawMessage `json:"geometry,omitempty"`
Properties json.RawMessage `json:"properties,omitempty"`
}
// DecodeGeoJSON is a helper function to implement GeoJSON codec
//
// func (x *MyType) UnmarshalJSON(b []byte) error {
// type tStruct *MyType
// return x.Feature.DecodeGeoJSON(b, tStruct(x))
// }
func (fea *Feature) DecodeGeoJSON(bytes []byte, props interface{}) error {
any := anyGeoJSON{}
if err := json.Unmarshal(bytes, &any); err != nil {
return err
}
if any.Type != "Feature" {
return ErrorUnsupportedType
}
return fea.decodeAnyGeoJSON(&any, props)
}
func (fea *Feature) decodeAnyGeoJSON(any *anyGeoJSON, props interface{}) error {
if any.Geometry != nil {
geo, err := decodeGeometry(any.Geometry)
if err != nil {
return err
}
fea.Geometry = geo
}
if any.Properties != nil {
if err := json.Unmarshal(any.Properties, &props); err != nil {
return err
}
}
fea.ID = any.ID
return nil
}
// New Feature from Geometry
func New(id curie.IRI, geometry Geometry) Feature {
return Feature{ID: id, Geometry: geometry}
}
// NewPoint ⟼ Feature[Point]
func NewPoint(id curie.IRI, coords Coord) Feature {
return Feature{
ID: id,
Geometry: &Point{Coords: coords},
}
}
// NewMultiPoint ⟼ Feature[MultiPoint]
func NewMultiPoint(id curie.IRI, coords Curve) Feature {
return Feature{
ID: id,
Geometry: &MultiPoint{Coords: coords},
}
}
// NewLineString ⟼ Feature[LineString]
func NewLineString(id curie.IRI, coords Curve) Feature {
return Feature{
ID: id,
Geometry: &LineString{Coords: coords},
}
}
// NewMultiLineString ⟼ Feature[MultiLineString]
func NewMultiLineString(id curie.IRI, coords Surface) Feature {
return Feature{
ID: id,
Geometry: &MultiLineString{Coords: coords},
}
}
// NewPolygon ⟼ Feature[Polygon]
func NewPolygon(id curie.IRI, coords Surface) Feature {
return Feature{
ID: id,
Geometry: &Polygon{Coords: coords},
}
}
// NewMultiPolygon ⟼ Feature[MultiPolygon]
func NewMultiPolygon(id curie.IRI, coords ...Surface) Feature {
return Feature{
ID: id,
Geometry: &MultiPolygon{Coords: coords},
}
}