Skip to content

Commit f35271d

Browse files
Merge pull request #77 from ninahakansson/cmsaf_vgac
VGAC converter
2 parents 39dae4d + 7399c55 commit f35271d

6 files changed

+463
-1
lines changed

bin/vgac2pps.py

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
# Copyright (c) 2019 level1c4pps developers
4+
#
5+
# This file is part of level1c4pps
6+
#
7+
# level1c4pps is free software: you can redistribute it and/or modify it
8+
# under the terms of the GNU General Public License as published by
9+
# the Free Software Foundation, either version 3 of the License, or
10+
# (at your option) any later version.
11+
#
12+
# level1c4pps is distributed in the hope that it will be useful, but
13+
# WITHOUT ANY WARRANTY; without even the implied warranty of
14+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
# General Public License for more details.
16+
#
17+
# You should have received a copy of the GNU General Public License
18+
# along with level1c4pps. If not, see <http://www.gnu.org/licenses/>.
19+
# Author(s):
20+
21+
# Martin Raspaud <martin.raspaud@smhi.se>
22+
# Nina Hakansson <nina.hakansson@smhi.se>
23+
# Adam.Dybbroe <adam.dybbroe@smhi.se>
24+
25+
"""Script to convert VIIRS level-1 to PPS level-1c format using Pytroll/Satpy."""
26+
27+
import argparse
28+
from level1c4pps.vgac2pps_lib import process_one_scene
29+
30+
31+
if __name__ == "__main__":
32+
""" Create PPS-format level1c data
33+
From a list of VIIRS level-1 files create a NWCSAF/PPS formatet level1c file for pps.
34+
"""
35+
parser = argparse.ArgumentParser(
36+
description=('Script to produce a PPS-level1c file for a VIIRS level-1 scene'))
37+
parser.add_argument('files', metavar='fileN', type=str, nargs='+',
38+
help='List of VIIRS files to process')
39+
parser.add_argument('-o', '--out_dir', type=str, nargs='?',
40+
required=False, default='.',
41+
help="Output directory where to store the level1c file")
42+
parser.add_argument('-ne', '--nc_engine', type=str, nargs='?',
43+
required=False, default='h5netcdf',
44+
help="Engine for saving netcdf files netcdf4 or h5netcdf (default).")
45+
parser.add_argument('-all_ch', '--all_channels', action='store_true',
46+
help="Save all 21 channels to level1c4pps file.")
47+
parser.add_argument('-n19', '--as_noaa19', action='store_true',
48+
help="Save only the AVHRR (1,2, 3B, 4, 5) channels to level1c4pps file. And apply SBAFs to the channels.")
49+
parser.add_argument('-pps_ch', '--pps_channels', action='store_true',
50+
help="Save only the necessary (for PPS) channels to level1c4pps file.")
51+
parser.add_argument('-avhrr_ch', '--avhrr_channels', action='store_true',
52+
help="Save only the AVHRR (1,2, 3B, 4, 5) channels to level1c4pps file.")
53+
parser.add_argument('-on', '--orbit_number', type=int, nargs='?',
54+
required=False, default=0,
55+
help="Orbit number (default is 00000).")
56+
57+
options = parser.parse_args()
58+
process_one_scene(options.files, options.out_dir, engine=options.nc_engine,
59+
all_channels=options.all_channels, pps_channels=options.pps_channels,
60+
orbit_n=options.orbit_number, as_noaa19=options.as_noaa19, avhrr_channels=options.avhrr_channels)

level1c4pps/__init__.py

+9
Original file line numberDiff line numberDiff line change
@@ -130,14 +130,18 @@
130130
SATPY_ANGLE_NAMES = {
131131
'solar_zenith': 'sunzenith', # no _angle
132132
'solar_zenith_angle': 'sunzenith',
133+
'sza': 'sunzenith',
133134
'solar_azimuth': 'sunazimuth', # no _angle
134135
'solar_azimuth_angle': 'sunazimuth',
136+
'azn': 'sunazimuth',
135137
'satellite_zenith_angle': 'satzenith',
136138
'sensor_zenith_angle': 'satzenith',
137139
'observation_zenith': 'satzenith',
140+
'vza': 'satzenith',
138141
'satellite_azimuth_angle': 'satazimuth',
139142
'sensor_azimuth_angle': 'satazimuth',
140143
'observation_azimuth': 'satazimuth',
144+
'azi': 'satazimuth',
141145
'sun_sensor_azimuth_difference_angle': 'azimuthdiff',
142146
}
143147

@@ -250,6 +254,10 @@ def dt64_to_datetime(dt64):
250254
seconds_since_epoch = (dt64 - unix_epoch) / one_second
251255
dt = datetime.utcfromtimestamp(seconds_since_epoch)
252256
return dt
257+
elif type(dt64) == np.float64:
258+
seconds_since_epoch = dt64
259+
dt = datetime.utcfromtimestamp(seconds_since_epoch)
260+
return dt
253261
return dt64
254262

255263

@@ -520,6 +528,7 @@ def platform_name_to_use_in_filename(platform_name):
520528
new_name = 'metopsga1'
521529
replace_dict = {'aqua': '2',
522530
'-': '',
531+
'jpss1': 'noaa20',
523532
'terra': '1',
524533
'suomi': ''}
525534
for orig, new in replace_dict.items():
Binary file not shown.

level1c4pps/tests/test_vgac2pps.py

+158
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
# Copyright (c) 2019 level1c4pps developers
4+
#
5+
# This file is part of level1c4pps
6+
#
7+
# level1c4pps is free software: you can redistribute it and/or modify it
8+
# under the terms of the GNU General Public License as published by
9+
# the Free Software Foundation, either version 3 of the License, or
10+
# (at your option) any later version.
11+
#
12+
# level1c4pps is distributed in the hope that it will be useful, but
13+
# WITHOUT ANY WARRANTY; without even the implied warranty of
14+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
# General Public License for more details.
16+
#
17+
# You should have received a copy of the GNU General Public License
18+
# along with level1c4pps. If not, see <http://www.gnu.org/licenses/>.
19+
# Author(s):
20+
21+
# Nina Hakansson <nina.hakansson@smhi.se>
22+
23+
"""Unit tests for the vgac2pps_lib module."""
24+
25+
import netCDF4
26+
import unittest
27+
from datetime import datetime
28+
try:
29+
from unittest import mock
30+
except ImportError:
31+
import mock
32+
from satpy import Scene
33+
34+
import level1c4pps.vgac2pps_lib as vgac2pps
35+
import numpy as np
36+
37+
38+
class TestVgac2PPS(unittest.TestCase):
39+
"""Test vgac2pps_lib."""
40+
41+
def setUp(self):
42+
"""Create a test scene."""
43+
vis006 = mock.MagicMock(attrs={'name': 'image0',
44+
'wavelength': [1, 2, 3, 'um'],
45+
'id_tag': 'ch_r06'})
46+
ir_108 = mock.MagicMock(attrs={'name': 'image1',
47+
'id_tag': 'ch_tb11',
48+
'wavelength': [1, 2, 3, 'um'],
49+
'start_time': datetime.utcnow(),
50+
'end_time': datetime.utcnow(),
51+
'history': 'dummy',
52+
'platform_name': 'tirosn',
53+
'orbit_number': 99999})
54+
qual_f = mock.MagicMock(attrs={'name': 'qual_flags',
55+
'id_tag': 'qual_flags'})
56+
scan_t = mock.MagicMock(attrs={'name': 'scanline_timestamps'})
57+
self.scene = Scene()
58+
self.scene.attrs['sensor'] = ['avhrr-1', 'avhrr-2', 'avhrr-3']
59+
scene_dict = {'M05': vis006, 'M15': ir_108,
60+
'qual_flags': qual_f, 'acq_time': scan_t}
61+
for key in scene_dict:
62+
pps_name = scene_dict[key].attrs['name']
63+
self.scene[key] = scene_dict[key]
64+
self.scene[key].attrs['name'] = pps_name
65+
66+
def test_get_encoding(self):
67+
"""Test the encoding for VGAC."""
68+
encoding_exp = {
69+
'image0': {'dtype': 'int16',
70+
'scale_factor': 0.01,
71+
'zlib': True,
72+
'complevel': 4,
73+
'_FillValue': -32767,
74+
'add_offset': 0.0},
75+
'image1': {'dtype': 'int16',
76+
'scale_factor': 0.01,
77+
'_FillValue': -32767,
78+
'zlib': True,
79+
'complevel': 4,
80+
'add_offset': 273.15},
81+
'qual_flags': {'dtype': 'int16', 'zlib': True,
82+
'complevel': 4, '_FillValue': -32001.0},
83+
'scanline_timestamps': {'dtype': 'int64', 'zlib': True,
84+
'units': 'Milliseconds since 1970-01-01',
85+
'complevel': 4, '_FillValue': -1.0},
86+
}
87+
encoding = vgac2pps.get_encoding_viirs(self.scene)
88+
self.assertDictEqual(encoding, encoding_exp)
89+
90+
def test_set_header_and_band_attrs(self):
91+
"""Test to set header_and_band_attrs."""
92+
vgac2pps.set_header_and_band_attrs(self.scene, orbit_n='12345')
93+
self.assertEqual(self.scene.attrs['orbit_number'], 12345)
94+
95+
def test_process_one_scene(self):
96+
"""Test process one scene for one example file."""
97+
98+
vgac2pps.process_one_scene(
99+
['./level1c4pps/tests/VGAC_VJ102MOD_A2018305_1042_n004946_K005.nc'],
100+
out_path='./level1c4pps/tests/',
101+
)
102+
filename = './level1c4pps/tests/S_NWC_viirs_noaa20_00000_20181101T1042080Z_20181101T1224090Z.nc'
103+
# written with hfnetcdf read with NETCDF4 ensure compatability
104+
pps_nc = netCDF4.Dataset(filename, 'r', format='NETCDF4') # Check compatability implicitly
105+
106+
for key in ['start_time', 'end_time', 'history', 'instrument',
107+
'orbit_number', 'platform',
108+
'sensor', 'source']:
109+
if key not in pps_nc.__dict__.keys():
110+
print("Missing in attributes:", key)
111+
self.assertTrue(key in pps_nc.__dict__.keys())
112+
113+
expected_vars = ['satzenith', 'azimuthdiff',
114+
'satazimuth', 'sunazimuth', 'sunzenith',
115+
'lon', 'lat',
116+
'image1', 'image2', 'image3', 'image4', 'image5',
117+
'image6', 'image7', 'image8', 'image9',
118+
'scanline_timestamps', 'time', 'time_bnds']
119+
for var in expected_vars:
120+
self.assertTrue(var in pps_nc.variables.keys())
121+
122+
np.testing.assert_almost_equal(pps_nc.variables['image1'].sun_earth_distance_correction_factor,
123+
1.0, decimal=4)
124+
def test_process_one_scene_n19(self):
125+
"""Test process one scene for one example file."""
126+
127+
vgac2pps.process_one_scene(
128+
['./level1c4pps/tests/VGAC_VJ102MOD_A2018305_1042_n004946_K005.nc'],
129+
out_path='./level1c4pps/tests/',
130+
as_noaa19=True,
131+
)
132+
filename = './level1c4pps/tests/S_NWC_avhrr_vgac20_00000_20181101T1042080Z_20181101T1224090Z.nc'
133+
filename_viirs = './level1c4pps/tests/S_NWC_viirs_noaa20_00000_20181101T1042080Z_20181101T1224090Z.nc'
134+
# written with hfnetcdf read with NETCDF4 ensure compatability
135+
pps_nc = netCDF4.Dataset(filename, 'r', format='NETCDF4') # Check compatability implicitly
136+
pps_nc_viirs = netCDF4.Dataset(filename_viirs, 'r', format='NETCDF4') # Check compatability implicitly
137+
138+
for key in ['start_time', 'end_time', 'history', 'instrument',
139+
'orbit_number', 'platform',
140+
'sensor', 'source']:
141+
if key not in pps_nc.__dict__.keys():
142+
print("Missing in attributes:", key)
143+
self.assertTrue(key in pps_nc.__dict__.keys())
144+
145+
expected_vars = ['satzenith', 'azimuthdiff',
146+
'satazimuth', 'sunazimuth', 'sunzenith',
147+
'lon', 'lat',
148+
'image1', 'image2', 'image3', 'image4', 'image5',
149+
'scanline_timestamps', 'time', 'time_bnds']
150+
151+
for var in expected_vars:
152+
self.assertTrue(var in pps_nc.variables.keys())
153+
154+
np.testing.assert_almost_equal(pps_nc.variables['image1'].sun_earth_distance_correction_factor,
155+
1.0, decimal=4)
156+
157+
np.testing.assert_equal(pps_nc.__dict__["platform"], "vgac20")
158+
self.assertTrue(np.abs(pps_nc.variables['image1'][0,0,0] - pps_nc_viirs.variables['image1'][0,0,0])>0.01)

0 commit comments

Comments
 (0)