#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2018 Satpy developers
#
# This file is part of satpy.
#
# satpy is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# satpy is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# satpy. If not, see <http://www.gnu.org/licenses/>.
"""Module for testing the satpy.readers.nc_slstr module."""
import datetime as dt
import unittest
import unittest.mock as mock
import numpy as np
import pytest
import xarray as xr
from satpy.dataset.dataid import DataID, ModifierTuple, WavelengthRange
from satpy.readers.slstr_l1b import NCSLSTR1B, NCSLSTRAngles, NCSLSTRFlag, NCSLSTRGeo
local_id_keys_config = {"name": {
"required": True,
},
"wavelength": {
"type": WavelengthRange,
},
"resolution": None,
"calibration": {
"enum": [
"reflectance",
"brightness_temperature",
"radiance",
"counts"
]
},
"stripe": {
"enum": [
"a",
"b",
"c",
"i",
"f",
]
},
"view": {
"enum": [
"nadir",
"oblique",
]
},
"modifiers": {
"required": True,
"default": ModifierTuple(),
"type": ModifierTuple,
},
}
[docs]
class TestSLSTRL1B(unittest.TestCase):
"""Common setup for SLSTR_L1B tests."""
[docs]
@mock.patch("satpy.readers.slstr_l1b.xr")
def setUp(self, xr_):
"""Create a fake dataset using the given radiance data."""
self.base_data = np.array(([1., 2., 3.], [4., 5., 6.]))
self.det_data = np.array(([0, 1, 1], [0, 1, 0]))
self.start_time = "2020-05-10T12:01:15.585Z"
self.end_time = "2020-05-10T12:06:18.012Z"
self.rad = xr.DataArray(
self.base_data,
dims=("columns", "rows"),
attrs={"scale_factor": 1.0, "add_offset": 0.0,
"_FillValue": -32768, "units": "mW.m-2.sr-1.nm-1",
}
)
det = xr.DataArray(
self.base_data,
dims=("columns", "rows"),
attrs={"scale_factor": 1.0, "add_offset": 0.0,
"_FillValue": 255,
}
)
self.fake_dataset = xr.Dataset(
data_vars={
"S5_radiance_an": self.rad,
"S9_BT_ao": self.rad,
"foo_radiance_an": self.rad,
"S5_solar_irradiances": self.rad,
"geometry_tn": self.rad,
"latitude_an": self.rad,
"x_tx": self.rad,
"y_tx": self.rad,
"x_in": self.rad,
"y_in": self.rad,
"x_an": self.rad,
"y_an": self.rad,
"flags_an": self.rad,
"detector_an": det,
},
attrs={
"start_time": self.start_time,
"stop_time": self.end_time,
},
)
[docs]
def make_dataid(**items):
"""Make a data id."""
return DataID(local_id_keys_config, **items)
[docs]
class TestSLSTRReader(TestSLSTRL1B):
"""Test various nc_slstr file handlers."""
[docs]
class FakeSpl:
"""Fake return function for SPL interpolation."""
[docs]
@staticmethod
def ev(foo_x, foo_y):
"""Fake function to return interpolated data."""
return np.zeros((3, 2))
[docs]
@mock.patch("satpy.readers.slstr_l1b.xr")
@mock.patch("scipy.interpolate.RectBivariateSpline")
def test_instantiate(self, bvs_, xr_):
"""Test initialization of file handlers."""
bvs_.return_value = self.FakeSpl
xr_.open_dataset.return_value = self.fake_dataset
good_start = dt.datetime.strptime(self.start_time,
"%Y-%m-%dT%H:%M:%S.%fZ")
good_end = dt.datetime.strptime(self.end_time,
"%Y-%m-%dT%H:%M:%S.%fZ")
ds_id = make_dataid(name="foo", calibration="radiance",
stripe="a", view="nadir")
ds_id_500 = make_dataid(name="foo", calibration="radiance",
stripe="a", view="nadir", resolution=500)
filename_info = {"mission_id": "S3A", "dataset_name": "foo",
"start_time": 0, "end_time": 0,
"stripe": "a", "view": "n"}
test = NCSLSTR1B("somedir/S1_radiance_an.nc", filename_info, "c")
assert test.view == "nadir"
assert test.stripe == "a"
with pytest.warns(UserWarning, match=r"No radiance adjustment supplied for channel"):
test.get_dataset(ds_id, dict(filename_info, **{"file_key": "foo"}))
assert test.start_time == good_start
assert test.end_time == good_end
xr_.open_dataset.assert_called()
xr_.open_dataset.reset_mock()
filename_info = {"mission_id": "S3A", "dataset_name": "foo",
"start_time": 0, "end_time": 0,
"stripe": "c", "view": "o"}
test = NCSLSTR1B("somedir/S1_radiance_co.nc", filename_info, "c")
assert test.view == "oblique"
assert test.stripe == "c"
test.get_dataset(ds_id, dict(filename_info, **{"file_key": "foo"}))
assert test.start_time == good_start
assert test.end_time == good_end
xr_.open_dataset.assert_called()
xr_.open_dataset.reset_mock()
filename_info = {"mission_id": "S3A", "dataset_name": "foo",
"start_time": 0, "end_time": 0,
"stripe": "a", "view": "n"}
test = NCSLSTRGeo("somedir/geometry_an.nc", filename_info, "c")
test.get_dataset(ds_id, dict(filename_info, **{"file_key": "latitude_{stripe:1s}{view:1s}"}))
assert test.start_time == good_start
assert test.end_time == good_end
xr_.open_dataset.assert_called()
xr_.open_dataset.reset_mock()
test = NCSLSTRFlag("somedir/S1_radiance_an.nc", filename_info, "c")
test.get_dataset(ds_id, dict(filename_info, **{"file_key": "flags_{stripe:1s}{view:1s}"}))
assert test.view == "nadir"
assert test.stripe == "a"
assert test.start_time == good_start
assert test.end_time == good_end
xr_.open_dataset.assert_called()
xr_.open_dataset.reset_mock()
test = NCSLSTRAngles("somedir/S1_radiance_an.nc", filename_info, "c")
test.get_dataset(ds_id, dict(filename_info, **{"file_key": "geometry_t{view:1s}"}))
assert test.start_time == good_start
assert test.end_time == good_end
xr_.open_dataset.assert_called()
xr_.open_dataset.reset_mock()
test.get_dataset(ds_id_500, dict(filename_info, **{"file_key": "geometry_t{view:1s}"}))
[docs]
class TestSLSTRCalibration(TestSLSTRL1B):
"""Test the implementation of the calibration factors."""
[docs]
@mock.patch("satpy.readers.slstr_l1b.xr")
def test_radiance_calibration(self, xr_):
"""Test radiance calibration steps."""
from satpy.readers.slstr_l1b import CHANCALIB_FACTORS
xr_.open_dataset.return_value = self.fake_dataset
ds_id = make_dataid(name="foo", calibration="radiance",
stripe="a", view="nadir")
filename_info = {"mission_id": "S3A", "dataset_name": "foo",
"start_time": 0, "end_time": 0,
"stripe": "a", "view": "n"}
test = NCSLSTR1B("somedir/S1_radiance_co.nc", filename_info, "c")
# Check warning is raised if we don't have calibration
with pytest.warns(UserWarning, match=r"No radiance adjustment supplied for channel"):
test.get_dataset(ds_id, dict(filename_info, **{"file_key": "foo"}))
# Check user calibration is used correctly
test = NCSLSTR1B("somedir/S1_radiance_co.nc", filename_info, "c",
user_calibration={"foo_nadir": 0.4})
data = test.get_dataset(ds_id, dict(filename_info, **{"file_key": "foo"}))
np.testing.assert_allclose(data.values, self.base_data * 0.4)
# Check internal calibration is used correctly
ds_id = make_dataid(name="S5", calibration="radiance", stripe="a", view="nadir")
filename_info["dataset_name"] = "S5"
test = NCSLSTR1B("somedir/S1_radiance_an.nc", filename_info, "c")
data = test.get_dataset(ds_id, dict(filename_info, **{"file_key": "S5"}))
np.testing.assert_allclose(data.values,
self.base_data * CHANCALIB_FACTORS["S5_nadir"])
[docs]
@mock.patch("satpy.readers.slstr_l1b.xr")
@mock.patch("satpy.readers.slstr_l1b.da")
def test_reflectance_calibration(self, da_, xr_):
"""Test reflectance calibration."""
xr_.open_dataset.return_value = self.fake_dataset
da_.map_blocks.return_value = self.rad / 100.
filename_info = {"mission_id": "S3A", "dataset_name": "S5",
"start_time": 0, "end_time": 0,
"stripe": "a", "view": "n"}
ds_id = make_dataid(name="S5", calibration="reflectance", stripe="a", view="nadir")
test = NCSLSTR1B("somedir/S1_radiance_an.nc", filename_info, "c")
data = test.get_dataset(ds_id, dict(filename_info, **{"file_key": "S5"}))
assert data.units == "%"
np.testing.assert_allclose(data.values, self.rad * np.pi)
[docs]
def test_cal_rad(self):
"""Test the radiance to reflectance converter."""
rad = np.array([10., 20., 30., 40., 50., 60., 70.])
didx = np.array([1, 2., 1., 3., 2., 2., 0.])
solflux = np.array([100., 200., 300., 400.])
good_rad = np.array([1. / 20., 1. / 15., 3. / 20., 1. / 10., 1. / 6., 2. / 10., 7. / 10.])
out_rad = NCSLSTR1B._cal_rad(rad, didx, solflux)
np.testing.assert_allclose(out_rad, good_rad)