Skip to content

Commit 646711f

Browse files
authored
Merge pull request #114 from planetlabs/extensions
Core types and extensions
2 parents 3f2381b + 5b60975 commit 646711f

File tree

15 files changed

+732
-0
lines changed

15 files changed

+732
-0
lines changed

asset.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package stac
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
7+
"github.com/mitchellh/mapstructure"
8+
)
9+
10+
type Asset struct {
11+
Type string `json:"type,omitempty"`
12+
Href string `json:"href"`
13+
Title string `json:"title,omitempty"`
14+
Description string `json:"description,omitempty"`
15+
Created string `json:"created,omitempty"`
16+
Roles []string `json:"roles,omitempty"`
17+
Extensions []AssetExtension `json:"-"`
18+
}
19+
20+
var _ json.Marshaler = (*Asset)(nil)
21+
22+
type AssetExtension interface {
23+
Apply(*Asset)
24+
URI() string
25+
}
26+
27+
func (asset Asset) MarshalJSON() ([]byte, error) {
28+
assetMap := map[string]any{}
29+
decoder, decoderErr := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
30+
TagName: "json",
31+
Result: &assetMap,
32+
})
33+
if decoderErr != nil {
34+
return nil, decoderErr
35+
}
36+
37+
decodeErr := decoder.Decode(asset)
38+
if decodeErr != nil {
39+
return nil, decodeErr
40+
}
41+
42+
for _, extension := range asset.Extensions {
43+
extension.Apply(&asset)
44+
if decodeErr := decoder.Decode(extension); decodeErr != nil {
45+
return nil, fmt.Errorf("trouble encoding JSON for %s asset: %w", extension.URI(), decodeErr)
46+
}
47+
}
48+
49+
return json.Marshal(assetMap)
50+
}

asset_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package stac_test
2+
3+
import (
4+
"encoding/json"
5+
"testing"
6+
7+
"github.com/planetlabs/go-stac"
8+
"github.com/planetlabs/go-stac/extensions/pl"
9+
"github.com/stretchr/testify/assert"
10+
"github.com/stretchr/testify/require"
11+
)
12+
13+
func TestAssetMarshal(t *testing.T) {
14+
asset := &stac.Asset{
15+
Title: "An Image",
16+
Href: "https://example.com/image.tif",
17+
Type: "image/tiff",
18+
Roles: []string{"data", "reflectance"},
19+
}
20+
21+
data, err := json.Marshal(asset)
22+
require.Nil(t, err)
23+
24+
expected := `{
25+
"title": "An Image",
26+
"href": "https://example.com/image.tif",
27+
"type": "image/tiff",
28+
"roles": ["data", "reflectance"]
29+
}`
30+
31+
assert.JSONEq(t, expected, string(data))
32+
}
33+
34+
func TestAssetExtendedMarshal(t *testing.T) {
35+
asset := &stac.Asset{
36+
Href: "https://example.com/image.tif",
37+
Type: "image/tiff",
38+
Extensions: []stac.AssetExtension{
39+
&pl.Asset{
40+
AssetType: "ortho_analytic_4b_sr",
41+
BundleType: "analytic_sr_udm2",
42+
},
43+
},
44+
}
45+
46+
data, err := json.Marshal(asset)
47+
require.Nil(t, err)
48+
49+
expected := `{
50+
"href": "https://example.com/image.tif",
51+
"type": "image/tiff",
52+
"pl:asset_type": "ortho_analytic_4b_sr",
53+
"pl:bundle_type": "analytic_sr_udm2"
54+
}`
55+
56+
assert.JSONEq(t, expected, string(data))
57+
}

catalog.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package stac
2+
3+
import (
4+
"encoding/json"
5+
6+
"github.com/mitchellh/mapstructure"
7+
)
8+
9+
type Catalog struct {
10+
Version string `json:"stac_version"`
11+
Id string `json:"id"`
12+
Title string `json:"title,omitempty"`
13+
Description string `json:"description"`
14+
Links []*Link `json:"links"`
15+
ConformsTo []string `json:"conformsTo,omitempty"`
16+
}
17+
18+
var _ json.Marshaler = (*Catalog)(nil)
19+
20+
func (catalog Catalog) MarshalJSON() ([]byte, error) {
21+
collectionMap := map[string]any{
22+
"type": "Catalog",
23+
}
24+
decoder, decoderErr := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
25+
TagName: "json",
26+
Result: &collectionMap,
27+
})
28+
if decoderErr != nil {
29+
return nil, decoderErr
30+
}
31+
32+
decodeErr := decoder.Decode(catalog)
33+
if decodeErr != nil {
34+
return nil, decodeErr
35+
}
36+
37+
return json.Marshal(collectionMap)
38+
}

catalog_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package stac_test
2+
3+
import (
4+
"encoding/json"
5+
"testing"
6+
7+
"github.com/planetlabs/go-stac"
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
func TestCatalogMarshal(t *testing.T) {
13+
catalog := &stac.Catalog{
14+
Version: "1.0.0",
15+
Id: "catalog-id",
16+
Description: "Test Catalog",
17+
Links: []*stac.Link{
18+
{Href: "https://example.com/stac/catalog", Rel: "self", Type: "application/json"},
19+
},
20+
}
21+
22+
data, err := json.Marshal(catalog)
23+
require.Nil(t, err)
24+
25+
expected := `{
26+
"type": "Catalog",
27+
"id": "catalog-id",
28+
"description": "Test Catalog",
29+
"links": [
30+
{
31+
"href": "https://example.com/stac/catalog",
32+
"rel": "self",
33+
"type": "application/json"
34+
}
35+
],
36+
"stac_version": "1.0.0"
37+
}`
38+
39+
assert.JSONEq(t, expected, string(data))
40+
}

collection.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package stac
2+
3+
import (
4+
"encoding/json"
5+
6+
"github.com/mitchellh/mapstructure"
7+
)
8+
9+
type Collection struct {
10+
Version string `json:"stac_version"`
11+
Id string `json:"id"`
12+
Title string `json:"title,omitempty"`
13+
Description string `json:"description"`
14+
Keywords []string `json:"keywords,omitempty"`
15+
License string `json:"license"`
16+
Providers []*Provider `json:"providers,omitempty"`
17+
Extent *Extent `json:"extent"`
18+
Summaries map[string]any `json:"summaries,omitempty"`
19+
Links []*Link `json:"links"`
20+
Assets map[string]*Asset `json:"assets,omitempty"`
21+
}
22+
23+
type Provider struct {
24+
Name string `json:"name"`
25+
Description string `json:"description,omitempty"`
26+
Roles []string `json:"roles,omitempty"`
27+
Url string `json:"url,omitempty"`
28+
}
29+
30+
type Extent struct {
31+
Spatial *SpatialExtent `json:"spatial,omitempty"`
32+
Temporal *TemporalExtent `json:"temporal,omitempty"`
33+
}
34+
35+
type SpatialExtent struct {
36+
Bbox [][]float64 `json:"bbox"`
37+
}
38+
39+
type TemporalExtent struct {
40+
Interval [][]any `json:"interval"`
41+
}
42+
43+
var _ json.Marshaler = (*Collection)(nil)
44+
45+
func (collection Collection) MarshalJSON() ([]byte, error) {
46+
collectionMap := map[string]any{
47+
"type": "Collection",
48+
}
49+
decoder, decoderErr := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
50+
TagName: "json",
51+
Result: &collectionMap,
52+
})
53+
if decoderErr != nil {
54+
return nil, decoderErr
55+
}
56+
57+
decodeErr := decoder.Decode(collection)
58+
if decodeErr != nil {
59+
return nil, decodeErr
60+
}
61+
62+
return json.Marshal(collectionMap)
63+
}
64+
65+
type CollectionsList struct {
66+
Collections []*Collection `json:"collections"`
67+
Links []*Link `json:"links"`
68+
}

collection_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package stac_test
2+
3+
import (
4+
"encoding/json"
5+
"testing"
6+
7+
"github.com/planetlabs/go-stac"
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
func TestCollectionMarshal(t *testing.T) {
13+
collection := &stac.Collection{
14+
Version: "1.0.0",
15+
Id: "collection-id",
16+
Description: "Test Collection",
17+
License: "various",
18+
Links: []*stac.Link{
19+
{Href: "https://example.com/stac/collections/collection-id", Rel: "self", Type: "application/json"},
20+
},
21+
Extent: &stac.Extent{
22+
Spatial: &stac.SpatialExtent{
23+
Bbox: [][]float64{{-180, -90, 180, 90}},
24+
},
25+
},
26+
}
27+
28+
data, err := json.Marshal(collection)
29+
require.Nil(t, err)
30+
31+
expected := `{
32+
"type": "Collection",
33+
"id": "collection-id",
34+
"description": "Test Collection",
35+
"extent": {
36+
"spatial": {
37+
"bbox": [
38+
[-180, -90, 180, 90]
39+
]
40+
}
41+
},
42+
"license": "various",
43+
"links": [
44+
{
45+
"href": "https://example.com/stac/collections/collection-id",
46+
"rel": "self",
47+
"type": "application/json"
48+
}
49+
],
50+
"stac_version": "1.0.0"
51+
}`
52+
53+
assert.JSONEq(t, expected, string(data))
54+
}

extensions/pl/pl.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package pl
2+
3+
import "github.com/planetlabs/go-stac"
4+
5+
const extensionUri = "https://planetlabs.github.io/stac-extension/v1.0.0-beta.1/schema.json"
6+
7+
type Asset struct {
8+
AssetType string `json:"pl:asset_type,omitempty"`
9+
BundleType string `json:"pl:bundle_type,omitempty"`
10+
}
11+
12+
var _ stac.AssetExtension = (*Asset)(nil)
13+
14+
func (*Asset) URI() string {
15+
return extensionUri
16+
}
17+
18+
func (*Asset) Apply(*stac.Asset) {}
19+
20+
type Item struct {
21+
ItemType string `json:"pl:item_type,omitempty"`
22+
PixelResolution float64 `json:"pl:pixel_resolution,omitempty"`
23+
PublishingStage string `json:"pl:publishing_stage,omitempty"`
24+
QualityCategory string `json:"pl:quality_category,omitempty"`
25+
StripId string `json:"pl:strip_id,omitempty"`
26+
BlackFill *float64 `json:"pl:black_fill,omitempty"`
27+
ClearPercent *float64 `json:"pl:clear_percent,omitempty"`
28+
GridCell *int `json:"pl:grid_cell,omitempty"`
29+
GroundControl *bool `json:"pl:ground_control,omitempty"`
30+
GroundControlRatio *float64 `json:"pl:ground_control_ratio,omitempty"`
31+
}
32+
33+
var _ stac.ItemExtension = (*Item)(nil)
34+
35+
func (*Item) URI() string {
36+
return extensionUri
37+
}
38+
39+
func (*Item) Apply(*stac.Item) {}

0 commit comments

Comments
 (0)