Skip to content

Commit

Permalink
Merge pull request #1020 from eysteinn/feature-slstr_l2
Browse files Browse the repository at this point in the history
Feature Sentinel-3 Level-2 SST
  • Loading branch information
mraspaud committed Jan 27, 2020
2 parents 7b8989f + 773d8fa commit 17efb75
Show file tree
Hide file tree
Showing 5 changed files with 219 additions and 1 deletion.
3 changes: 3 additions & 0 deletions doc/source/index.rst
Expand Up @@ -228,6 +228,9 @@ the base Satpy installation.
* - GOES-R GLM Grided Level 2 in NetCDF4 format
- `glm_l2`
- Beta
* - Sentinel-3 SLSTR SST data in NetCDF4 format
- `slstr_l2`
- Beta


Indices and tables
Expand Down
59 changes: 59 additions & 0 deletions satpy/etc/readers/slstr_l2.yaml
@@ -0,0 +1,59 @@
reader:
description: NC Reader for Sentinel-3 SLSTR Level 2 data
name: slstr_l2
sensors: [slstr_l2]
default_channels: []
reader: !!python/name:satpy.readers.yaml_reader.FileYAMLReader

file_types:
SLSTRB:
file_reader: !!python/name:satpy.readers.slstr_l2.SLSTRL2FileHandler
file_patterns: ['{dt1:%Y%m%d%H%M%S}-{generating_centre:3s}-{type_id:3s}_GHRSST-SSTskin-SLSTR{something:1s}-{dt2:%Y%m%d%H%M%S}-{version}.nc',
'{mission_id:3s}_SL_{processing_level:1s}_WST____{start_time:%Y%m%dT%H%M%S}_{end_time:%Y%m%dT%H%M%S}_{creation_time:%Y%m%dT%H%M%S}_{duration:4d}_{cycle:3d}_{relative_orbit:3d}_{frame:4s}_{centre:3s}_{mode:1s}_{timeliness:2s}_{collection:3s}.SEN3.tar']

datasets:
longitude:
name: longitude
resolution: 1000
view: nadir
file_type: SLSTRB
standard_name: lon
units: degree

latitude:
name: latitude
resolution: 1000
view: nadir
file_type: SLSTRB
standard_name: lat
units: degree

sea_surface_temperature:
name: sea_surface_temperature
sensor: slstr_l2
coordinates: [longitude, latitude]
file_type: SLSTRB
resolution: 1000
view: nadir
units: kelvin
standard_name: sea_surface_temperature

sea_ice_fraction:
name: sea_ice_fraction
sensor: slstr_l2
coordinates: [longitude, latitude]
file_type: SLSTRB
resolution: 1000
view: nadir
units: "%"
standard_name: sea_ice_fraction

# Quality estimation 0-5: no data, cloud, worst, low, acceptable, best
quality_level:
name: quality_level
sensor: slstr_l2
coordinates: [longitude, latitude]
file_type: SLSTRB
resolution: 1000
view: nadir
standard_name: sea_ice_fraction
75 changes: 75 additions & 0 deletions satpy/readers/slstr_l2.py
@@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017 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/>.
"""Reader for Sentinel-3 SLSTR SST data."""

from datetime import datetime
from satpy.readers.file_handlers import BaseFileHandler
from satpy import CHUNK_SIZE
import xarray as xr


class SLSTRL2FileHandler(BaseFileHandler):
"""File handler for Sentinel-3 SSL L2 netCDF files."""

def __init__(self, filename, filename_info, filetype_info, engine=None):
"""Initialize the file handler for Sentinel-3 SSL L2 netCDF data."""
super(SLSTRL2FileHandler, self).__init__(filename, filename_info, filetype_info)

if filename.endswith('tar'):
import tarfile
import os
import tempfile
with tempfile.TemporaryDirectory() as tempdir:
with tarfile.open(name=filename, mode='r') as tf:
sst_filename = next((name for name in tf.getnames()
if name.endswith('nc') and 'GHRSST-SSTskin' in name))
tf.extract(sst_filename, tempdir)
fullpath = os.path.join(tempdir, sst_filename)
self.nc = xr.open_dataset(fullpath,
decode_cf=True,
mask_and_scale=True,
engine=engine,
chunks={'ni': CHUNK_SIZE,
'nj': CHUNK_SIZE})
else:
self.nc = xr.open_dataset(filename,
decode_cf=True,
mask_and_scale=True,
engine=engine,
chunks={'ni': CHUNK_SIZE,
'nj': CHUNK_SIZE})

self.nc = self.nc.rename({'ni': 'x', 'nj': 'y'})
self.filename_info['start_time'] = datetime.strptime(
self.nc.start_time, '%Y%m%dT%H%M%SZ')
self.filename_info['end_time'] = datetime.strptime(
self.nc.stop_time, '%Y%m%dT%H%M%SZ')

def get_dataset(self, key, info):
"""Get any available dataset."""
stdname = info.get('standard_name')
return self.nc[stdname].squeeze()

@property
def start_time(self):
"""Get start time."""
return self.filename_info['start_time']

@property
def end_time(self):
"""Get end time."""
return self.filename_info['end_time']
3 changes: 2 additions & 1 deletion satpy/tests/reader_tests/__init__.py
Expand Up @@ -41,7 +41,7 @@
test_hsaf_grib, test_abi_l2_nc, test_eum_base,
test_ami_l1b, test_viirs_compact, test_seviri_l2_bufr,
test_geos_area, test_nwcsaf_msg, test_glm_l2,
test_seviri_l1b_icare, test_mimic_TPW2_nc,)
test_seviri_l1b_icare, test_mimic_TPW2_nc, test_slstr_l2,)

if sys.version_info < (2, 7):
import unittest2 as unittest
Expand Down Expand Up @@ -106,5 +106,6 @@ def suite():
mysuite.addTests(test_mimic_TPW2_nc.suite())
mysuite.addTests(test_glm_l2.suite())
mysuite.addTests(test_seviri_l1b_icare.suite())
mysuite.addTests(test_slstr_l2.suite())

return mysuite
80 changes: 80 additions & 0 deletions satpy/tests/reader_tests/test_slstr_l2.py
@@ -0,0 +1,80 @@
#!/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.slstr_l2 module."""

import unittest
from unittest import mock
from unittest.mock import MagicMock
from unittest.mock import patch
import xarray as xr
from satpy.readers.slstr_l2 import SLSTRL2FileHandler


class TestSLSTRL2Reader(unittest.TestCase):
"""Test Sentinel-3 SST L2 reader."""

@mock.patch('xarray.open_dataset')
def test_instantiate(self, mocked_dataset):
"""Test initialization of file handlers."""
filename_info = {}
tmp = MagicMock(start_time='20191120T125002Z', stop_time='20191120T125002Z')
tmp.rename.return_value = tmp
xr.open_dataset.return_value = tmp
SLSTRL2FileHandler('somedir/somefile.nc', filename_info, None)
mocked_dataset.assert_called()
mocked_dataset.reset_mock()

with patch('tarfile.open') as tf:
tf.return_value.__enter__.return_value = MagicMock(getnames=lambda *a: ["GHRSST-SSTskin.nc"])
SLSTRL2FileHandler('somedir/somefile.tar', filename_info, None)
mocked_dataset.assert_called()
mocked_dataset.reset_mock()

@mock.patch('xarray.open_dataset')
def test_get_dataset(self, mocked_dataset):
"""Test retrieval of datasets."""
filename_info = {}
tmp = MagicMock(start_time='20191120T125002Z', stop_time='20191120T125002Z')
tmp.rename.return_value = tmp
xr.open_dataset.return_value = tmp
test = SLSTRL2FileHandler('somedir/somefile.nc', filename_info, None)
test.nc = {'longitude': xr.Dataset(),
'latitude': xr.Dataset(),
'sea_surface_temperature': xr.Dataset(),
'sea_ice_fraction': xr.Dataset(),
}
test.get_dataset('longitude', {'standard_name': 'longitude'})
test.get_dataset('latitude', {'standard_name': 'latitude'})
test.get_dataset('sea_surface_temperature', {'standard_name': 'sea_surface_temperature'})
test.get_dataset('sea_ice_fraction', {'standard_name': 'sea_ice_fraction'})
with self.assertRaises(KeyError):
test.get_dataset('erroneous dataset', {'standard_name': 'erroneous dataset'})
mocked_dataset.assert_called()
mocked_dataset.reset_mock()


def suite():
"""Test suite for test_slstr_l2."""
loader = unittest.TestLoader()
mysuite = unittest.TestSuite()
mysuite.addTest(loader.loadTestsFromTestCase(TestSLSTRL2Reader))
return mysuite


if __name__ == '__main__':
unittest.main()

0 comments on commit 17efb75

Please sign in to comment.