Skip to content

Commit

Permalink
Add tests and refactor sar-c reader
Browse files Browse the repository at this point in the history
  • Loading branch information
mraspaud committed May 30, 2021
1 parent c64e53c commit 1295501
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 19 deletions.
9 changes: 8 additions & 1 deletion satpy/etc/readers/sar-c_safe.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ file_types:
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_reader: !!python/name:satpy.readers.sar_c_safe.SAFEXML
file_reader: !!python/name:satpy.readers.sar_c_safe.SAFEXMLAnnotation
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']


Expand Down Expand Up @@ -148,3 +148,10 @@ datasets:
file_type: safe_annotation
xml_item: geolocationGridPoint
xml_tag: incidenceAngle

calibration_constant:
name: calibration_constant
sensor: sar-c
polarization: [hh, hv, vv, vh]
units: 1
file_type: safe_calibration
34 changes: 20 additions & 14 deletions satpy/readers/sar_c_safe.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,20 @@ def get_metadata(self):
"""Convert the xml metadata to dict."""
return dictify(self.root.getroot())

@property
def start_time(self):
"""Get the start time."""
return self._start_time

@property
def end_time(self):
"""Get the end time."""
return self._end_time


class SAFEXMLAnnotation(SAFEXML):
"""XML file reader for the SAFE format, Annotation file."""

def get_dataset(self, key, info, chunks=None):
"""Load a dataset."""
if self._polarization != key["polarization"]:
Expand All @@ -122,26 +136,12 @@ def get_dataset(self, key, info, chunks=None):
if key["name"] == "incidence_angle":
return self.get_incidence_angle(chunks=chunks or CHUNK_SIZE)

def get_calibration_constant(self):
"""Load the calibration constant."""
return float(self.root.find('.//absoluteCalibrationConstant').text)

@lru_cache(maxsize=10)
def get_incidence_angle(self, chunks):
"""Get the incidence angle array."""
incidence_angle = XMLArray(self.root, ".//geolocationGridPoint", "incidenceAngle")
return incidence_angle.expand(self._image_shape, chunks=chunks)

@property
def start_time(self):
"""Get the start time."""
return self._start_time

@property
def end_time(self):
"""Get the end time."""
return self._end_time


class SAFEXMLCalibration(SAFEXML):
"""XML file reader for the SAFE format, Calibration file."""
Expand All @@ -150,8 +150,14 @@ def get_dataset(self, key, info, chunks=None):
"""Load a dataset."""
if self._polarization != key["polarization"]:
return
if key["name"] == "calibration_constant":
return self.get_calibration_constant()
return self.get_calibration(key["name"], chunks=chunks or CHUNK_SIZE)

def get_calibration_constant(self):
"""Load the calibration constant."""
return float(self.root.find('.//absoluteCalibrationConstant').text)

@lru_cache(maxsize=10)
def get_calibration(self, calibration, chunks=None):
"""Get the calibration array."""
Expand Down
91 changes: 87 additions & 4 deletions satpy/tests/reader_tests/test_sar_c_safe.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,54 @@ def __init__(self, *args):
</imageStatistics>
</imageInformation>
</imageAnnotation>
<geolocationGrid>
<geolocationGridPointList count="4">
<geolocationGridPoint>
<azimuthTime>2018-02-12T03:24:58.493342</azimuthTime>
<slantRangeTime>4.964462411376810e-03</slantRangeTime>
<line>0</line>
<pixel>0</pixel>
<latitude>7.021017981690355e+01</latitude>
<longitude>5.609684402205929e+01</longitude>
<height>8.234046399593353e-04</height>
<incidenceAngle>1.918318045731997e+01</incidenceAngle>
<elevationAngle>1.720012646010728e+01</elevationAngle>
</geolocationGridPoint>
<geolocationGridPoint>
<azimuthTime>2018-02-12T03:24:58.493342</azimuthTime>
<slantRangeTime>4.964462411376810e-03</slantRangeTime>
<line>0</line>
<pixel>9</pixel>
<latitude>7.021017981690355e+01</latitude>
<longitude>5.609684402205929e+01</longitude>
<height>8.234046399593353e-04</height>
<incidenceAngle>1.918318045731997e+01</incidenceAngle>
<elevationAngle>1.720012646010728e+01</elevationAngle>
</geolocationGridPoint>
<geolocationGridPoint>
<azimuthTime>2018-02-12T03:24:58.493342</azimuthTime>
<slantRangeTime>4.964462411376810e-03</slantRangeTime>
<line>9</line>
<pixel>0</pixel>
<latitude>7.021017981690355e+01</latitude>
<longitude>5.609684402205929e+01</longitude>
<height>8.234046399593353e-04</height>
<incidenceAngle>1.918318045731997e+01</incidenceAngle>
<elevationAngle>1.720012646010728e+01</elevationAngle>
</geolocationGridPoint>
<geolocationGridPoint>
<azimuthTime>2018-02-12T03:24:58.493342</azimuthTime>
<slantRangeTime>4.964462411376810e-03</slantRangeTime>
<line>9</line>
<pixel>9</pixel>
<latitude>7.021017981690355e+01</latitude>
<longitude>5.609684402205929e+01</longitude>
<height>8.234046399593353e-04</height>
<incidenceAngle>1.918318045731997e+01</incidenceAngle>
<elevationAngle>1.720012646010728e+01</elevationAngle>
</geolocationGridPoint>
</geolocationGridPointList>
</geolocationGrid>
</product>
"""

Expand Down Expand Up @@ -439,14 +487,14 @@ class TestSAFEXMLNoise(unittest.TestCase):

def setUp(self):
"""Set up the test case."""
from satpy.readers.sar_c_safe import SAFEXML, SAFEXMLNoise
from satpy.readers.sar_c_safe import SAFEXMLAnnotation, SAFEXMLNoise

with tempfile.NamedTemporaryFile(delete=False) as ntf:
self.annotation_filename = ntf.name
ntf.write(annotation_xml)
ntf.close()
filename_info = dict(start_time=None, end_time=None, polarization="vv")
self.annotation_fh = SAFEXML(self.annotation_filename, filename_info, mock.MagicMock())
self.annotation_fh = SAFEXMLAnnotation(self.annotation_filename, filename_info, mock.MagicMock())

with tempfile.NamedTemporaryFile(delete=False) as ntf:
self.noise_filename = ntf.name
Expand Down Expand Up @@ -523,14 +571,14 @@ class TestSAFEXMLCalibration(unittest.TestCase):

def setUp(self):
"""Set up the test case."""
from satpy.readers.sar_c_safe import SAFEXML, SAFEXMLCalibration
from satpy.readers.sar_c_safe import SAFEXMLAnnotation, SAFEXMLCalibration

with tempfile.NamedTemporaryFile(delete=False) as ntf:
self.annotation_filename = ntf.name
ntf.write(annotation_xml)
ntf.close()
filename_info = dict(start_time=None, end_time=None, polarization="vv")
self.annotation_fh = SAFEXML(self.annotation_filename, filename_info, mock.MagicMock())
self.annotation_fh = SAFEXMLAnnotation(self.annotation_filename, filename_info, mock.MagicMock())

with tempfile.NamedTemporaryFile(delete=False) as ntf:
self.calibration_filename = ntf.name
Expand Down Expand Up @@ -588,3 +636,38 @@ def test_get_calibration_dataset_has_right_chunk_size(self):
res = self.calibration_fh.get_dataset(query, {}, chunks=3)
assert res.data.chunksize == (3, 3)
np.testing.assert_allclose(res, self.expected_gamma)

def test_get_calibration_constant(self):
"""Test getting the calibration constant."""
query = DataQuery(name="calibration_constant", polarization="vv")
res = self.calibration_fh.get_dataset(query, {})
assert res == 1


class TestSAFEXMLAnnotation(unittest.TestCase):
"""Test the SAFE XML Annotation file handler."""

def setUp(self):
"""Set up the test case."""
from satpy.readers.sar_c_safe import SAFEXMLAnnotation

with tempfile.NamedTemporaryFile(delete=False) as ntf:
self.annotation_filename = ntf.name
ntf.write(annotation_xml)
ntf.close()
filename_info = dict(start_time=None, end_time=None, polarization="vv")
self.annotation_fh = SAFEXMLAnnotation(self.annotation_filename, filename_info, mock.MagicMock())

self.expected_gamma = np.array([[1840.695, 1779.672, 1718.649, 1452.926, 1187.203, 1186.226,
1185.249, 1184.276, 1183.303, 1181.365]]) * np.ones((10, 1))

def tearDown(self):
"""Tear down the test case."""
with suppress(PermissionError):
os.remove(self.annotation_filename)

def test_incidence_angle(self):
"""Test reading the incidence angle."""
query = DataQuery(name="incidence_angle", polarization="vv")
res = self.annotation_fh.get_dataset(query, {})
np.testing.assert_allclose(res, 19.18318046)

0 comments on commit 1295501

Please sign in to comment.