Skip to content

Commit e4da4ae

Browse files
committed
Move make_sentinel1_stac_item and test it
1 parent d35745e commit e4da4ae

File tree

4 files changed

+125
-91
lines changed

4 files changed

+125
-91
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ dev = [
1010
"pytest>=7.0",
1111
"pytest-cov>=5.0",
1212
"shapely>=2.1",
13+
"stac-validator>=3.6.0",
1314
"zarr>=2.18.3"
1415
]
1516

tests/test_20_sentinel1.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import shapely.geometry
66
import shapely.wkt
77
import xarray as xr
8+
from stac_validator import stac_validator
89

910
from xarray_sentinel import esa_safe, sentinel1
1011

@@ -503,3 +504,23 @@ def test_do_override_product_files() -> None:
503504
res = sentinel1.do_override_product_files(template, product_files)
504505

505506
assert "./annotation/s3-vv.xml" in res
507+
508+
509+
@pytest.mark.parametrize(
510+
"safe,annotation",
511+
[
512+
[SLC_S3, SLC_S3_VH_annotation],
513+
[SLC_IW, SLC_IW1_VV_annotation],
514+
[GRD_IW, GRD_IW_VV_annotation],
515+
],
516+
)
517+
def test_make_sentinel1_stac_item(safe, annotation) -> None:
518+
item = sentinel1.make_sentinel1_stac_item(
519+
"test", safe / "manifest.safe", annotation
520+
)
521+
522+
validator = stac_validator.StacValidate()
523+
# HACK: the geometry appears to be correct, but it doesn't validate
524+
result = validator.validate_dict(item | {"geometry": None})
525+
526+
assert result, validator.message

xarray_sentinel/esa_safe.py

Lines changed: 0 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -177,97 +177,6 @@ def parse_manifest_sentinel1(
177177
return attributes, files
178178

179179

180-
def make_sentinel1_stac_item(
181-
item_id: str,
182-
manifest_path: PathOrFileType,
183-
annotation: PathOrFileType,
184-
namespaces=SENTINEL1_NAMESPACES,
185-
) -> dict[str, Any]:
186-
manifest = ElementTree.parse(manifest_path).getroot()
187-
188-
product_information = parse_tag(annotation, ".//productInformation")
189-
image_information = parse_tag(annotation, ".//imageInformation")
190-
191-
coordinates = [
192-
[float(v) for v in token.split(",")]
193-
for token in findtext(manifest, ".//gml:coordinates").split()
194-
]
195-
coordinates += coordinates[:1]
196-
product_timeliness = findtext(manifest, ".//s1sarl1:productTimelinessCategory")
197-
product_timeliness_map = {
198-
"Fast-24h": {
199-
"product:timeliness_category": "STC",
200-
"product:timeliness": "PT24H",
201-
}
202-
}
203-
204-
stac_item = {
205-
"type": "Feature",
206-
"stac_version": "1.1.0",
207-
"stac_extensions": [
208-
"https://stac-extensions.github.io/product/v0.1.0/schema.json",
209-
"https://stac-extensions.github.io/processing/v1.2.0/schema.json",
210-
"https://stac-extensions.github.io/sat/v1.0.0/schema.json",
211-
"https://stac-extensions.github.io/view/v1.0.0/schema.json",
212-
"https://stac-extensions.github.io/sar/v1.2.0/schema.json",
213-
"https://stac-extensions.github.io/eopf/v1.0.0/schema.json",
214-
],
215-
"id": item_id,
216-
"properties": {
217-
"datetime": None,
218-
"start_datetime": findtext(manifest, ".//safe:startTime") + "Z",
219-
"end_datetime": findtext(manifest, ".//safe:stopTime") + "Z",
220-
"created": manifest.find(
221-
".//safe:processing", namespaces=namespaces
222-
).attrib["stop"]
223-
+ "Z",
224-
"platform": "sentinel-1"
225-
+ findtext(manifest, ".//safe:platform/safe:number").lower(),
226-
"instruments": ["sar"],
227-
"constellation": "sentinel-1",
228-
"product:type": "S01SSMSLC",
229-
"product:timeliness_category": product_timeliness_map[product_timeliness][
230-
"product:timeliness_category"
231-
],
232-
"product:timeliness": product_timeliness_map[product_timeliness][
233-
"product:timeliness"
234-
],
235-
"processing:software": manifest.find(
236-
".//safe:software", namespaces=namespaces
237-
).attrib,
238-
"sat:platform_international_designator": findtext(
239-
manifest, ".//safe:nssdcIdentifier"
240-
),
241-
"sat:absolute_orbit": int(findall(manifest, ".//safe:orbitNumber")[0]),
242-
"sat:relative_orbit": int(
243-
findall(manifest, ".//safe:relativeOrbitNumber")[0]
244-
),
245-
"sat:orbit_state": findtext(manifest, ".//s1:pass").lower(),
246-
"sat:anx_datetime": findtext(manifest, ".//s1:ascendingNodeTime") + "Z",
247-
"view:incidence_angle": image_information["incidenceAngleMidSwath"],
248-
"sar:polarizations": findall(
249-
manifest, ".//s1sarl1:transmitterReceiverPolarisation"
250-
),
251-
"sar:instrument_mode": findtext(
252-
manifest, ".//s1sarl1:instrumentMode/s1sarl1:mode"
253-
),
254-
"sar:frequency_band": "C",
255-
"sar:center_frequency": product_information["radarFrequency"] / 1e9,
256-
"sar:pixel_spacing_range": image_information["rangePixelSpacing"],
257-
"sar:pixel_spacing_azimuth": image_information["azimuthPixelSpacing"],
258-
"sar:observation_direction": "right",
259-
"sar:beam_ids": findall(
260-
manifest, ".//s1sarl1:instrumentMode/s1sarl1:swath"
261-
),
262-
"eopf:datatake_id": int(findtext(manifest, ".//s1sarl1:missionDataTakeID")),
263-
},
264-
"geometry": {"type": "Polygon", "coordinates": [coordinates]},
265-
"links": [],
266-
"assets": {},
267-
}
268-
return stac_item
269-
270-
271180
# DEPRECATED
272181
def make_stac_item(attrs: Mapping[str, Any]) -> dict[str, Any]:
273182
assert attrs["family_name"] == "SENTINEL-1"

xarray_sentinel/sentinel1.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import os
1616
import warnings
1717
from typing import Any, Sequence, TypeVar
18+
from xml.etree import ElementTree
1819

1920
import fsspec
2021
import numpy as np
@@ -1234,3 +1235,105 @@ def open_sentinel1_dataset(
12341235
conventions.update_attributes(ds, group=metadata)
12351236

12361237
return ds
1238+
1239+
1240+
def make_sentinel1_stac_item(
1241+
item_id: str,
1242+
manifest_path: esa_safe.PathOrFileType,
1243+
annotation: esa_safe.PathOrFileType,
1244+
namespaces=esa_safe.SENTINEL1_NAMESPACES,
1245+
) -> dict[str, Any]:
1246+
manifest = ElementTree.parse(manifest_path).getroot()
1247+
1248+
product_information = esa_safe.parse_tag(annotation, ".//productInformation")
1249+
image_information = esa_safe.parse_tag(annotation, ".//imageInformation")
1250+
1251+
coordinates = [
1252+
[float(v) for v in token.split(",")]
1253+
for token in esa_safe.findtext(manifest, ".//gml:coordinates").split()
1254+
]
1255+
coordinates += coordinates[:1]
1256+
product_timeliness = esa_safe.findtext(
1257+
manifest, ".//s1sarl1:productTimelinessCategory"
1258+
)
1259+
product_timeliness_map = {
1260+
"Fast-24h": {
1261+
"product:timeliness_category": "STC",
1262+
"product:timeliness": "PT24H",
1263+
},
1264+
"NRT-3h": {
1265+
"product:timeliness_category": "NRT",
1266+
"product:timeliness": "PT3H",
1267+
},
1268+
}
1269+
1270+
stac_item = {
1271+
"type": "Feature",
1272+
"stac_version": "1.1.0",
1273+
"stac_extensions": [
1274+
"https://stac-extensions.github.io/product/v0.1.0/schema.json",
1275+
"https://stac-extensions.github.io/processing/v1.2.0/schema.json",
1276+
"https://stac-extensions.github.io/sat/v1.0.0/schema.json",
1277+
"https://stac-extensions.github.io/view/v1.0.0/schema.json",
1278+
"https://stac-extensions.github.io/sar/v1.2.0/schema.json",
1279+
# "https://stac-extensions.github.io/eopf/v1.0.0/schema.json",
1280+
],
1281+
"id": item_id,
1282+
"properties": {
1283+
"datetime": None,
1284+
"start_datetime": esa_safe.findtext(manifest, ".//safe:startTime") + "Z",
1285+
"end_datetime": esa_safe.findtext(manifest, ".//safe:stopTime") + "Z",
1286+
"created": manifest.find(
1287+
".//safe:processing", namespaces=namespaces
1288+
).attrib["stop"]
1289+
+ "Z",
1290+
"platform": "sentinel-1"
1291+
+ esa_safe.findtext(manifest, ".//safe:platform/safe:number").lower(),
1292+
"instruments": ["sar"],
1293+
"constellation": "sentinel-1",
1294+
"product:type": esa_safe.findtext(manifest, ".//s1sarl1:productType"),
1295+
"product:timeliness_category": product_timeliness_map[product_timeliness][
1296+
"product:timeliness_category"
1297+
],
1298+
"product:timeliness": product_timeliness_map[product_timeliness][
1299+
"product:timeliness"
1300+
],
1301+
"processing:software": manifest.find(
1302+
".//safe:software", namespaces=namespaces
1303+
).attrib,
1304+
"sat:platform_international_designator": esa_safe.findtext(
1305+
manifest, ".//safe:nssdcIdentifier"
1306+
),
1307+
"sat:absolute_orbit": int(
1308+
esa_safe.findall(manifest, ".//safe:orbitNumber")[0]
1309+
),
1310+
"sat:relative_orbit": int(
1311+
esa_safe.findall(manifest, ".//safe:relativeOrbitNumber")[0]
1312+
),
1313+
"sat:orbit_state": esa_safe.findtext(manifest, ".//s1:pass").lower(),
1314+
"sat:anx_datetime": esa_safe.findtext(manifest, ".//s1:ascendingNodeTime")
1315+
+ "Z",
1316+
"view:incidence_angle": image_information["incidenceAngleMidSwath"],
1317+
"sar:polarizations": esa_safe.findall(
1318+
manifest, ".//s1sarl1:transmitterReceiverPolarisation"
1319+
),
1320+
"sar:instrument_mode": esa_safe.findtext(
1321+
manifest, ".//s1sarl1:instrumentMode/s1sarl1:mode"
1322+
),
1323+
"sar:frequency_band": "C",
1324+
"sar:center_frequency": product_information["radarFrequency"] / 1e9,
1325+
"sar:pixel_spacing_range": image_information["rangePixelSpacing"],
1326+
"sar:pixel_spacing_azimuth": image_information["azimuthPixelSpacing"],
1327+
"sar:observation_direction": "right",
1328+
"sar:beam_ids": esa_safe.findall(
1329+
manifest, ".//s1sarl1:instrumentMode/s1sarl1:swath"
1330+
),
1331+
"eopf:datatake_id": int(
1332+
esa_safe.findtext(manifest, ".//s1sarl1:missionDataTakeID")
1333+
),
1334+
},
1335+
"geometry": {"type": "Polygon", "coordinates": [coordinates]},
1336+
"links": [],
1337+
"assets": {},
1338+
}
1339+
return stac_item

0 commit comments

Comments
 (0)