Skip to content

Commit 95993f9

Browse files
authored
Merge pull request #219 from planetlabs/custom-geometry
Support for unmarshalling custom geometry types
2 parents cb91d24 + 0440d1a commit 95993f9

File tree

2 files changed

+130
-0
lines changed

2 files changed

+130
-0
lines changed

item.go

+36
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"encoding/json"
55
"errors"
66
"fmt"
7+
"reflect"
78
"regexp"
89

910
"github.com/go-viper/mapstructure/v2"
@@ -122,6 +123,14 @@ func (item Item) MarshalJSON() ([]byte, error) {
122123
return json.Marshal(itemMap)
123124
}
124125

126+
var geometryUnmarshaler json.Unmarshaler
127+
128+
// GeometryUnmarshaler allows a custom geometry type that satisfies the json.Unmarshaler interface to be provided.
129+
// If not set, item geometries will be unmarshaled as a map[string]any.
130+
func GeometryUnmarshaler(g json.Unmarshaler) {
131+
geometryUnmarshaler = g
132+
}
133+
125134
func (item *Item) UnmarshalJSON(data []byte) error {
126135
itemMap := map[string]any{}
127136
if err := json.Unmarshal(data, &itemMap); err != nil {
@@ -166,6 +175,33 @@ func (item *Item) UnmarshalJSON(data []byte) error {
166175
return err
167176
}
168177

178+
if geometryUnmarshaler != nil {
179+
geometryMap, ok := itemMap["geometry"]
180+
if ok {
181+
geometryData, err := json.Marshal(geometryMap)
182+
if err != nil {
183+
return err
184+
}
185+
186+
var gv any
187+
gvt := reflect.TypeOf(geometryUnmarshaler)
188+
if gvt.Kind() == reflect.Pointer {
189+
gv = reflect.New(gvt.Elem()).Interface()
190+
} else {
191+
gv = reflect.New(gvt).Elem().Interface()
192+
}
193+
g, ok := gv.(json.Unmarshaler)
194+
if !ok {
195+
return fmt.Errorf("expected %#v to satisfy the json.Unmarshaler interface", gv)
196+
}
197+
if err := g.UnmarshalJSON(geometryData); err != nil {
198+
return err
199+
}
200+
201+
item.Geometry = g
202+
}
203+
}
204+
169205
return nil
170206
}
171207

item_test.go

+94
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,97 @@ func TestItemMarshal(t *testing.T) {
6363

6464
assert.JSONEq(t, expected, string(data))
6565
}
66+
67+
func TestItemUnmarshal(t *testing.T) {
68+
data := `{
69+
"type": "Feature",
70+
"stac_version": "1.0.0",
71+
"id": "item-id",
72+
"geometry": {
73+
"type": "Point",
74+
"coordinates": [1, 2]
75+
},
76+
"properties": {
77+
"test": "value"
78+
},
79+
"links": [
80+
{
81+
"rel": "self",
82+
"href": "https://example.com/stac/item-id"
83+
}
84+
],
85+
"assets": {
86+
"thumbnail": {
87+
"title": "Thumbnail",
88+
"href": "https://example.com/stac/item-id/thumb.png",
89+
"type": "image/png"
90+
}
91+
}
92+
}`
93+
94+
item := &stac.Item{}
95+
require.NoError(t, json.Unmarshal([]byte(data), item))
96+
97+
assert.Equal(t, "item-id", item.Id)
98+
99+
require.NotNil(t, item.Geometry)
100+
101+
g, ok := item.Geometry.(map[string]any)
102+
require.True(t, ok)
103+
geometryType, ok := g["type"].(string)
104+
require.True(t, ok)
105+
assert.Equal(t, "Point", geometryType)
106+
}
107+
108+
type TestGeometry struct {
109+
data []byte
110+
}
111+
112+
func (g *TestGeometry) UnmarshalJSON(data []byte) error {
113+
g.data = data
114+
return nil
115+
}
116+
117+
func TestGeometryUnmarshal(t *testing.T) {
118+
original := &TestGeometry{}
119+
stac.GeometryUnmarshaler(original)
120+
defer stac.GeometryUnmarshaler(nil)
121+
122+
data := `{
123+
"type": "Feature",
124+
"stac_version": "1.0.0",
125+
"id": "item-id",
126+
"geometry": {
127+
"type": "Point",
128+
"coordinates": [1, 2]
129+
},
130+
"properties": {
131+
"test": "value"
132+
},
133+
"links": [
134+
{
135+
"rel": "self",
136+
"href": "https://example.com/stac/item-id"
137+
}
138+
],
139+
"assets": {
140+
"thumbnail": {
141+
"title": "Thumbnail",
142+
"href": "https://example.com/stac/item-id/thumb.png",
143+
"type": "image/png"
144+
}
145+
}
146+
}`
147+
148+
item := &stac.Item{}
149+
require.NoError(t, json.Unmarshal([]byte(data), item))
150+
151+
assert.Equal(t, "item-id", item.Id)
152+
153+
assert.Nil(t, original.data)
154+
require.NotNil(t, item.Geometry)
155+
156+
g, ok := item.Geometry.(*TestGeometry)
157+
require.True(t, ok)
158+
assert.NotNil(t, g.data)
159+
}

0 commit comments

Comments
 (0)