#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2017-2021 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/>.
"""Unittesting the ASCAT SCATTEROMETER SOIL MOISTURE BUFR reader."""
import datetime as dt
import os
import sys
import unittest
import numpy as np
# TDB: this test is based on test_seviri_l2_bufr.py and test_iasi_l2.py
# This is a test for ASCAT SoilMoisture product message, take from a real
# bufr file distributed over EUMETCAST
[docs]
def create_message():
"""Create fake message for testing."""
nlat = 10
nlon = 10
samples = nlat*nlon
lat, lon = np.meshgrid(np.linspace(63, 65, nlat), np.linspace(-30, -20, nlon))
lat = np.round(np.ravel(lat), 4)
lon = np.round(np.ravel(lon), 4)
rstate = np.random.RandomState(0)
surfaceSoilMoisture = np.round(rstate.rand(samples)*100, 1)
surfaceSoilMoisture[0] = -1e+100
retmsg = {
"inputDelayedDescriptorReplicationFactor": [8],
"edition": 4,
"masterTableNumber": 0,
"bufrHeaderCentre": 254,
"bufrHeaderSubCentre": 0,
"updateSequenceNumber": 0,
"dataCategory": 12,
"internationalDataSubCategory": 255,
"dataSubCategory": 190,
"masterTablesVersionNumber": 13,
"localTablesVersionNumber": 0,
"typicalYear": 2020,
"typicalMonth": 12,
"typicalDay": 21,
"typicalHour": 9,
"typicalMinute": 33,
"typicalSecond": 0,
"numberOfSubsets": samples,
"observedData": 1,
"compressedData": 1,
"unexpandedDescriptors": 312061,
"centre": 254,
"subCentre": 0,
"#1#softwareIdentification": 1000,
"satelliteIdentifier": 4,
"satelliteInstruments": 190,
"year": 2020,
"month": 12,
"day": 21,
"hour": 9,
"minute": 33,
"second": np.linspace(0, 59, samples),
"latitude": lat,
"longitude": lon,
"surfaceSoilMoisture": surfaceSoilMoisture,
"soilMoistureQuality": np.zeros(samples),
}
return retmsg
MSG = create_message()
# the notional filename that would contain the above test message data
FILENAME = "W_XX-EUMETSAT-TEST,SOUNDING+SATELLITE,METOPA+ASCAT_C_EUMC_20201221093300_73545_eps_o_125_ssm_l2.bin"
# the information that would be extracted from the above filename according to the pattern in the .yaml
FILENAME_INFO = {
"reception_location": "TEST",
"platform": "METOPA",
"instrument": "ASCAT",
"start_time": "20201221093300",
"perigee": "73545",
"species": "125_ssm",
"level": "l2"
}
# file type info for the above file that is defined in the .yaml
FILETYPE_INFO = {
"file_type": "ascat_l2_soilmoisture_bufr",
"file_reader": "AscatSoilMoistureBufr"
}
[docs]
def save_test_data(path):
"""Save the test file to the indicated directory."""
import eccodes as ec
filepath = os.path.join(path, FILENAME)
with open(filepath, "wb") as f:
for m in [MSG]:
buf = ec.codes_bufr_new_from_samples("BUFR4_local_satellite")
for key in m:
val = m[key]
if np.isscalar(val):
ec.codes_set(buf, key, val)
else:
ec.codes_set_array(buf, key, val)
ec.codes_set(buf, "pack", 1)
ec.codes_write(buf, f)
ec.codes_release(buf)
return filepath
[docs]
class TesitAscatL2SoilmoistureBufr(unittest.TestCase):
"""Test ASCAT Soil Mosture loader."""
[docs]
def setUp(self):
"""Create temporary file to perform tests with."""
import tempfile
from satpy.readers.ascat_l2_soilmoisture_bufr import AscatSoilMoistureBufr
self.base_dir = tempfile.mkdtemp()
self.fname = save_test_data(self.base_dir)
self.fname_info = FILENAME_INFO
self.ftype_info = FILETYPE_INFO
self.reader = AscatSoilMoistureBufr(self.fname, self.fname_info, self.ftype_info)
[docs]
def tearDown(self):
"""Remove the temporary directory created for a test."""
try:
import shutil
shutil.rmtree(self.base_dir, ignore_errors=True)
except OSError:
pass
[docs]
@unittest.skipIf(sys.platform.startswith("win"), "'eccodes' not supported on Windows")
def test_scene(self):
"""Test scene creation."""
from satpy import Scene
fname = os.path.join(self.base_dir, FILENAME)
scn = Scene(reader="ascat_l2_soilmoisture_bufr", filenames=[fname])
assert "scatterometer" in scn.sensor_names
assert dt.datetime(2020, 12, 21, 9, 33, 0) == scn.start_time
assert dt.datetime(2020, 12, 21, 9, 33, 59) == scn.end_time
[docs]
@unittest.skipIf(sys.platform.startswith("win"), "'eccodes' not supported on Windows")
def test_scene_load_available_datasets(self):
"""Test that all datasets are available."""
from satpy import Scene
fname = os.path.join(self.base_dir, FILENAME)
scn = Scene(reader="ascat_l2_soilmoisture_bufr", filenames=[fname])
assert "surface_soil_moisture" in scn.available_dataset_names()
scn.load(scn.available_dataset_names())
loaded = [dataset.name for dataset in scn]
assert sorted(loaded) == sorted(scn.available_dataset_names())
[docs]
@unittest.skipIf(sys.platform.startswith("win"), "'eccodes' not supported on Windows")
def test_scene_dataset_values(self):
"""Test loading data."""
from satpy import Scene
fname = os.path.join(self.base_dir, FILENAME)
scn = Scene(reader="ascat_l2_soilmoisture_bufr", filenames=[fname])
for name in scn.available_dataset_names():
scn.load([name])
loaded_values = scn[name].values
fill_value = scn[name].attrs["fill_value"]
# replace nans in data loaded from file with the fill value defined in the .yaml
# to make them comparable
loaded_values_nan_filled = np.nan_to_num(loaded_values, nan=fill_value)
key = scn[name].attrs["key"]
original_values = MSG[key]
# this makes each assertion below a separate test from unittest's point of view
# (note: if all subtests pass, they will count as one test)
with self.subTest(msg="Test failed for dataset: "+name):
assert np.allclose(original_values, loaded_values_nan_filled)