diff --git a/satpy/etc/readers/fci_l2_nc.yaml b/satpy/etc/readers/fci_l2_nc.yaml index d4b8c65b1e..9a74b9b934 100644 --- a/satpy/etc/readers/fci_l2_nc.yaml +++ b/satpy/etc/readers/fci_l2_nc.yaml @@ -7,17 +7,40 @@ reader: reader: !!python/name:satpy.readers.yaml_reader.FileYAMLReader file_types: - # EUMETSAT MTG FCI L2 Optimal Cloud Analysis files in NetCDF4 format + # Filename examples + # FCI_SIM_OCA_2L_2KM_{creation_time:%Y%m%d}_1700.nc + # W_XX-EUMETSAT-Darmstadt,IMG+SAT,MTI1+FCI-2-ASR--FD------NC4E_C_EUMT_20201105031219_L2PF_DEV_20170410171000_20170410172000_N__T_0104_0000.nc + nc_fci_oca: file_reader: !!python/name:satpy.readers.fci_l2_nc.FciL2NCFileHandler - # TODO: Pattern based on the available test files, not compatible with MTG GFS definitions - file_patterns: ['FCI_SIM_OCA_2L_2KM_{creation_time:%Y%m%d}_1700.nc'] + file_patterns: ['FCI_SIM_OCA_2L_2KM_{creation_time:%Y%m%d}_1700.nc', + 'W_XX-EUMETSAT-{reception_location},{instrument},{long_platform_id}+{processing_location}-{level}-OCA--{temp_str}_C_EUMT_{creation_time:%Y%m%d%H%M%S}_L2PF_{env}_{start_time:%Y%m%d%H%M%S}_{end_time:%Y%m%d%H%M%S}_N__T_{rep_cycle_in_day}_{rep_cycle_count}.nc'] - # EUMETSAT MTG FCI L2 Cloud Mask files in NetCDF4 format nc_fci_clm: file_reader: !!python/name:satpy.readers.fci_l2_nc.FciL2NCFileHandler + file_patterns: ['FCI_SIM_CLM_2KM_{creation_time:%Y%m%d}_1700.nc', + 'W_XX-EUMETSAT-{reception_location},{instrument},{long_platform_id}+{processing_location}-{level}-CLM--{temp_str}_C_EUMT_{creation_time:%Y%m%d%H%M%S}_L2PF_{env}_{start_time:%Y%m%d%H%M%S}_{end_time:%Y%m%d%H%M%S}_N__T_{rep_cycle_in_day}_{rep_cycle_count}.nc'] + + nc_fci_test_clm: + file_reader: !!python/name:satpy.readers.fci_l2_nc.FciL2NCFileHandler + file_patterns: [ 'W_XX-EUMETSAT-{reception_location},{instrument},{long_platform_id}+{processing_location}-{level}-CLMTest-{temp_str}_C_EUMT_{creation_time:%Y%m%d%H%M%S}_L2PF_{env}_{start_time:%Y%m%d%H%M%S}_{end_time:%Y%m%d%H%M%S}_N__T_{rep_cycle_in_day}_{rep_cycle_count}.nc' ] + + nc_fci_ct: + file_reader: !!python/name:satpy.readers.fci_l2_nc.FciL2NCFileHandler + file_patterns: ['W_XX-EUMETSAT-{reception_location},{instrument},{long_platform_id}+{processing_location}-{level}-CT--{temp_str}_C_EUMT_{creation_time:%Y%m%d%H%M%S}_L2PF_{env}_{start_time:%Y%m%d%H%M%S}_{end_time:%Y%m%d%H%M%S}_N__T_{rep_cycle_in_day}_{rep_cycle_count}.nc'] + + nc_fci_cloud: + file_reader: !!python/name:satpy.readers.fci_l2_nc.FciL2NCFileHandler + file_patterns: ['W_XX-EUMETSAT-{reception_location},{instrument},{long_platform_id}+{processing_location}-{level}-CTTH--{temp_str}_C_EUMT_{creation_time:%Y%m%d%H%M%S}_L2PF_{env}_{start_time:%Y%m%d%H%M%S}_{end_time:%Y%m%d%H%M%S}_N__T_{rep_cycle_in_day}_{rep_cycle_count}.nc' ] + + nc_fci_asr: + file_reader: !!python/name:satpy.readers.fci_l2_nc.FciL2NCSegmentFileHandler + file_patterns: [ "W_XX-EUMETSAT-{reception_location},{instrument},{long_platform_id}+{processing_location}-{level}-ASR--{temp_str}_C_EUMT_{creation_time:%Y%m%d%H%M%S}_L2PF_{env}_{start_time:%Y%m%d%H%M%S}_{end_time:%Y%m%d%H%M%S}_N__T_{rep_cycle_in_day}_{rep_cycle_count}.nc"] + + nc_fci_gii: + file_reader: !!python/name:satpy.readers.fci_l2_nc.FciL2NCSegmentFileHandler # TODO: Pattern based on the available test files, not compatible with MTG GFS definitions - file_patterns: ['FCI_SIM_CLM_2KM_{creation_time:%Y%m%d}_1700.nc'] + file_patterns: ["W_XX-EUMETSAT-{reception_location},{instrument},{long_platform_id}+{processing_location}-{level}-GII--{temp_str}_C_EUMT_{creation_time:%Y%m%d%H%M%S}_L2PF_{env}_{start_time:%Y%m%d%H%M%S}_{end_time:%Y%m%d%H%M%S}_N__T_{rep_cycle_in_day}_{rep_cycle_count}.nc"] datasets: @@ -166,3 +189,815 @@ datasets: standard_name: quality_index fill_value: -999 mask_value: 0 + +# CLM Test + cloud_test_sit1_flag: + name: cloud_test_sit1_flag + file_type: nc_fci_test_clm + file_key: cloud_mask_test_flag + standard_name: cloud_mask_test_sit1_flag + extract_byte: 0 + fill_value: -999 + mask_value: 0 + + cloud_test_cmt1_flag: + name: cloud_test_cmt1_flag + file_type: nc_fci_test_clm + file_key: cloud_mask_test_flag + standard_name: cloud_mask_test_cmt1_flag + extract_byte: 1 + fill_value: -999 + mask_value: 0 + + cloud_test_cmt2_flag: + name: cloud_test_cmt2_flag + file_type: nc_fci_test_clm + file_key: cloud_mask_test_flag + standard_name: cloud_mask_test_cmt2_flag + extract_byte: 2 + fill_value: -999 + mask_value: 0 + + cloud_test_cmt3_flag: + name: cloud_test_cmt3_flag + file_type: nc_fci_test_clm + file_key: cloud_mask_test_flag + standard_name: cloud_mask_test_cmt3_flag + extract_byte: 3 + fill_value: -999 + mask_value: 0 + + cloud_test_cmt4_flag: + name: cloud_test_cmt4_flag + file_type: nc_fci_test_clm + file_key: cloud_mask_test_flag + standard_name: cloud_mask_test_cmt4_flag + extract_byte: 4 + fill_value: -999 + mask_value: 0 + + cloud_test_cmt5_flag: + name: cloud_test_cmt5_flag + file_type: nc_fci_test_clm + file_key: cloud_mask_test_flag + standard_name: cloud_mask_test_cmt5_flag + extract_byte: 5 + fill_value: -999 + mask_value: 0 + + cloud_test_cmt6_flag: + name: cloud_test_cmt6_flag + file_type: nc_fci_test_clm + file_key: cloud_mask_test_flag + standard_name: cloud_mask_test_cmt6_flag + extract_byte: 6 + fill_value: -999 + mask_value: 0 + + cloud_test_cmt7_flag: + name: cloud_test_cmt7_flag + file_type: nc_fci_test_clm + file_key: cloud_mask_test_flag + standard_name: cloud_mask_test_cmt7_flag + extract_byte: 7 + fill_value: -999 + mask_value: 0 + + cloud_test_cmt8_flag: + name: cloud_test_cmt8_flag + file_type: nc_fci_test_clm + file_key: cloud_mask_test_flag + standard_name: cloud_mask_test_cmt8_flag + extract_byte: 8 + fill_value: -999 + mask_value: 0 + + cloud_test_cmt9_flag: + name: cloud_test_cmt9_flag + file_type: nc_fci_test_clm + file_key: cloud_mask_test_flag + standard_name: cloud_mask_test_cmt9_flag + extract_byte: 9 + fill_value: -999 + mask_value: 0 + + cloud_test_cmt10_flag: + name: cloud_test_cmt10_flag + file_type: nc_fci_test_clm + file_key: cloud_mask_test_flag + standard_name: cloud_mask_test_cmt0_flag + extract_byte: 10 + fill_value: -999 + mask_value: 0 + + cloud_test_cmt11_flag: + name: cloud_test_cmt11_flag + file_type: nc_fci_test_clm + file_key: cloud_mask_test_flag + standard_name: cloud_mask_test_cmt11_flag + extract_byte: 11 + fill_value: -999 + mask_value: 0 + + cloud_test_cmt12_flag: + name: cloud_test_cmt12_flag + file_type: nc_fci_test_clm + file_key: cloud_mask_test_flag + standard_name: cloud_mask_test_cmt12_flag + extract_byte: 12 + fill_value: -999 + mask_value: 0 + + cloud_test_cmt13_flag: + name: cloud_test_cmt13_flag + file_type: nc_fci_test_clm + file_key: cloud_mask_test_flag + standard_name: cloud_mask_test_cmt13_flag + extract_byte: 13 + fill_value: -999 + mask_value: 0 + + cloud_test_cmt14_flag: + name: cloud_test_cmt14_flag + file_type: nc_fci_test_clm + file_key: cloud_mask_test_flag + standard_name: cloud_mask_test_cmt14_flag + extract_byte: 14 + fill_value: -999 + mask_value: 0 + + cloud_test_opqt_flag: + name: cloud_test_opqt_flag + file_type: nc_fci_test_clm + file_key: cloud_mask_test_flag + standard_name: cloud_mask_test_opqt_flag + extract_byte: 15 + fill_value: -999 + mask_value: 0 + + cloud_test_cmrt1_flag: + name: cloud_test_cmrt1_flag + file_type: nc_fci_test_clm + file_key: cloud_mask_test_flag + standard_name: cloud_mask_test_cmrt1_flag + extract_byte: 16 + fill_value: -999 + mask_value: 0 + + cloud_test_cmrt2_flag: + name: cloud_test_cmrt2_flag + file_type: nc_fci_test_clm + file_key: cloud_mask_test_flag + standard_name: cloud_mask_test_cmrt2_flag + extract_byte: 17 + fill_value: -999 + mask_value: 0 + + cloud_test_cmrt3_flag: + name: cloud_test_cmrt3_flag + file_type: nc_fci_test_clm + file_key: cloud_mask_test_flag + standard_name: cloud_mask_test_cmrt3_flag + extract_byte: 18 + fill_value: -999 + mask_value: 0 + + cloud_test_cmrt4_flag: + name: cloud_test_cmrt4_flag + file_type: nc_fci_test_clm + file_key: cloud_mask_test_flag + standard_name: cloud_mask_test_cmrt4_flag + extract_byte: 19 + fill_value: -999 + mask_value: 0 + + cloud_test_cmrt5_flag: + name: cloud_test_cmrt5_flag + file_type: nc_fci_test_clm + file_key: cloud_mask_test_flag + standard_name: cloud_mask_test_cmrt5_flag + extract_byte: 20 + fill_value: -999 + mask_value: 0 + + cloud_test_cmrt6_flag: + name: cloud_test_cmrt6_flag + file_type: nc_fci_test_clm + file_key: cloud_mask_test_flag + standard_name: cloud_mask_test_cmrt6_flag + extract_byte: 21 + fill_value: -999 + mask_value: 0 + + cloud_test_dust_flag: + name: cloud_test_dust_flag + file_type: nc_fci_test_clm + file_key: cloud_mask_test_flag + standard_name: cloud_mask_test_dust_flag + extract_byte: 22 + fill_value: -999 + mask_value: 0 + + cloud_test_ash_flag: + name: cloud_test_ash_flag + file_type: nc_fci_test_clm + file_key: cloud_mask_test_flag + standard_name: cloud_mask_test_ash_flag + extract_byte: 23 + fill_value: -999 + mask_value: 0 + + cloud_test_dust_ash_flag: + name: cloud_test_dust_ash_flag + file_type: nc_fci_test_clm + file_key: cloud_mask_test_flag + standard_name: cloud_mask_test_dust_ash_flag + extract_byte: 24 + fill_value: -999 + mask_value: 0 + + cloud_test_sit1: + name: cloud_test_sit1 + file_type: nc_fci_test_clm + file_key: cloud_mask_test_result + standard_name: cloud_mask_test_sit1 + extract_byte: 0 + fill_value: -999 + mask_value: 0 + + cloud_test_cmt1: + name: cloud_test_cmt1 + file_type: nc_fci_test_clm + file_key: cloud_mask_test_result + standard_name: cloud_mask_test_cmt1 + extract_byte: 1 + fill_value: -999 + mask_value: 0 + + cloud_test_cmt2: + name: cloud_test_cmt2 + file_type: nc_fci_test_clm + file_key: cloud_mask_test_result + standard_name: cloud_mask_test_cmt2 + extract_byte: 2 + fill_value: -999 + mask_value: 0 + + cloud_test_cmt3: + name: cloud_test_cmt3 + file_type: nc_fci_test_clm + file_key: cloud_mask_test_result + standard_name: cloud_mask_test_cmt3 + extract_byte: 3 + fill_value: -999 + mask_value: 0 + + cloud_test_cmt4: + name: cloud_test_cmt4 + file_type: nc_fci_test_clm + file_key: cloud_mask_test_result + standard_name: cloud_mask_test_cmt4 + extract_byte: 4 + fill_value: -999 + mask_value: 0 + + cloud_test_cmt5: + name: cloud_test_cmt5 + file_type: nc_fci_test_clm + file_key: cloud_mask_test_result + standard_name: cloud_mask_test_cmt5 + extract_byte: 5 + fill_value: -999 + mask_value: 0 + + cloud_test_cmt6: + name: cloud_test_cmt6 + file_type: nc_fci_test_clm + file_key: cloud_mask_test_result + standard_name: cloud_mask_test_cmt6 + extract_byte: 6 + fill_value: -999 + mask_value: 0 + + cloud_test_cmt7: + name: cloud_test_cmt7 + file_type: nc_fci_test_clm + file_key: cloud_mask_test_result + standard_name: cloud_mask_test_cmt7 + extract_byte: 7 + fill_value: -999 + mask_value: 0 + + cloud_test_cmt8: + name: cloud_test_cmt8 + file_type: nc_fci_test_clm + file_key: cloud_mask_test_result + standard_name: cloud_mask_test_cmt8 + extract_byte: 8 + fill_value: -999 + mask_value: 0 + + cloud_test_cmt9: + name: cloud_test_cmt9 + file_type: nc_fci_test_clm + file_key: cloud_mask_test_result + standard_name: cloud_mask_test_cmt9 + extract_byte: 9 + fill_value: -999 + mask_value: 0 + + cloud_test_cmt10: + name: cloud_test_cmt10 + file_type: nc_fci_test_clm + file_key: cloud_mask_test_result + standard_name: cloud_mask_test_cmt10 + extract_byte: 10 + fill_value: -999 + mask_value: 0 + + cloud_test_cmt11: + name: cloud_test_cmt11 + file_type: nc_fci_test_clm + file_key: cloud_mask_test_result + standard_name: cloud_mask_test_cmt11 + extract_byte: 11 + fill_value: -999 + mask_value: 0 + + cloud_test_cmt12: + name: cloud_test_cmt12 + file_type: nc_fci_test_clm + file_key: cloud_mask_test_result + standard_name: cloud_mask_test_cmt12 + extract_byte: 12 + fill_value: -999 + mask_value: 0 + + cloud_test_cmt13: + name: cloud_test_cmt13 + file_type: nc_fci_test_clm + file_key: cloud_mask_test_result + standard_name: cloud_mask_test_cmt13 + extract_byte: 13 + fill_value: -999 + mask_value: 0 + + cloud_test_cmt14: + name: cloud_test_cmt14 + file_type: nc_fci_test_clm + file_key: cloud_mask_test_result + standard_name: cloud_mask_test_cmt14 + extract_byte: 14 + fill_value: -999 + mask_value: 0 + + cloud_test_opqt: + name: cloud_test_opqt + file_type: nc_fci_test_clm + file_key: cloud_mask_test_result + standard_name: cloud_mask_test_opqt + extract_byte: 15 + fill_value: -999 + mask_value: 0 + + cloud_test_cmrt1: + name: cloud_test_cmrt1 + file_type: nc_fci_test_clm + file_key: cloud_mask_test_result + standard_name: cloud_mask_test_cmrt1 + extract_byte: 16 + fill_value: -999 + mask_value: 0 + + cloud_test_cmrt2: + name: cloud_test_cmrt2 + file_type: nc_fci_test_clm + file_key: cloud_mask_test_result + standard_name: cloud_mask_test_cmrt2 + extract_byte: 17 + fill_value: -999 + mask_value: 0 + + cloud_test_cmrt3: + name: cloud_test_cmrt3 + file_type: nc_fci_test_clm + file_key: cloud_mask_test_result + standard_name: cloud_mask_test_cmrt3 + extract_byte: 18 + fill_value: -999 + mask_value: 0 + + cloud_test_cmrt4: + name: cloud_test_cmrt4 + file_type: nc_fci_test_clm + file_key: cloud_mask_test_result + standard_name: cloud_mask_test_cmrt4 + extract_byte: 19 + fill_value: -999 + mask_value: 0 + + cloud_test_cmrt5: + name: cloud_test_cmrt5 + file_type: nc_fci_test_clm + file_key: cloud_mask_test_result + standard_name: cloud_mask_test_cmrt5 + extract_byte: 20 + fill_value: -999 + mask_value: 0 + + cloud_test_cmrt6: + name: cloud_test_cmrt6 + file_type: nc_fci_test_clm + file_key: cloud_mask_test_result + standard_name: cloud_mask_test_cmrt6 + extract_byte: 21 + fill_value: -999 + mask_value: 0 + + cloud_test_dust: + name: cloud_test_dust + file_type: nc_fci_test_clm + file_key: cloud_mask_test_result + standard_name: cloud_mask_test_dust + extract_byte: 22 + fill_value: -999 + mask_value: 0 + + cloud_test_ash: + name: cloud_test_ash + file_type: nc_fci_test_clm + file_key: cloud_mask_test_result + standard_name: cloud_mask_test_ash + extract_byte: 23 + fill_value: -999 + mask_value: 0 + + cloud_test_dust_ash: + name: cloud_test_dust_ash + file_type: nc_fci_test_clm + file_key: cloud_mask_test_result + standard_name: cloud_mask_test_dust_ash + extract_byte: 24 + fill_value: -999 + mask_value: 0 + + cloud_mask_cmrt6_result: + name: cloud_mask_cmrt6_result + file_type: nc_fci_test_clm + file_key: cloud_mask_cmrt6_test_result + standard_name: cloud_mask_cmrt6_result + extract_byte: 0 +# fill_value: -999 + mask_value: 0 + + latitude: + name: latitude + file_key: 'latitude' +# resolution: + file_type: [nc_fci_gii, nc_fci_asr] + standard_name: latitude + fill_value: -32767 + mask_value: -32767 + units: degree_north + + + longitude: + name: longitude + file_key: 'longitude' +# resolution: + file_type: [nc_fci_gii, nc_fci_asr] + standard_name: longitude + fill_value: -32767 + mask_value: -32767 + units: degree_east + + + # GII + k_index: + name: k_index + file_type: nc_fci_gii + file_key: k_index + standard_name: k_index + fill_value: -32767 + mask_value: -32767 + coordinates: + - longitude + - latitude + + lifted_index: + name: lifted_index + file_type: nc_fci_gii + file_key: lifted_index + standard_name: lifted_index + fill_value: -32767 + mask_value: -32767 + coordinates: + - longitude + - latitude + + percent_cloud_free: + name: percent_cloud_free + file_type: nc_fci_gii + file_key: percent_cloud_free + standard_name: percent_cloud_free + fill_value: -127 + mask_value: -127 + coordinates: + - longitude + - latitude + + prec_water_high: + name: prec_water_high + file_type: nc_fci_gii + file_key: prec_water_high + standard_name: prec_water_high + fill_value: 65535 + mask_value: 65535 + coordinates: + - longitude + - latitude + + prec_water_low: + name: prec_water_low + file_type: nc_fci_gii + file_key: prec_water_low + standard_name: prec_water_low + fill_value: 65535 + mask_value: 65535 + coordinates: + - longitude + - latitude + + prec_water_mid: + name: prec_water_mid + file_type: nc_fci_gii + file_key: prec_water_mid + standard_name: prec_water_mid + fill_value: 65535 + mask_value: 65535 + coordinates: + - longitude + - latitude + + prec_water_total: + name: prec_water_total + file_type: nc_fci_gii + file_key: prec_water_total + standard_name: prec_water_total + fill_value: 65535 + mask_value: 65535 + coordinates: + - longitude + - latitude + +# FCI CT L2 + cloud_phase: + name: cloud_phase + file_type: nc_fci_ct + file_key: cloud_phase +# standard_name: cloud_phase + fill_value: 0 + mask_value: 0 + + cloud_type: + name: cloud_type + file_type: nc_fci_ct + file_key: cloud_type +# standard_name: cloud_type + fill_value: 0 + mask_value: 0 + +# FCI CTTH Product + cloud_top_aviation_height: + name: cloud_top_aviation_height + file_type: nc_fci_cloud + file_key: cloud_top_aviation_height + fill_value: 0 + mask_value: 0 + + cloud_top_height: + name: cloud_top_height + file_type: nc_fci_cloud + file_key: cloud_top_height + fill_value: 0 + mask_value: 0 + + cloud_top_pressure: + name: cloud_top_pressure + file_type: nc_fci_th + file_key: cloud_top_pressure + fill_value: 0 + mask_value: 0 + + cloud_top_temperature: + name: cloud_top_temperature + file_type: nc_fci_cloud + file_key: cloud_top_temperature + fill_value: 0 + mask_value: 0 + + effective_cloudiness: + name: effective_cloudiness + file_type: nc_fci_cloud + file_key: effective_cloudiness + fill_value: 0 + mask_value: 0 + +# ASR + bt_max: + name: bt_max + file_type: nc_fci_asr + file_key: bt_max + standard_name: bt_max + fill_value: 65535 + mask_value: 65535 + coordinates: + - longitude + - latitude + + bt_mean: + name: bt_mean + file_type: nc_fci_asr + file_key: bt_mean + standard_name: bt_mean + fill_value: 65535 + mask_value: 65535 + coordinates: + - longitude + - latitude + + bt_min: + name: bt_min + file_type: nc_fci_asr + file_key: bt_min + standard_name: bt_min + fill_value: 65535 + mask_value: 65535 + coordinates: + - longitude + - latitude + + bt_std: + name: bt_std + file_type: nc_fci_asr + file_key: bt_std + standard_name: bt_std + fill_value: 65535 + mask_value: 65535 + coordinates: + - longitude + - latitude + + radiance_max: + name: radiance_max + file_type: nc_fci_asr + file_key: radiance_max + standard_name: radiance_max + fill_value: 65535 + mask_value: 65535 + coordinates: + - longitude + - latitude + + radiance_mean: + name: radiance_mean + file_type: nc_fci_asr + file_key: radiance_mean + standard_name: radiance_mean + fill_value: 65535 + mask_value: 65535 + coordinates: + - longitude + - latitude + + radiance_min: + name: radiance_min + file_type: nc_fci_asr + file_key: radiance_min + standard_name: radiance_min + fill_value: 65535 + mask_value: 65535 + coordinates: + - longitude + - latitude + + radiance_std: + name: radiance_std + file_type: nc_fci_asr + file_key: radiance_std + standard_name: radiance_std + fill_value: 65535 + mask_value: 65535 + coordinates: + - longitude + - latitude + + reflectance_max: + name: reflectance_max + file_type: nc_fci_asr + file_key: reflectance_max + standard_name: reflectance_max + fill_value: 65535 + mask_value: 65535 + coordinates: + - longitude + - latitude + + reflectance_mean: + name: reflectance_mean + file_type: nc_fci_asr + file_key: reflectance_mean + standard_name: reflectance_mean + fill_value: 65535 + mask_value: 65535 + coordinates: + - longitude + - latitude + + reflectance_min: + name: reflectance_min + file_type: nc_fci_asr + file_key: reflectance_min + standard_name: reflectance_min + fill_value: 65535 + mask_value: 65535 + coordinates: + - longitude + - latitude + + reflectance_std: + name: reflectance_std + file_type: nc_fci_asr + file_key: reflectance_std + standard_name: reflectance_std + fill_value: 65535 + mask_value: 65535 + coordinates: + - longitude + - latitude + + quality_bt: + name: quality_bt + file_type: nc_fci_asr + file_key: quality_bt + standard_name: quality_bt + fill_value: 65535 + mask_value: 65535 + coordinates: + - longitude + - latitude + + quality_reflectance: + name: quality_reflectance + file_type: nc_fci_asr + file_key: quality_reflectance + standard_name: quality_reflectance + fill_value: 65535 + mask_value: 65535 + coordinates: + - longitude + - latitude + + quality_radiance: + name: quality_radiance + file_type: nc_fci_asr + file_key: quality_radiance + standard_name: quality_radiance + fill_value: 65535 + mask_value: 65535 + coordinates: + - longitude + - latitude + + land_pixel_percent: + name: land_pixel_percent + file_type: nc_fci_asr + file_key: land_pixel_percent + standard_name: land_pixel_percent + fill_value: 65535 + mask_value: 65535 + coordinates: + - longitude + - latitude + + water_pixel_percent: + name: water_pixel_percent + file_type: nc_fci_asr + file_key: water_pixel_percent + standard_name: water_pixel_percent + fill_value: 65535 + mask_value: 65535 + coordinates: + - longitude + - latitude + + pixel_percentage: + name: pixel_percentage + file_type: nc_fci_asr + file_key: pixel_percentage + standard_name: pixel_percentage + fill_value: 65535 + mask_value: 65535 + coordinates: + - longitude + - latitude \ No newline at end of file diff --git a/satpy/readers/fci_l2_nc.py b/satpy/readers/fci_l2_nc.py index a03b807b28..1c6a0d18b1 100644 --- a/satpy/readers/fci_l2_nc.py +++ b/satpy/readers/fci_l2_nc.py @@ -19,49 +19,26 @@ """Reader for the FCI L2 products in NetCDF4 format.""" import logging +from contextlib import suppress +from datetime import datetime, timedelta + import numpy as np import xarray as xr -from datetime import datetime, timedelta - -from satpy.readers.file_handlers import BaseFileHandler -from satpy.readers._geos_area import get_area_definition, make_ext from satpy import CHUNK_SIZE +from satpy.readers._geos_area import get_area_definition, make_ext +from satpy.readers.file_handlers import BaseFileHandler logger = logging.getLogger(__name__) PRODUCT_DATA_DURATION_MINUTES = 20 +SSP_DEFAULT = 0.0 -class FciL2NCFileHandler(BaseFileHandler): - """Reader class for FCI L2 products in NetCDF4 format.""" - - def __init__(self, filename, filename_info, filetype_info): - """Open the NetCDF file with xarray and prepare for dataset reading.""" - super().__init__(filename, filename_info, filetype_info) - - # Use xarray's default netcdf4 engine to open the file - self.nc = xr.open_dataset( - self.filename, - decode_cf=True, - mask_and_scale=True, - chunks={ - 'number_of_columns': CHUNK_SIZE, - 'number_of_rows': CHUNK_SIZE - } - ) - - # Read metadata which are common to all datasets - self.nlines = self.nc['y'].size - self.ncols = self.nc['x'].size - self._projection = self.nc['mtg_geos_projection'] - - # Compute the area definition - self._area_def = self._compute_area_def() +class FciL2CommonFunctions(object): @property - def start_time(self): - """Get observation start time.""" + def _start_time(self): try: start_time = datetime.strptime(self.nc.attrs['time_coverage_start'], '%Y%m%d%H%M%S') except (ValueError, KeyError): @@ -71,17 +48,17 @@ def start_time(self): return start_time @property - def end_time(self): + def _end_time(self): """Get observation end time.""" try: end_time = datetime.strptime(self.nc.attrs['time_coverage_end'], '%Y%m%d%H%M%S') except (ValueError, KeyError): # TODO if the sensing_end_time_utc attribute is not valid, adds 20 minutes to the start time - end_time = self.start_time + timedelta(minutes=PRODUCT_DATA_DURATION_MINUTES) + end_time = self._start_time + timedelta(minutes=PRODUCT_DATA_DURATION_MINUTES) return end_time @property - def spacecraft_name(self): + def _spacecraft_name(self): """Return spacecraft name.""" try: return self.nc.attrs['platform'] @@ -91,7 +68,7 @@ def spacecraft_name(self): return 'DEFAULT_MTG' @property - def sensor(self): + def _sensor_name(self): """Return instrument.""" try: return self.nc.attrs['data_source'] @@ -100,15 +77,73 @@ def sensor(self): logger.warning("Sensor cannot be obtained from file content, using default value instead") return 'fci' + def _get_global_attributes(self): + """Create a dictionary of global attributes to be added to all datasets. + + Returns: + dict: A dictionary of global attributes. + filename: name of the product file + start_time: sensing start time from best available source + end_time: sensing end time from best available source + spacecraft_name: name of the spacecraft + ssp_lon: longitude of subsatellite point + sensor: name of sensor + creation_time: creation time of the product + platform_name: name of the platform + + """ + attributes = { + 'filename': self.filename, + 'start_time': self._start_time, + 'end_time': self._end_time, + 'spacecraft_name': self._spacecraft_name, + 'ssp_lon': self.ssp_lon, + 'sensor': self._sensor_name, + 'creation_time': self.filename_info['creation_time'], + 'platform_name': self._spacecraft_name, + } + return attributes + + def __del__(self): + """Close the NetCDF file that may still be open.""" + with suppress(OSError): + self.nc.close() + + +class FciL2NCFileHandler(BaseFileHandler, FciL2CommonFunctions): + """Reader class for FCI L2 products in NetCDF4 format.""" + + def __init__(self, filename, filename_info, filetype_info): + """Open the NetCDF file with xarray and prepare for dataset reading.""" + super().__init__(filename, filename_info, filetype_info) + + # Use xarray's default netcdf4 engine to open the file + self.nc = xr.open_dataset( + self.filename, + decode_cf=True, + mask_and_scale=True, + chunks={ + 'number_of_columns': CHUNK_SIZE, + 'number_of_rows': CHUNK_SIZE + } + ) + + # Read metadata which are common to all datasets + self.nlines = self.nc['y'].size + self.ncols = self.nc['x'].size + self._projection = self.nc['mtg_geos_projection'] + + # Compute the area definition + self._area_def = self._compute_area_def() + @property def ssp_lon(self): """Return subsatellite point longitude.""" try: return float(self._projection.attrs['longitude_of_projection_origin']) except KeyError: - # TODO if the longitude_of_projection_origin attribute is not valid, return a default value logger.warning("ssp_lon cannot be obtained from file content, using default value instead") - return 0. + return SSP_DEFAULT def get_dataset(self, dataset_id, dataset_info): """Get dataset using the file_key in dataset_info.""" @@ -133,9 +168,14 @@ def get_dataset(self, dataset_id, dataset_info): fill_value = dataset_info['fill_value'] except KeyError: fill_value = np.NaN - float_variable = variable.where(variable != fill_value, mask_value).astype('float32', copy=False) - float_variable.attrs = variable.attrs - variable = float_variable + + if dataset_info['file_type'] == 'nc_fci_test_clm': + data_values = variable.where(variable != fill_value, mask_value).astype('uint32', copy=False) + else: + data_values = variable.where(variable != fill_value, mask_value).astype('float32', copy=False) + + data_values.attrs = variable.attrs + variable = data_values # If the variable has 3 dimensions, select the required layer if variable.ndim == 3: @@ -143,8 +183,11 @@ def get_dataset(self, dataset_id, dataset_info): logger.debug('Selecting the layer %d.', layer) variable = variable.sel(maximum_number_of_layers=layer) + if dataset_info['file_type'] == 'nc_fci_test_clm' and var_key != 'cloud_mask_cmrt6_test_result': + variable.values = (variable.values >> dataset_info['extract_byte'] << 31 >> 31) + # Rename the dimensions as required by Satpy - variable = variable.rename({'number_of_rows': 'y', 'number_of_columns': 'x'}) + variable = variable.rename({"number_of_rows": 'y', "number_of_columns": 'x'}) # Manage the attributes of the dataset variable.attrs.setdefault('units', None) @@ -154,33 +197,6 @@ def get_dataset(self, dataset_id, dataset_info): return variable - def _get_global_attributes(self): - """Create a dictionary of global attributes to be added to all datasets. - - Returns: - dict: A dictionary of global attributes. - filename: name of the product file - start_time: sensing start time from best available source - end_time: sensing end time from best available source - spacecraft_name: name of the spacecraft - ssp_lon: longitude of subsatellite point - sensor: name of sensor - creation_time: creation time of the product - platform_name: name of the platform - - """ - attributes = { - 'filename': self.filename, - 'start_time': self.start_time, - 'end_time': self.end_time, - 'spacecraft_name': self.spacecraft_name, - 'ssp_lon': self.ssp_lon, - 'sensor': self.sensor, - 'creation_time': self.filename_info['creation_time'], - 'platform_name': self.spacecraft_name, - } - return attributes - def get_area_def(self, key): """Return the area definition (common to all data in product).""" return self._area_def @@ -238,9 +254,59 @@ def _compute_area_def(self): return area_def - def __del__(self): - """Close the NetCDF file that may still be open.""" + +class FciL2NCSegmentFileHandler(BaseFileHandler, FciL2CommonFunctions): + """Reader class for FCI L2 Segmented products in NetCDF4 format.""" + + def __init__(self, filename, filename_info, filetype_info): + """Open the NetCDF file with xarray and prepare for dataset reading.""" + super().__init__(filename, filename_info, filetype_info) + # Use xarray's default netcdf4 engine to open the file + self.nc = xr.open_dataset( + self.filename, + decode_cf=True, + mask_and_scale=True, + chunks={ + 'number_of_FoR_cols': CHUNK_SIZE, + 'number_of_FoR_rows': CHUNK_SIZE + } + ) + + # Read metadata which are common to all datasets + self.nlines = self.nc['number_of_FoR_rows'].size + self.ncols = self.nc['number_of_FoR_cols'].size + + self.ssp_lon = SSP_DEFAULT + + def get_dataset(self, dataset_id, dataset_info): + """Get dataset using the file_key in dataset_info.""" + var_key = dataset_info['file_key'] + logger.debug('Reading in file to get dataset with key %s.', var_key) + try: - self.nc.close() - except AttributeError: - pass + variable = self.nc[var_key] + except KeyError: + logger.warning("Could not find key %s in NetCDF file, no valid Dataset created", var_key) + return None + + # TODO in some of the test files, invalid pixels contain the value defined as "fill_value" in the YAML file + # instead of being masked directly in the netCDF variable. + # therefore NaN is applied where such value is found or (0 if the array contains integer values) + # the next 11 lines have to be removed once the product files are correctly configured + + mask_value = dataset_info.get('mask_value', np.NaN) + fill_value = dataset_info.get('fill_value', np.NaN) + + float_variable = variable.where(variable != fill_value, mask_value).astype('float32', copy=False) + float_variable.attrs = variable.attrs + variable = float_variable + + # Rename the dimensions as required by Satpy + variable = variable.rename({"number_of_FoR_rows": 'y', "number_of_FoR_cols": 'x'}) +# # Manage the attributes of the dataset + variable.attrs.setdefault('units', None) + + variable.attrs.update(dataset_info) + variable.attrs.update(self._get_global_attributes()) + + return variable diff --git a/satpy/tests/reader_tests/test_fci_l2_nc.py b/satpy/tests/reader_tests/test_fci_l2_nc.py index ef9660a72d..c61139b8ef 100644 --- a/satpy/tests/reader_tests/test_fci_l2_nc.py +++ b/satpy/tests/reader_tests/test_fci_l2_nc.py @@ -18,22 +18,21 @@ """The fci_cld_l2_nc reader tests package.""" -import os -import numpy as np import datetime -from netCDF4 import Dataset - -from satpy.readers.fci_l2_nc import FciL2NCFileHandler, PRODUCT_DATA_DURATION_MINUTES - +import os import unittest +from contextlib import suppress +from unittest import mock -try: - from unittest import mock -except ImportError: - import mock +import numpy as np +from netCDF4 import Dataset +from satpy.readers.fci_l2_nc import FciL2NCFileHandler, FciL2NCSegmentFileHandler, PRODUCT_DATA_DURATION_MINUTES TEST_FILE = 'test_file_fci_l2_nc.nc' +SEG_TEST_FILE = 'test_seg_file_fci_l2_nc.nc' +TEST_ERROR_FILE = 'test_error_file_fci_l2_nc.nc' +TEST_BYTE_FILE = 'test_byte_file_fci_l2_nc.nc' class TestFciL2NCFileHandler(unittest.TestCase): @@ -86,35 +85,33 @@ def setUp(self): filename=TEST_FILE, filename_info={ 'creation_time': datetime.datetime(year=2017, month=9, day=20, - hour=12, minute=30, second=30) + hour=12, minute=30, second=30), }, filetype_info={} ) def tearDown(self): """Remove the previously created test file.""" - # First delets the reader, forcing the file to be closed if still open + # First delete the reader, forcing the file to be closed if still open del self.reader # Then can safely remove it from the system - try: + with suppress(OSError): os.remove(TEST_FILE) - except OSError: - pass def test_all_basic(self): """Test all basic functionalities.""" self.assertEqual(PRODUCT_DATA_DURATION_MINUTES, 20) - self.assertEqual(self.reader.start_time, + self.assertEqual(self.reader._start_time, datetime.datetime(year=2017, month=9, day=20, hour=17, minute=30, second=40)) - self.assertEqual(self.reader.end_time, + self.assertEqual(self.reader._end_time, datetime.datetime(year=2017, month=9, day=20, hour=17, minute=41, second=17)) - self.assertEqual(self.reader.spacecraft_name, 'test_platform') - self.assertEqual(self.reader.sensor, 'test_data_source') + self.assertEqual(self.reader._spacecraft_name, 'test_platform') + self.assertEqual(self.reader._sensor_name, 'test_data_source') self.assertEqual(self.reader.ssp_lon, 10.0) global_attributes = self.reader._get_global_attributes() @@ -172,7 +169,9 @@ def test_dataset(self): # Checks the correct execution of the get_dataset function with a valid file_key dataset = self.reader.get_dataset(None, {'file_key': 'test_one_layer', - 'fill_value': -999, 'mask_value': 0}) + 'fill_value': -999, 'mask_value': 0., + 'file_type': 'test_file_type'}) + self.assertTrue(np.allclose(dataset.values, np.ones((100, 10)))) self.assertEqual(dataset.attrs['test_attr'], 'attr') self.assertEqual(dataset.attrs['units'], 'test_units') @@ -181,7 +180,8 @@ def test_dataset(self): # Checks the correct execution of the get_dataset function with a valid file_key & layer dataset = self.reader.get_dataset(None, {'file_key': 'test_two_layers', 'layer': 1, - 'fill_value': -999, 'mask_value': 0}) + 'fill_value': -999, 'mask_value': 0, + 'file_type': 'test_file_type'}) self.assertTrue(np.allclose(dataset.values, 2 * np.ones((100, 10)))) self.assertEqual(dataset.attrs['units'], None) self.assertEqual(dataset.attrs['spacecraft_name'], 'test_platform') @@ -189,6 +189,267 @@ def test_dataset(self): # Checks the correct execution of the get_dataset function with an invalid file_key invalid_dataset = self.reader.get_dataset(None, {'file_key': 'test_invalid', - 'fill_value': -999, 'mask_value': 0}) + 'fill_value': -999, 'mask_value': 0, + 'file_type': 'test_file_type'}) + # Checks that the function returns None + self.assertEqual(invalid_dataset, None) + + +class TestFciL2NCSegmentFileHandler(unittest.TestCase): + """Test the FciL2NCSegmentFileHandler reader.""" + + def setUp(self): + """Set up the test by creating a test file and opening it with the reader.""" + # Easiest way to test the reader is to create a test netCDF file on the fly + with Dataset(SEG_TEST_FILE, 'w') as nc: + # Create dimensions + nc.createDimension('number_of_FoR_cols', 10) + nc.createDimension('number_of_FoR_rows', 100) + nc.createDimension('number_of_channels', 8) + nc.createDimension('number_of_categories', 6) + + # add global attributes + nc.data_source = 'test_fci_data_source' + nc.platform = 'test_fci_platform' + nc.time_coverage_start = '20170920173040' + nc.time_coverage_end = '20170920174117' + + # Add datasets + x = nc.createVariable('x', np.float32, dimensions=('number_of_FoR_cols',)) + x.standard_name = 'projection_x_coordinate' + x[:] = np.arange(10) + + y = nc.createVariable('y', np.float32, dimensions=('number_of_FoR_rows',)) + x.standard_name = 'projection_y_coordinate' + y[:] = np.arange(100) + + chans = nc.createVariable('channels', np.float32, dimensions=('number_of_channels',)) + chans.standard_name = 'fci_channels' + chans[:] = np.arange(8) + + cats = nc.createVariable('categories', np.float32, dimensions=('number_of_categories',)) + cats.standard_name = 'product_categories' + cats[:] = np.arange(6) + + test_dataset = nc.createVariable('test_values', np.float32, + dimensions=('number_of_FoR_rows', 'number_of_FoR_cols', + 'number_of_channels', 'number_of_categories')) + test_dataset[:] = np.ones((100, 10, 8, 6)) + test_dataset.test_attr = 'attr' + test_dataset.units = 'test_units' + + self.segment_reader = FciL2NCSegmentFileHandler( + filename=SEG_TEST_FILE, + filename_info={ + 'creation_time': datetime.datetime(year=2017, month=9, day=20, + hour=12, minute=30, second=30), + }, + filetype_info={} + ) + + def tearDown(self): + """Remove the previously created test file.""" + # First delete the reader, forcing the file to be closed if still open + del self.segment_reader + # Then can safely remove it from the system + with suppress(OSError): + os.remove(SEG_TEST_FILE) + + def test_all_basic(self): + """Test all basic functionalities.""" + self.assertEqual(PRODUCT_DATA_DURATION_MINUTES, 20) + + self.assertEqual(self.segment_reader._start_time, + datetime.datetime(year=2017, month=9, day=20, + hour=17, minute=30, second=40)) + + self.assertEqual(self.segment_reader._end_time, + datetime.datetime(year=2017, month=9, day=20, + hour=17, minute=41, second=17)) + + self.assertEqual(self.segment_reader._spacecraft_name, 'test_fci_platform') + self.assertEqual(self.segment_reader._sensor_name, 'test_fci_data_source') + self.assertEqual(self.segment_reader.ssp_lon, 0.0) + + global_attributes = self.segment_reader._get_global_attributes() + + expected_global_attributes = { + 'filename': SEG_TEST_FILE, + 'start_time': datetime.datetime(year=2017, month=9, day=20, + hour=17, minute=30, second=40), + 'end_time': datetime.datetime(year=2017, month=9, day=20, + hour=17, minute=41, second=17), + 'spacecraft_name': 'test_fci_platform', + 'ssp_lon': 0.0, + 'sensor': 'test_fci_data_source', + 'creation_time': datetime.datetime(year=2017, month=9, day=20, + hour=12, minute=30, second=30), + 'platform_name': 'test_fci_platform' + } + self.assertEqual(global_attributes, expected_global_attributes) + + def test_dataset(self): + """Test the execution of the get_dataset function.""" + # Checks the correct execution of the get_dataset function with a valid file_key + dataset = self.segment_reader.get_dataset(None, + {'file_key': 'test_values', + 'fill_value': -999, 'mask_value': 0, }) + self.assertTrue(np.allclose(dataset.values, np.ones((100, 10, 8, 6)))) + self.assertEqual(dataset.attrs['test_attr'], 'attr') + self.assertEqual(dataset.attrs['units'], 'test_units') + self.assertEqual(dataset.attrs['fill_value'], -999) + + # Checks the correct execution of the get_dataset function with an invalid file_key + invalid_dataset = self.segment_reader.get_dataset(None, + {'file_key': 'test_invalid', + 'fill_value': -999, 'mask_value': 0}) # Checks that the function returns None self.assertEqual(invalid_dataset, None) + + +class TestFciL2NCErrorFileHandler(unittest.TestCase): + """Test the FciL2NCFileHandler reader.""" + + def setUp(self): + """Set up the test by creating a test file and opening it with the reader.""" + # Easiest way to test the reader is to create a test netCDF file on the fly + + with Dataset(TEST_ERROR_FILE, 'w') as nc_err: + # Create dimensions + nc_err.createDimension('number_of_FoR_cols', 10) + nc_err.createDimension('number_of_FoR_rows', 100) + nc_err.createDimension('number_of_channels', 8) + nc_err.createDimension('number_of_categories', 6) + # add erroneous global attributes + nc_err.data_source = 'test_fci_data_source' # Error in key name + nc_err.platform_err = 'test_fci_platform' # Error in key name + nc_err.time_coverage_start = '2017092017304000' # Error in time format + nc_err.time_coverage_end_err = '20170920174117' # Error in key name + + # Add datasets + x = nc_err.createVariable('x', np.float32, dimensions=('number_of_FoR_cols',)) + x.standard_name = 'projection_x_coordinate' + x[:] = np.arange(10) + + y = nc_err.createVariable('y', np.float32, dimensions=('number_of_FoR_rows',)) + x.standard_name = 'projection_y_coordinate' + y[:] = np.arange(100) + + chans = nc_err.createVariable('channels', np.float32, dimensions=('number_of_channels',)) + chans.standard_name = 'fci_channels' + chans[:] = np.arange(8) + + cats = nc_err.createVariable('categories', np.float32, dimensions=('number_of_categories',)) + cats.standard_name = 'product_categories' + cats[:] = np.arange(6) + + test_dataset = nc_err.createVariable('test_values', np.float32, + dimensions=('number_of_FoR_rows', 'number_of_FoR_cols', + 'number_of_channels', 'number_of_categories')) + test_dataset[:] = np.ones((100, 10, 8, 6)) + test_dataset.test_attr = 'attr' + test_dataset.units = 'test_units' + + self.error_reader = FciL2NCSegmentFileHandler( + filename=TEST_ERROR_FILE, + filename_info={ + 'creation_time': datetime.datetime(year=2017, month=9, day=20, + hour=12, minute=30, second=30), + }, + filetype_info={} + ) + + def tearDown(self): + """Remove the previously created test file.""" + # First delete the reader, forcing the file to be closed if still open + del self.error_reader + # Then can safely remove it from the system + with suppress(OSError): + os.remove(TEST_ERROR_FILE) + + def test_errors(self): + self.assertRaises(TypeError, self.error_reader._start_time, + datetime.datetime(year=2017, month=9, day=20, + hour=17, minute=30, second=40)) + + self.assertRaises(TypeError, self.error_reader._end_time, + datetime.datetime(year=2017, month=9, day=20, + hour=17, minute=41, second=17)) + + self.assertRaises(TypeError, self.error_reader._spacecraft_name) + + self.assertRaises(TypeError, self.error_reader._sensor_name) + + +class TestFciL2NCReadingByteData(unittest.TestCase): + """Test the FciL2NCFileHandler when reading and extracting byte data.""" + + def setUp(self): + """Set up the test by creating a test file and opening it with the reader.""" + # Easiest way to test the reader is to create a test netCDF file on the fly + + with Dataset(TEST_BYTE_FILE, 'w') as nc_byte: + # Create dimensions + nc_byte.createDimension('number_of_columns', 1) + nc_byte.createDimension('number_of_rows', 1) + + # Add datasets + x = nc_byte.createVariable('x', np.float32, dimensions=('number_of_columns',)) + x.standard_name = 'projection_x_coordinate' + x[:] = np.arange(1) + + y = nc_byte.createVariable('y', np.float32, dimensions=('number_of_rows',)) + x.standard_name = 'projection_y_coordinate' + y[:] = np.arange(1) + + mtg_geos_projection = nc_byte.createVariable('mtg_geos_projection', np.int, dimensions=()) + mtg_geos_projection.longitude_of_projection_origin = 10.0 + mtg_geos_projection.semi_major_axis = 6378137. + mtg_geos_projection.semi_minor_axis = 6356752. + mtg_geos_projection.perspective_point_height = 35786400. + + test_dataset = nc_byte.createVariable('cloud_mask_test_flag', np.float32, + dimensions=('number_of_rows', 'number_of_columns',)) + + # This number was chosen as we know the expected byte values + test_dataset[:] = 4544767 + + self.byte_reader = FciL2NCFileHandler( + filename=TEST_BYTE_FILE, + filename_info={ + 'creation_time': datetime.datetime(year=2017, month=9, day=20, + hour=12, minute=30, second=30), + }, + filetype_info={} + ) + + def tearDown(self): + """Remove the previously created test file.""" + # First delete the reader, forcing the file to be closed if still open + del self.byte_reader + # Then can safely remove it from the system + with suppress(OSError): + os.remove(TEST_BYTE_FILE) + + def test_byte_extraction(self): + """Test the execution of the get_dataset function.""" + + # Value of 1 is expected to be returned for this test + dataset = self.byte_reader.get_dataset(None, + {'file_key': 'cloud_mask_test_flag', + 'fill_value': -999, 'mask_value': 0., + 'file_type': 'nc_fci_test_clm', + 'extract_byte': 1, + }) + + self.assertEqual(dataset.values, 1) + + # Value of 0 is expected fto be returned or this test + dataset = self.byte_reader.get_dataset(None, + {'file_key': 'cloud_mask_test_flag', + 'fill_value': -999, 'mask_value': 0., + 'file_type': 'nc_fci_test_clm', + 'extract_byte': 23, + }) + + self.assertEqual(dataset.values, 0)