Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 24 additions & 19 deletions satpy/etc/readers/sar-c_safe.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ reader:
long_name: Sentinel-1 A and B SAR-C data in SAFE format
description: SAFE Reader for SAR-C data
status: Nominal
supports_fsspec: false
supports_fsspec: true
sensors: [sar-c]
reader: !!python/name:satpy.readers.sar_c_safe.SAFESARReader
data_identification_keys:
Expand Down Expand Up @@ -38,21 +38,27 @@ reader:
transitive: false

file_types:
safe_measurement:
file_patterns: ['{fmission_id:3s}_{fsar_mode:2s}_{fproduct_type:3s}{fresolution:1s}_{fprocessing_level:1s}{fproduct_class:1s}{fpolarization:2s}_{fstart_time:%Y%m%dT%H%M%S}_{fend_time:%Y%m%dT%H%M%S}_{forbit_number:6d}_{fmission_data_take_id:6s}_{fproduct_unique_id:4s}.SAFE/measurement/{mission_id:3s}-{swath_id:2s}-{product_type:3s}-{polarization:2s}-{start_time:%Y%m%dt%H%M%S}-{end_time:%Y%m%dt%H%M%S}-{orbit_number:6d}-{mission_data_take_id:6s}-{image_number:3s}.tiff']
requires: [safe_calibration, safe_noise, safe_annotation]
safe_calibration:
file_patterns: ['{fmission_id:3s}_{fsar_mode:2s}_{fproduct_type:3s}{fresolution:1s}_{fprocessing_level:1s}{fproduct_class:1s}{fpolarization:2s}_{fstart_time:%Y%m%dT%H%M%S}_{fend_time:%Y%m%dT%H%M%S}_{forbit_number:6d}_{fmission_data_take_id:6s}_{fproduct_unique_id:4s}.SAFE/annotation/calibration/calibration-{mission_id:3s}-{swath_id:2s}-{product_type:3s}-{polarization:2s}-{start_time:%Y%m%dt%H%M%S}-{end_time:%Y%m%dt%H%M%S}-{orbit_number:6d}-{mission_data_take_id:6s}-{image_number:3s}.xml']
requires: [safe_annotation]
safe_noise:
file_patterns: ['{fmission_id:3s}_{fsar_mode:2s}_{fproduct_type:3s}{fresolution:1s}_{fprocessing_level:1s}{fproduct_class:1s}{fpolarization:2s}_{fstart_time:%Y%m%dT%H%M%S}_{fend_time:%Y%m%dT%H%M%S}_{forbit_number:6d}_{fmission_data_take_id:6s}_{fproduct_unique_id:4s}.SAFE/annotation/calibration/noise-{mission_id:3s}-{swath_id:2s}-{product_type:3s}-{polarization:2s}-{start_time:%Y%m%dt%H%M%S}-{end_time:%Y%m%dt%H%M%S}-{orbit_number:6d}-{mission_data_take_id:6s}-{image_number:3s}.xml']
requires: [safe_annotation]
safe_annotation:
file_patterns: ['{fmission_id:3s}_{fsar_mode:2s}_{fproduct_type:3s}{fresolution:1s}_{fprocessing_level:1s}{fproduct_class:1s}{fpolarization:2s}_{fstart_time:%Y%m%dT%H%M%S}_{fend_time:%Y%m%dT%H%M%S}_{forbit_number:6d}_{fmission_data_take_id:6s}_{fproduct_unique_id:4s}.SAFE/annotation/{mission_id:3s}-{swath_id:2s}-{product_type:3s}-{polarization:2s}-{start_time:%Y%m%dt%H%M%S}-{end_time:%Y%m%dt%H%M%S}-{orbit_number:6d}-{mission_data_take_id:6s}-{image_number:3s}.xml']

safe_measurement:
file_patterns:
- "{fmission_id:3s}_{fsar_mode:2s}_{fproduct_type:3s}{fresolution:1s}_{fprocessing_level:1s}{fproduct_class:1s}{fpolarization:2s}_{fstart_time:%Y%m%dT%H%M%S}_{fend_time:%Y%m%dT%H%M%S}_{forbit_number:6d}_{fmission_data_take_id:6s}_{fproduct_unique_id:4s}.SAFE/measurement/{mission_id:3s}-{swath_id:2s}-{product_type:3s}-{polarization:2s}-{start_time:%Y%m%dt%H%M%S}-{end_time:%Y%m%dt%H%M%S}-{orbit_number:6d}-{mission_data_take_id:6s}-{image_number:3s}.tiff"
- "{fmission_id:3s}_{fsar_mode:2s}_{fproduct_type:3s}{fresolution:1s}_{fprocessing_level:1s}{fproduct_class:1s}{fpolarization:2s}_{fstart_time:%Y%m%dT%H%M%S}_{fend_time:%Y%m%dT%H%M%S}_{forbit_number:6d}_{fmission_data_take_id:6s}_{fproduct_unique_id:4s}_COG.SAFE/measurement/{mission_id:3s}-{swath_id:2s}-{product_type:3s}-{polarization:2s}-{start_time:%Y%m%dt%H%M%S}-{end_time:%Y%m%dt%H%M%S}-{orbit_number:6d}-{mission_data_take_id:6s}-{image_number:3s}-cog.tiff"
requires: [safe_calibration, safe_noise, safe_annotation]
safe_calibration:
file_patterns:
- "{fmission_id:3s}_{fsar_mode:2s}_{fproduct_type:3s}{fresolution:1s}_{fprocessing_level:1s}{fproduct_class:1s}{fpolarization:2s}_{fstart_time:%Y%m%dT%H%M%S}_{fend_time:%Y%m%dT%H%M%S}_{forbit_number:6d}_{fmission_data_take_id:6s}_{fproduct_unique_id:4s}.SAFE/annotation/calibration/calibration-{mission_id:3s}-{swath_id:2s}-{product_type:3s}-{polarization:2s}-{start_time:%Y%m%dt%H%M%S}-{end_time:%Y%m%dt%H%M%S}-{orbit_number:6d}-{mission_data_take_id:6s}-{image_number:3s}.xml"
- "{fmission_id:3s}_{fsar_mode:2s}_{fproduct_type:3s}{fresolution:1s}_{fprocessing_level:1s}{fproduct_class:1s}{fpolarization:2s}_{fstart_time:%Y%m%dT%H%M%S}_{fend_time:%Y%m%dT%H%M%S}_{forbit_number:6d}_{fmission_data_take_id:6s}_{fproduct_unique_id:4s}_COG.SAFE/annotation/calibration/calibration-{mission_id:3s}-{swath_id:2s}-{product_type:3s}-{polarization:2s}-{start_time:%Y%m%dt%H%M%S}-{end_time:%Y%m%dt%H%M%S}-{orbit_number:6d}-{mission_data_take_id:6s}-{image_number:3s}-cog.xml"
requires: [safe_annotation]
safe_noise:
file_patterns:
- "{fmission_id:3s}_{fsar_mode:2s}_{fproduct_type:3s}{fresolution:1s}_{fprocessing_level:1s}{fproduct_class:1s}{fpolarization:2s}_{fstart_time:%Y%m%dT%H%M%S}_{fend_time:%Y%m%dT%H%M%S}_{forbit_number:6d}_{fmission_data_take_id:6s}_{fproduct_unique_id:4s}.SAFE/annotation/calibration/noise-{mission_id:3s}-{swath_id:2s}-{product_type:3s}-{polarization:2s}-{start_time:%Y%m%dt%H%M%S}-{end_time:%Y%m%dt%H%M%S}-{orbit_number:6d}-{mission_data_take_id:6s}-{image_number:3s}.xml"
- "{fmission_id:3s}_{fsar_mode:2s}_{fproduct_type:3s}{fresolution:1s}_{fprocessing_level:1s}{fproduct_class:1s}{fpolarization:2s}_{fstart_time:%Y%m%dT%H%M%S}_{fend_time:%Y%m%dT%H%M%S}_{forbit_number:6d}_{fmission_data_take_id:6s}_{fproduct_unique_id:4s}_COG.SAFE/annotation/calibration/noise-{mission_id:3s}-{swath_id:2s}-{product_type:3s}-{polarization:2s}-{start_time:%Y%m%dt%H%M%S}-{end_time:%Y%m%dt%H%M%S}-{orbit_number:6d}-{mission_data_take_id:6s}-{image_number:3s}-cog.xml"
requires: [safe_annotation]
safe_annotation:
file_patterns:
- "{fmission_id:3s}_{fsar_mode:2s}_{fproduct_type:3s}{fresolution:1s}_{fprocessing_level:1s}{fproduct_class:1s}{fpolarization:2s}_{fstart_time:%Y%m%dT%H%M%S}_{fend_time:%Y%m%dT%H%M%S}_{forbit_number:6d}_{fmission_data_take_id:6s}_{fproduct_unique_id:4s}.SAFE/annotation/{mission_id:3s}-{swath_id:2s}-{product_type:3s}-{polarization:2s}-{start_time:%Y%m%dt%H%M%S}-{end_time:%Y%m%dt%H%M%S}-{orbit_number:6d}-{mission_data_take_id:6s}-{image_number:3s}.xml"
- "{fmission_id:3s}_{fsar_mode:2s}_{fproduct_type:3s}{fresolution:1s}_{fprocessing_level:1s}{fproduct_class:1s}{fpolarization:2s}_{fstart_time:%Y%m%dT%H%M%S}_{fend_time:%Y%m%dT%H%M%S}_{forbit_number:6d}_{fmission_data_take_id:6s}_{fproduct_unique_id:4s}_COG.SAFE/annotation/{mission_id:3s}-{swath_id:2s}-{product_type:3s}-{polarization:2s}-{start_time:%Y%m%dt%H%M%S}-{end_time:%Y%m%dt%H%M%S}-{orbit_number:6d}-{mission_data_take_id:6s}-{image_number:3s}-cog.xml"

datasets:

latitude:
name: latitude
resolution: 80
Expand All @@ -77,7 +83,6 @@ datasets:
polarization: [hh, hv, vv, vh]
units: meter


measurement:
name: measurement
sensor: sar-c
Expand Down Expand Up @@ -105,11 +110,11 @@ datasets:
polarization: [hh, hv, vv, vh]
file_type: safe_noise
xml_item:
- noiseVector
- noiseRangeVector
- noiseVector
- noiseRangeVector
xml_tag:
- noiseLut
- noiseRangeLut
- noiseLut
- noiseRangeLut

sigma:
name: sigma_squared
Expand Down
4 changes: 2 additions & 2 deletions satpy/readers/sar_c_safe.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
from collections import defaultdict
from datetime import timezone as tz
from functools import cached_property
from pathlib import Path
from threading import Lock

import defusedxml.ElementTree as ET
Expand All @@ -52,6 +51,7 @@
from dask import array as da
from geotiepoints.geointerpolator import lonlat2xyz, xyz2lonlat
from geotiepoints.interpolator import MultipleSplineInterpolator
from upath import UPath
from xarray import DataArray

from satpy.dataset.data_dict import DatasetDict
Expand Down Expand Up @@ -112,7 +112,7 @@ def __init__(self, filename, filename_info, filetype_info,
self._end_time = filename_info["end_time"].replace(tzinfo=tz.utc)
self._polarization = filename_info["polarization"]
if isinstance(self.filename, str):
self.filename = Path(self.filename)
self.filename = UPath(self.filename)
with self.filename.open() as fd:
self.root = ET.parse(fd)
self._image_shape = image_shape
Expand Down
75 changes: 69 additions & 6 deletions satpy/tests/reader_tests/test_sar_c_safe.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,15 @@
from datetime import datetime
from enum import Enum
from pathlib import Path
from zipfile import ZipFile

import numpy as np
import pytest
import yaml
from pytest_lazy_fixtures import lf
from upath import UPath

from satpy.readers.core.remote import FSFile

geotiepoints = pytest.importorskip("geotiepoints", "1.7.5")

Expand All @@ -43,10 +48,15 @@
END_TIME = datetime(2019, 2, 1, 2, 47, 20)

@pytest.fixture(scope="module")
def granule_directory(tmp_path_factory):
def data_directory(tmp_path_factory):
"""Create a granule directory."""
return tmp_path_factory.mktemp("data")


@pytest.fixture(scope="module")
def granule_directory(data_directory):
"""Create a granule directory."""
data_dir = tmp_path_factory.mktemp("data")
gdir = data_dir / f"S1A_IW_GRDH_1SDV_{dirname_suffix}.SAFE"
gdir = data_directory / f"S1A_IW_GRDH_1SDV_{dirname_suffix}.SAFE"
os.mkdir(gdir)
return gdir

Expand All @@ -62,6 +72,29 @@
return annotation_file


@pytest.fixture(scope="module")
def zipped_annotation_file(annotation_file, data_directory):
"""Create a zipped annotation file."""
zip_file = data_directory / "annotation.zip"
with ZipFile(zip_file, mode="w") as archive:
basename = os.path.join(*os.path.normpath(annotation_file).split(os.path.sep)[-3:])
archive.write(annotation_file, basename)
fname = f"zip://{basename}::file://{zip_file.as_posix()}"
return fname


@pytest.fixture(scope="module")
def upath_annotation_file(zipped_annotation_file):
"""Create a upath of a zipped annotation file."""
return UPath(zipped_annotation_file)


@pytest.fixture(scope="module")
def fs_annotation_file(upath_annotation_file):
"""Create an FSFile of a zipped annotation file."""
return FSFile(upath_annotation_file)


@pytest.fixture(scope="module")
def annotation_filehandler(annotation_file):
"""Create an annotation filehandler."""
Expand Down Expand Up @@ -161,13 +194,35 @@


@pytest.fixture(scope="module")
def measurement_filehandler(measurement_file, noise_filehandler, calibration_filehandler):
def zipped_measurement_file(measurement_file, data_directory):
"""Create a zipped measurement file."""
zip_file = data_directory / "measurement.zip"
with ZipFile(zip_file, mode="w") as archive:
basename = os.path.join(*os.path.normpath(measurement_file).split(os.path.sep)[-3:])
archive.write(measurement_file, basename)
return f"zip://{basename}::file://{zip_file.as_posix()}"


@pytest.fixture(scope="module")
def upath_measurement_file(zipped_measurement_file):
"""Create a upath of a zipped measurement file."""
return UPath(zipped_measurement_file)


@pytest.fixture(scope="module")
def fsfile_measurement_file(upath_measurement_file):
"""Create an FSFile of a zipped measurement file."""
return FSFile(upath_measurement_file)


@pytest.fixture(scope="module", params=("measurement_file", "upath_measurement_file", "fsfile_measurement_file"))
def measurement_filehandler(request, noise_filehandler, calibration_filehandler):
"""Create a measurement filehandler."""
filename_info = {"mission_id": "S1A", "dataset_name": "foo", "start_time": START_TIME, "end_time": END_TIME,
"polarization": "vv"}
filetype_info = None
from satpy.readers.sar_c_safe import SAFEGRD
filehandler = SAFEGRD(measurement_file,
filehandler = SAFEGRD(request.getfixturevalue(request.param),

Check notice on line 225 in satpy/tests/reader_tests/test_sar_c_safe.py

View check run for this annotation

codefactor.io / CodeFactor

satpy/tests/reader_tests/test_sar_c_safe.py#L225

Multiple spaces after operator. (E222)
filename_info,
filetype_info,
calibration_filehandler,
Expand Down Expand Up @@ -782,8 +837,16 @@
assert type(res) is np.float32


def test_incidence_angle(annotation_filehandler):
@pytest.mark.parametrize(("annotation_file_thing"), [
lf("annotation_file"),
lf("zipped_annotation_file"),
lf("upath_annotation_file"),
lf("fs_annotation_file")
])
def test_incidence_angle(annotation_file_thing):
"""Test reading the incidence angle in an annotation file."""
filename_info = dict(start_time=START_TIME, end_time=END_TIME, polarization="vv")
annotation_filehandler = SAFEXMLAnnotation(annotation_file_thing, filename_info, None)
query = DataQuery(name="incidence_angle", polarization="vv")
res = annotation_filehandler.get_dataset(query, {})
np.testing.assert_allclose(res, 19.18318046)
Expand Down
Loading