Skip to content

Commit e4adc40

Browse files
committed
Support for STAC 1.1
1 parent 292411d commit e4adc40

29 files changed

+1351
-40
lines changed

asset.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ type Asset struct {
1313
Description string `json:"description,omitempty"`
1414
Created string `json:"created,omitempty"`
1515
Roles []string `json:"roles,omitempty"`
16+
Bands []*Band `json:"bands,omitempty"`
1617
Extensions []Extension `json:"-"`
1718
}
1819

@@ -47,6 +48,16 @@ func EncodeAssets(assets map[string]*Asset) (map[string]any, []string, error) {
4748
return nil, nil, err
4849
}
4950
}
51+
52+
bandMaps, uris, err := EncodeBands(asset.Bands)
53+
if err != nil {
54+
return nil, nil, err
55+
}
56+
if len(bandMaps) > 0 {
57+
extensionUris = append(extensionUris, uris...)
58+
assetMap["bands"] = bandMaps
59+
}
60+
5061
assetsMap[key] = assetMap
5162
}
5263
return assetsMap, extensionUris, nil

asset_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"testing"
66

77
"github.com/planetlabs/go-stac"
8-
"github.com/planetlabs/go-stac/extensions/pl"
8+
"github.com/planetlabs/go-stac/extensions/pl/v1"
99
"github.com/stretchr/testify/assert"
1010
"github.com/stretchr/testify/require"
1111
)

band.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package stac
2+
3+
import (
4+
"regexp"
5+
6+
"github.com/go-viper/mapstructure/v2"
7+
)
8+
9+
var bandExtensions = newExtensionRegistry()
10+
11+
func RegisterBandExtension(pattern *regexp.Regexp, provider ExtensionProvider) {
12+
bandExtensions.register(pattern, provider)
13+
}
14+
15+
func GetBandExtension(uri string) Extension {
16+
return bandExtensions.get(uri)
17+
}
18+
19+
type Band struct {
20+
Name string `json:"name,omitempty"`
21+
Description string `json:"description,omitempty"`
22+
NoData any `json:"nodata,omitempty"`
23+
DataType string `json:"data_type,omitempty"`
24+
Statistics *Statistics `json:"statistics,omitempty"`
25+
Unit string `json:"unit,omitempty"`
26+
Extensions []Extension `json:"-"`
27+
}
28+
29+
type Statistics struct {
30+
Mean *float64 `json:"mean,omitempty"`
31+
Minimum *float64 `json:"minimum,omitempty"`
32+
Maximum *float64 `json:"maximum,omitempty"`
33+
Stdev *float64 `json:"stdev,omitempty"`
34+
ValidPercent *float64 `json:"valid_percent,omitempty"`
35+
Count *int `json:"count,omitempty"`
36+
}
37+
38+
func encodeBand(band *Band) (map[string]any, []string, error) {
39+
extensionUris := []string{}
40+
bandMap := map[string]any{}
41+
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
42+
TagName: "json",
43+
Result: &bandMap,
44+
})
45+
if err != nil {
46+
return nil, nil, err
47+
}
48+
if err := decoder.Decode(band); err != nil {
49+
return nil, nil, err
50+
}
51+
for _, extension := range band.Extensions {
52+
extensionUris = append(extensionUris, extension.URI())
53+
if err := extension.Encode(bandMap); err != nil {
54+
return nil, nil, err
55+
}
56+
}
57+
return bandMap, extensionUris, nil
58+
}
59+
60+
func EncodeBands(bands []*Band) ([]map[string]any, []string, error) {
61+
extensionUris := []string{}
62+
bandMaps := make([]map[string]any, len(bands))
63+
for i, band := range bands {
64+
bandMap, uris, err := encodeBand(band)
65+
if err != nil {
66+
return nil, nil, err
67+
}
68+
bandMaps[i] = bandMap
69+
extensionUris = append(extensionUris, uris...)
70+
}
71+
return bandMaps, extensionUris, nil
72+
}

catalog.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package stac
22

33
import (
44
"encoding/json"
5+
"errors"
56
"fmt"
67
"regexp"
78

@@ -116,6 +117,9 @@ func (catalog *Catalog) UnmarshalJSON(data []byte) error {
116117
continue
117118
}
118119
if err := extension.Decode(catalogMap); err != nil {
120+
if errors.Is(err, ErrExtensionDoesNotApply) {
121+
continue
122+
}
119123
return fmt.Errorf("decoding error for %s: %w", uri, err)
120124
}
121125
catalog.Extensions = append(catalog.Extensions, extension)

collection.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ package stac
22

33
import (
44
"encoding/json"
5+
"errors"
56
"fmt"
67
"regexp"
78

9+
"github.com/Masterminds/semver/v3"
810
"github.com/go-viper/mapstructure/v2"
911
)
1012

@@ -20,6 +22,7 @@ type Collection struct {
2022
Summaries map[string]any `json:"summaries,omitempty"`
2123
Links []*Link `json:"links"`
2224
Assets map[string]*Asset `json:"assets,omitempty"`
25+
ItemAssets map[string]*Asset `json:"item_assets,omitempty"`
2326
Extensions []Extension `json:"-"`
2427
}
2528

@@ -78,6 +81,36 @@ func (collection Collection) MarshalJSON() ([]byte, error) {
7881
extensionUris := []string{}
7982
lookup := map[string]bool{}
8083

84+
assetsMap, assetExtensionUris, err := EncodeAssets(collection.Assets)
85+
if err != nil {
86+
return nil, err
87+
}
88+
if len(assetsMap) > 0 {
89+
collectionMap["assets"] = assetsMap
90+
}
91+
92+
for _, uri := range assetExtensionUris {
93+
if !lookup[uri] {
94+
extensionUris = append(extensionUris, uri)
95+
lookup[uri] = true
96+
}
97+
}
98+
99+
itemAssetsMap, itemAssetExtensionUris, err := EncodeAssets(collection.ItemAssets)
100+
if err != nil {
101+
return nil, err
102+
}
103+
if len(itemAssetsMap) > 0 {
104+
collectionMap["item_assets"] = itemAssetsMap
105+
}
106+
107+
for _, uri := range itemAssetExtensionUris {
108+
if !lookup[uri] {
109+
extensionUris = append(extensionUris, uri)
110+
lookup[uri] = true
111+
}
112+
}
113+
81114
for _, extension := range collection.Extensions {
82115
if err := extension.Encode(collectionMap); err != nil {
83116
return nil, err
@@ -140,11 +173,33 @@ func (collection *Collection) UnmarshalJSON(data []byte) error {
140173
continue
141174
}
142175
if err := extension.Decode(collectionMap); err != nil {
176+
if errors.Is(err, ErrExtensionDoesNotApply) {
177+
continue
178+
}
143179
return fmt.Errorf("decoding error for %s: %w", uri, err)
144180
}
145181
collection.Extensions = append(collection.Extensions, extension)
146182
}
147183

184+
if err := decodeExtendedAssets(collectionMap, "assets", collection.Assets, extensionUris); err != nil {
185+
return err
186+
}
187+
188+
// Item assets were added to collections in version 1.1.0
189+
itemAssetsConstraint, err := semver.NewConstraint(">= 1.1.0")
190+
if err != nil {
191+
return fmt.Errorf("could not parse version constraint: %w", err)
192+
}
193+
194+
if collectionVersion, err := semver.NewVersion(collection.Version); err == nil && itemAssetsConstraint.Check(collectionVersion) {
195+
if err := decodeExtendedAssets(collectionMap, "item_assets", collection.ItemAssets, extensionUris); err != nil {
196+
return err
197+
}
198+
} else {
199+
// prior to 1.1.0, the item assets extension is used
200+
collection.ItemAssets = nil
201+
}
202+
148203
if err := decodeExtendedLinks(collectionMap, collection.Links, extensionUris); err != nil {
149204
return err
150205
}

0 commit comments

Comments
 (0)