-
Notifications
You must be signed in to change notification settings - Fork 307
Add reader for EarthCARE MSI L1 data #3080
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #3080 +/- ##
========================================
Coverage 96.18% 96.19%
========================================
Files 391 393 +2
Lines 56633 56734 +101
========================================
+ Hits 54471 54573 +102
+ Misses 2162 2161 -1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Pull Request Test Coverage Report for Build 14446587353Details
💛 - Coveralls |
Here's a self-contained example if it helps in reviewing :-) Call as When given several input files, they are shown together. import argparse
import matplotlib.pyplot as plt
import satpy
import cartopy
from satpy.writers import get_enhanced_image
import numpy as np
ECA_MSI_BANDS = ['SWIR1', 'SWIR2', 'TIR1', 'TIR2', 'TIR3', 'VIS', 'VNIR']
parser = argparse.ArgumentParser()
parser.add_argument('input', nargs='+')
parser.add_argument('--band', required=True, choices=ECA_MSI_BANDS)
parser.add_argument('-v', action="store_true")
args = parser.parse_args()
fig = plt.figure(figsize=(5,3.25))
crs = cartopy.crs.PlateCarree()
ax = fig.add_subplot(111, projection=crs)
ax.coastlines()
ax.gridlines(draw_labels=True)
scn = satpy.Scene(filenames=args.input, reader='msi_l1c_earthcare')
scn.load([args.band, 'latitude', 'longitude'])
if args.band in scn.available_dataset_names():
# For bands, simply load calibrated data
im = scn[args.band][:,:]
else:
# For composites, use get_enhanced_image and convert to RGB
im = get_enhanced_image(scn[args.band])
im.stretch()
tmp = (im.data.transpose('y', 'x', 'bands')*255).astype(np.uint8)
im = tmp
lats = scn['latitude'].data
lons = scn['longitude'].data
ax.pcolormesh(lons, lats, im, transform=crs)
plt.show() |
class FakeHDF5FileHandler2(FakeHDF5FileHandler): | ||
"""Swap-in HDF5 File Handler.""" | ||
|
||
|
||
def _setup_test_data(self, N_BANDS, N_SCANS, N_COLS): | ||
# Set some default attributes | ||
data = { | ||
"ScienceData/pixel_values": | ||
xr.DataArray( | ||
da.ones((N_BANDS, N_SCANS, N_COLS), chunks=1024, dtype=np.float32), | ||
attrs={"units": "Wm-2 sr-1 or K", "DIMENSION_LIST": DIMLIST}, | ||
dims=("band", "dim_2", "dim_1")), | ||
"ScienceData/land_flag": | ||
xr.DataArray( | ||
da.ones((N_SCANS, N_COLS), chunks=1024, dtype=np.uint16), | ||
attrs={"units": ""}, | ||
dims=("along_track", "across_track")), | ||
"ScienceData/solar_azimuth_angle": | ||
xr.DataArray( | ||
da.ones((N_SCANS, N_COLS), chunks=1024, dtype=np.float32), | ||
attrs={"units": "degrees"}, | ||
dims=("along_track", "across_track")), | ||
"ScienceData/longitude": | ||
xr.DataArray( | ||
da.ones((N_SCANS, N_COLS), chunks=1024, dtype=np.float32), | ||
attrs={"units": "degrees"}, | ||
dims=("along_track", "across_track")), | ||
"ScienceData/latitude": | ||
xr.DataArray( | ||
da.ones((N_SCANS, N_COLS), chunks=1024, dtype=np.float32), | ||
attrs={"units": "degrees"}, | ||
dims=("along_track", "across_track")), | ||
"ScienceData/solar_spectral_irradiance": | ||
xr.DataArray( | ||
da.array(SOL_IRRAD), | ||
attrs={"units": "W m-2"}, | ||
dims=("band", "across_track")), | ||
} | ||
|
||
return data | ||
|
||
def get_test_content(self, filename, filename_info, filetype_info): | ||
"""Mimic reader input file content.""" | ||
test_content = self._setup_test_data(N_BANDS, N_SCANS, N_COLS) | ||
return test_content | ||
|
||
|
||
class ECMSIL1CTester: | ||
"""Test MSI/EarthCARE L1C Reader.""" | ||
|
||
def setup_method(self): | ||
"""Wrap HDF5 file handler with our own fake handler.""" | ||
from satpy._config import config_search_paths | ||
from satpy.readers.msi_ec_l1c_h5 import MSIECL1CFileHandler | ||
self.reader_configs = config_search_paths(os.path.join("readers", self.yaml_file)) | ||
# http://stackoverflow.com/questions/12219967/how-to-mock-a-base-class-with-python-mock-library | ||
self.p = mock.patch.object(MSIECL1CFileHandler, "__bases__", (FakeHDF5FileHandler2,)) | ||
self.fake_handler = self.p.start() | ||
self.p.is_local = True | ||
|
||
def teardown_method(self): | ||
"""Stop wrapping the HDF5 file handler.""" | ||
self.p.stop() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nowadays we try to create stub files using fixtures instead of mocking, to you think this is something we could do here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you point me to a test doing that? It is often confusing to know which way is "the good way" :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here an example, I hope it's good enough
satpy/satpy/tests/reader_tests/test_insat3d_img_l1b_h5.py
Lines 91 to 102 in cd20189
@pytest.fixture(scope="session") | |
def insat_filename(tmp_path_factory): | |
"""Create a fake insat 3d l1b file.""" | |
filename = tmp_path_factory.mktemp("data") / "3DIMG_25OCT2022_0400_L1B_STD_V01R00.h5" | |
with h5netcdf.File(filename, mode="w") as h5f: | |
h5f.dimensions = dimensions | |
h5f.attrs.update(global_attrs) | |
for resolution, channels in CHANNELS_BY_RESOLUTION.items(): | |
_create_channels(channels, h5f, resolution) | |
_create_lonlats(h5f, resolution) | |
return filename |
:D
Could you paste the image of a composite here? It’s always nice to have as a reference. |
If Pierre is busy, I'll add an example image this evening :) |
1. Use a 2D array for SOL_IRRAD (of type np.float32) 2. Change NonStandard/solar_irradiance to ScienceData/solar_spectral_irradiance 3. Change band name NIR to VNIR 4. Use variable N_COLS instead of number 2048
Hello @simonrp84 The tests run fine but the decreasing coverage makes me realize that I don't use the |
Note (for Simon): I didn't actually change the reader itself, so all the data loads "as before". Also: the product user guide does not mention any |
I suspect that may have been some artefact in the commissioning data that was removed. |
The code did not seem necessary for production data.
Hi Simon, thanks for checking. I removed the code. I also checked that I could actually load MSI data from disk :-) |
ok @mraspaud the tests are now file-based. only ubuntu latest fails but I think that this is the case for other open PRs. |
This PR adds a reader to satpy for EarthCARE MSI L1 data. It was initiated by @simonrp84