Skip to content

Commit 1326dea

Browse files
committed
Implement #370 for Satellite Extension
1 parent 59b93c5 commit 1326dea

File tree

3 files changed

+151
-3
lines changed

3 files changed

+151
-3
lines changed

pystac/extensions/sat.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,13 @@ def relative_orbit(self, v: Optional[int]) -> None:
9595
def get_schema_uri(cls) -> str:
9696
return SCHEMA_URI
9797

98-
@staticmethod
99-
def ext(obj: T) -> "SatExtension[T]":
98+
@classmethod
99+
def ext(cls, obj: T) -> "SatExtension[T]":
100100
if isinstance(obj, pystac.Item):
101+
cls.validate_has_extension(obj)
101102
return cast(SatExtension[T], ItemSatExtension(obj))
102103
elif isinstance(obj, pystac.Asset):
104+
cls.validate_has_extension(obj)
103105
return cast(SatExtension[T], AssetSatExtension(obj))
104106
else:
105107
raise pystac.ExtensionTypeError(

tests/data-files/sat/sentinel-1.json

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
{
2+
"stac_version": "1.0.0-rc.1",
3+
"stac_extensions": [
4+
"https://stac-extensions.github.io/sar/v1.0.0/schema.json",
5+
"https://stac-extensions.github.io/sat/v1.0.0/schema.json"
6+
],
7+
"id": "S1A_IW_SLC__1SDV_20150305T051937_20150305T052005_004892_006196_ABBB",
8+
"type": "Feature",
9+
"bbox": [
10+
9.768469,
11+
40.479584,
12+
13.182633,
13+
42.516747
14+
],
15+
"geometry": {
16+
"type": "Polygon",
17+
"coordinates": [
18+
[
19+
[
20+
12.756503,
21+
40.479584
22+
],
23+
[
24+
9.768469,
25+
40.877575
26+
],
27+
[
28+
10.115331,
29+
42.516747
30+
],
31+
[
32+
13.182633,
33+
42.119347
34+
],
35+
[
36+
12.756503,
37+
40.479584
38+
]
39+
]
40+
]
41+
},
42+
"properties": {
43+
"title": "Sentinel-1A SLC IW SDV T95 2015-03-05T05:19:37/2015-03-05T05:20:05",
44+
"datetime": "2015-03-05T05:19:37.9237880Z",
45+
"start_datetime": "2015-03-05T05:19:37.9237880Z",
46+
"end_datetime": "2015-03-05T05:20:05.2359650Z",
47+
"platform": "sentinel-1a",
48+
"constellation": "sentinel-1",
49+
"instruments": [
50+
"c-sar"
51+
],
52+
"sat:platform_international_designator": "2014-016A",
53+
"sat:orbit_state": "descending",
54+
"sat:absolute_orbit": 4892,
55+
"sat:relative_orbit": 95,
56+
"sat:anx_datetime": "2015-03-05T04:41:46.5788740Z",
57+
"sar:instrument_mode": "IW",
58+
"sar:polarizations": [
59+
"VV",
60+
"VH"
61+
],
62+
"sar:resolution_range": 3.5,
63+
"sar:resolution_azimuth": 22,
64+
"sar:pixel_spacing_range": 2.3,
65+
"sar:pixel_spacing_azimuth": 14.1,
66+
"sar:looks_range": 1,
67+
"sar:looks_azimuth": 1,
68+
"sar:looks_equivalent_number": 1,
69+
"sar:frequency_band": "C",
70+
"sar:center_frequency": 5.405,
71+
"sar:product_type": "SLC"
72+
},
73+
"assets": {
74+
"noises_iw1_vh": {
75+
"href": "./annotation/calibration/noise-s1a-iw1-slc-vh-20150305t051939-20150305t052005-004892-006196-001.xml",
76+
"title": "Calibration Schema",
77+
"type": "text/xml",
78+
"sar:polarizations": [
79+
"VH"
80+
]
81+
},
82+
"calibrations_iw1_vh": {
83+
"href": "./annotation/calibration/calibration-s1a-iw1-slc-vh-20150305t051939-20150305t052005-004892-006196-001.xml",
84+
"title": "Noise Schema",
85+
"type": "text/xml",
86+
"sar:polarizations": [
87+
"VH"
88+
]
89+
},
90+
"products_iw1_vh": {
91+
"href": "./annotation/s1a-iw1-slc-vh-20150305t051939-20150305t052005-004892-006196-001.xml",
92+
"title": "Product Schema",
93+
"type": "text/xml",
94+
"sar:polarizations": [
95+
"VH"
96+
]
97+
},
98+
"measurement_iw1_vh": {
99+
"href": "./measurement/s1a-iw1-slc-vh-20150305t051939-20150305t052005-004892-006196-001.tiff",
100+
"title": "Measurements",
101+
"type": "image/tiff",
102+
"sar:polarizations": [
103+
"VH"
104+
]
105+
},
106+
"thumbnail": {
107+
"href": "./preview/quick-look.png",
108+
"title": "Thumbnail",
109+
"type": "image/png"
110+
}
111+
},
112+
"links": [
113+
{
114+
"rel": "self",
115+
"href": "https://example.com/collections/sentinel-1/items/S1A_EW_GRDM_1SSH_20181103T235855_20181103T235955_024430_02AD5D_5616"
116+
},
117+
{
118+
"rel": "parent",
119+
"href": "https://example.com/collections/sentinel-1"
120+
},
121+
{
122+
"rel": "root",
123+
"href": "https://example.com/collections"
124+
}
125+
]
126+
}

tests/extensions/test_sat.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import pystac
88
from pystac.extensions import sat
99
from pystac.extensions.sat import SatExtension
10+
from tests.utils import TestCases
1011

1112

1213
def make_item() -> pystac.Item:
@@ -25,6 +26,7 @@ class SatTest(unittest.TestCase):
2526
def setUp(self) -> None:
2627
super().setUp()
2728
self.item = make_item()
29+
self.sentinel_example_uri = TestCases.get_path("data-files/sat/sentinel-1.json")
2830

2931
def test_stac_extensions(self) -> None:
3032
self.assertTrue(SatExtension.has_extension(self.item))
@@ -90,7 +92,7 @@ def test_from_dict(self) -> None:
9092
"geometry": None,
9193
"links": [],
9294
"assets": {},
93-
"stac_extensions": ["sat"],
95+
"stac_extensions": [SatExtension.get_schema_uri()],
9496
}
9597
item = pystac.Item.from_dict(d)
9698
self.assertEqual(orbit_state, SatExtension.ext(item).orbit_state)
@@ -121,3 +123,21 @@ def test_clear_relative_orbit(self) -> None:
121123
SatExtension.ext(self.item).relative_orbit = None
122124
self.assertIsNone(SatExtension.ext(self.item).relative_orbit)
123125
self.item.validate()
126+
127+
def test_extension_not_implemented(self) -> None:
128+
# Should raise exception if Item does not include extension URI
129+
item = pystac.Item.from_file(self.sentinel_example_uri)
130+
item.stac_extensions.remove(SatExtension.get_schema_uri())
131+
132+
with self.assertRaises(pystac.ExtensionNotImplemented):
133+
_ = SatExtension.ext(item)
134+
135+
# Should raise exception if owning Item does not include extension URI
136+
asset = item.assets["measurement_iw1_vh"]
137+
138+
with self.assertRaises(pystac.ExtensionNotImplemented):
139+
_ = SatExtension.ext(asset)
140+
141+
# Should succeed if Asset has no owner
142+
ownerless_asset = pystac.Asset.from_dict(asset.to_dict())
143+
_ = SatExtension.ext(ownerless_asset)

0 commit comments

Comments
 (0)