Skip to content

Commit 59b93c5

Browse files
committed
Implement #370 for SAR extension
1 parent aeaa6d9 commit 59b93c5

File tree

3 files changed

+132
-3
lines changed

3 files changed

+132
-3
lines changed

pystac/extensions/sar.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -304,15 +304,17 @@ def observation_direction(self, v: Optional[ObservationDirection]) -> None:
304304
def get_schema_uri(cls) -> str:
305305
return SCHEMA_URI
306306

307-
@staticmethod
308-
def ext(obj: T) -> "SarExtension[T]":
307+
@classmethod
308+
def ext(cls, obj: T) -> "SarExtension[T]":
309309
if isinstance(obj, pystac.Item):
310+
cls.validate_has_extension(obj)
310311
return cast(SarExtension[T], ItemSarExtension(obj))
311312
elif isinstance(obj, pystac.Asset):
313+
cls.validate_has_extension(obj)
312314
return cast(SarExtension[T], AssetSarExtension(obj))
313315
else:
314316
raise pystac.ExtensionTypeError(
315-
f"File extension does not apply to type {type(obj)}"
317+
f"SAR extension does not apply to type {type(obj)}"
316318
)
317319

318320

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

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
{
2+
"type": "Feature",
3+
"stac_version": "1.0.0-rc.1",
4+
"stac_extensions": [
5+
"https://stac-extensions.github.io/sar/v1.0.0/schema.json"
6+
],
7+
"id": "sentinel-1-example",
8+
"properties": {
9+
"datetime": "2018-11-03T23:58:55.617217Z",
10+
"start_datetime": "2018-11-03T23:58:55.121559Z",
11+
"end_datetime": "2018-11-03T23:59:55.112875Z",
12+
"platform": "sentinel-1a",
13+
"constellation": "sentinel-1",
14+
"instruments": [
15+
"c-sar"
16+
],
17+
"sar:instrument_mode": "EW",
18+
"sar:polarizations": [
19+
"HH"
20+
],
21+
"sar:resolution_range": 50,
22+
"sar:resolution_azimuth": 50,
23+
"sar:pixel_spacing_range": 25,
24+
"sar:pixel_spacing_azimuth": 25,
25+
"sar:looks_range": 3,
26+
"sar:looks_azimuth": 1,
27+
"sar:looks_equivalent_number": 2.7,
28+
"sar:frequency_band": "C",
29+
"sar:center_frequency": 5.405,
30+
"sar:product_type": "GRD"
31+
},
32+
"geometry": {
33+
"type": "Polygon",
34+
"coordinates": [
35+
[
36+
[
37+
-67.071648,
38+
-64.72924
39+
],
40+
[
41+
-65.087479,
42+
-56.674374
43+
],
44+
[
45+
-68.033211,
46+
-51.105831
47+
],
48+
[
49+
-70.275032,
50+
-59.805672
51+
],
52+
[
53+
-67.071648,
54+
-64.72924
55+
]
56+
]
57+
]
58+
},
59+
"links": [
60+
{
61+
"rel": "root",
62+
"href": "../../catalog.json",
63+
"type": "application/json"
64+
},
65+
{
66+
"rel": "parent",
67+
"href": "../collection.json",
68+
"type": "application/json"
69+
}
70+
],
71+
"assets": {
72+
"noises": {
73+
"href": "https://example.com/collections/sentinel-1/items/annotation/calibration/noise-s1a-ew-grd-hh-20181103t235855-20181103t235955-024430-02ad5d-001.xml",
74+
"type": "text/xml",
75+
"title": "Calibration Schema"
76+
},
77+
"calibrations": {
78+
"href": "https://example.com/collections/sentinel-1/items/annotation/calibration/calibration-s1a-ew-grd-hh-20181103t235855-20181103t235955-024430-02ad5d-001.xml",
79+
"type": "text/xml",
80+
"title": "Noise Schema"
81+
},
82+
"products": {
83+
"href": "https://example.com/collections/sentinel-1/items/annotation/s1a-ew-grd-hh-20181103t235855-20181103t235955-024430-02ad5d-001.xml",
84+
"type": "text/xml",
85+
"title": "Product Schema"
86+
},
87+
"measurement": {
88+
"href": "https://example.com/collections/sentinel-1/items/measurement/s1a-ew-grd-hh-20181103t235855-20181103t235955-024430-02ad5d-001.tiff",
89+
"type": "image/tiff",
90+
"title": "Measurements",
91+
"sar:polarizations": [
92+
"HH"
93+
]
94+
},
95+
"thumbnail": {
96+
"href": "https://example.com/collections/sentinel-1/items/preview/quick-look.png",
97+
"type": "image/png",
98+
"title": "Thumbnail"
99+
}
100+
},
101+
"bbox": [
102+
-70.275032,
103+
-64.72924,
104+
-65.087479,
105+
-51.105831
106+
]
107+
}

tests/extensions/test_sar.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import pystac
88
from pystac.extensions import sar
99
from pystac.extensions.sar import SarExtension
10+
from tests.utils import TestCases
1011

1112

1213
def make_item() -> pystac.Item:
@@ -24,6 +25,7 @@ class SarItemExtTest(unittest.TestCase):
2425
def setUp(self) -> None:
2526
super().setUp()
2627
self.item = make_item()
28+
self.sentinel_example_uri = TestCases.get_path("data-files/sar/sentinel-1.json")
2729

2830
def test_stac_extensions(self) -> None:
2931
self.assertTrue(SarExtension.has_extension(self.item))
@@ -141,6 +143,24 @@ def test_polarization_must_be_list(self) -> None:
141143
product_type,
142144
)
143145

146+
def test_extension_not_implemented(self) -> None:
147+
# Should raise exception if Item does not include extension URI
148+
item = pystac.Item.from_file(self.sentinel_example_uri)
149+
item.stac_extensions.remove(SarExtension.get_schema_uri())
150+
151+
with self.assertRaises(pystac.ExtensionNotImplemented):
152+
_ = SarExtension.ext(item)
153+
154+
# Should raise exception if owning Item does not include extension URI
155+
asset = item.assets["measurement"]
156+
157+
with self.assertRaises(pystac.ExtensionNotImplemented):
158+
_ = SarExtension.ext(asset)
159+
160+
# Should succeed if Asset has no owner
161+
ownerless_asset = pystac.Asset.from_dict(asset.to_dict())
162+
_ = SarExtension.ext(ownerless_asset)
163+
144164

145165
if __name__ == "__main__":
146166
unittest.main()

0 commit comments

Comments
 (0)