diff --git a/satpy/etc/readers/goesr_netcdficare.yaml b/satpy/etc/readers/goesr_netcdficare.yaml
new file mode 100644
index 0000000000..4b36914b67
--- /dev/null
+++ b/satpy/etc/readers/goesr_netcdficare.yaml
@@ -0,0 +1,306 @@
+# References:
+# - GOES Level 1.5 Image Data Format Description
+# - Radiometric Calibration of GOESR ABI Level 1.5 Image Data in Equivalent
+# Spectral Blackbody Radiance
+# Netcdf built by Icare and Meteo France. Stored at Icare.
+
+reader:
+ name: goesr_netcdficare
+ short_name: goesr_NETCDF_ICARE
+ long_name: METEOFRANCE GEOSTATIONARY NETCDF BUILD for ICARE (Lille)
+ description: A reader for L1b NETCDF for all GEOSTATIONNARY retrieved from the ICARE service.
+ status: Defunct
+ supports_fsspec: false
+ sensors: [abi]
+ default_channels: [VIS_004, VIS_006, VIS_008, VIS_014, VIS_016, VIS_022, IR_039, IR_062, IR_069, IR_073, IR_085, IR_096, IR_103, IR_112, IR_123, IR_133]
+ reader: !!python/name:satpy.readers.yaml_reader.FileYAMLReader
+
+file_types:
+
+ GOES500m :
+ file_reader: !!python/name:satpy.readers.geos_netcdficare.NETCDF_ICARE
+ file_patterns: ['Emultic500mNC4_{platform_shortname:6s}_{start_time:%Y%m%d%H%M}.nc',
+ 'Wmultic500mNC4_{platform_shortname:6s}_{start_time:%Y%m%d%H%M}.nc',
+ 'Emultic500mNC4_{platform_shortname:6s}_{start_time:%Y%m%d_%H%M}.nc',
+ 'Wmultic500mNC4_{platform_shortname:6s}_{start_time:%Y%m%d_%H%M}.nc']
+
+ GOES1km :
+ file_reader: !!python/name:satpy.readers.geos_netcdficare.NETCDF_ICARE
+ file_patterns: ['Emultic1kmNC4_{platform_shortname:6s}_{start_time:%Y%m%d%H%M}.nc',
+ 'Wmultic1kmNC4_{platform_shortname:6s}_{start_time:%Y%m%d%H%M}.nc',
+ 'Emultic1kmNC4_{platform_shortname:6s}_{start_time:%Y%m%d_%H%M}.nc',
+ 'Wmultic1kmNC4_{platform_shortname:6s}_{start_time:%Y%m%d_%H%M}.nc']
+
+ GOES2km :
+ file_reader: !!python/name:satpy.readers.geos_netcdficare.NETCDF_ICARE
+ file_patterns: ['Emultic2kmNC4_{platform_shortname:6s}_{start_time:%Y%m%d%H%M}.nc',
+ 'Wmultic2kmNC4_{platform_shortname:6s}_{start_time:%Y%m%d%H%M}.nc',
+ 'Emultic2kmNC4_{platform_shortname:6s}_{start_time:%Y%m%d_%H%M}.nc',
+ 'Wmultic2kmNC4_{platform_shortname:6s}_{start_time:%Y%m%d_%H%M}.nc']
+
+
+datasets:
+
+ VIS_004 :
+ name: C01
+ wavelength: [0.450, 0.470, 0.490]
+ resolution:
+ 1000: { file_type: GOES1km }
+ 2000: { file_type: GOES2km }
+ calibration:
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavelength
+ units: W m-2 um-1 sr-1
+ reflectance:
+ standard_name: toa_bidirectional_reflectance
+ units: "%"
+ counts:
+ standard_name: counts
+ units: "1"
+ file_type: [GOES2km, GOES1km]
+
+ VIS_006 :
+ name: C02
+ wavelength: [0.590, 0.640, 0.690]
+ resolution:
+ 500: { file_type: GOES500m }
+ 2000: { file_type: GOES2km }
+ calibration:
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavelength
+ units: W m-2 um-1 sr-1
+ reflectance:
+ standard_name: toa_bidirectional_reflectance
+ units: "%"
+ counts:
+ standard_name: counts
+ units: "1"
+ file_type: [GOES2km, GOES500m]
+
+ VIS_008 :
+ name: C03
+ wavelength: [0.8455, 0.865, 0.8845]
+ resolution:
+ 1000: { file_type: GOES1km }
+ 2000: { file_type: GOES2km }
+ calibration:
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavelength
+ units: W m-2 um-1 sr-1
+ reflectance:
+ standard_name: toa_bidirectional_reflectance
+ units: "%"
+ counts:
+ standard_name: counts
+ units: "1"
+ file_type: [GOES2km, GOES1km]
+
+ VIS_014 :
+ name: C04
+ wavelength: [1.3705, 1.378, 1.3855]
+ resolution: 2000
+ calibration:
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavelength
+ units: W m-2 um-1 sr-1
+ reflectance:
+ standard_name: toa_bidirectional_reflectance
+ units: "%"
+ counts:
+ standard_name: counts
+ units: "1"
+ file_type: GOES2km
+
+ VIS_016 :
+ name: C05
+ wavelength: [1.580, 1.610, 1.640]
+ resolution:
+ 1000: { file_type: GOES1km }
+ 2000: { file_type: GOES2km }
+ calibration:
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavelength
+ units: W m-2 um-1 sr-1
+ reflectance:
+ standard_name: toa_bidirectional_reflectance
+ units: "%"
+ counts:
+ standard_name: counts
+ units: "1"
+ file_type: [GOES2km, GOES1km]
+
+ VIS_022 :
+ name: C06
+ wavelength: [2.225, 2.250, 2.275]
+ resolution: 2000
+ calibration:
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavelength
+ units: W m-2 um-1 sr-1
+ reflectance:
+ standard_name: toa_bidirectional_reflectance
+ units: "%"
+ counts:
+ standard_name: counts
+ units: "1"
+ file_type: GOES2km
+
+ IR_039 :
+ name: C07
+ wavelength: [3.80, 3.90, 4.00]
+ resolution: 2000
+ calibration:
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavenumber
+ units: mW m-2 sr-1 (cm-1)-1
+ brightness_temperature:
+ standard_name: toa_brightness_temperature
+ units: K
+ counts:
+ standard_name: counts
+ units: "1"
+ file_type: GOES2km
+
+ IR_062 :
+ name: C08
+ wavelength: [5.770, 6.185, 6.600]
+ resolution: 2000
+ calibration:
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavenumber
+ units: mW m-2 sr-1 (cm-1)-1
+ brightness_temperature:
+ standard_name: toa_brightness_temperature
+ units: K
+ counts:
+ standard_name: counts
+ units: "1"
+ file_type: GOES2km
+
+ IR_069 :
+ name: C09
+ wavelength: [6.75, 6.95, 7.15]
+ resolution: 2000
+ calibration:
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavenumber
+ units: mW m-2 sr-1 (cm-1)-1
+ brightness_temperature:
+ standard_name: toa_brightness_temperature
+ units: K
+ counts:
+ standard_name: counts
+ units: "1"
+ file_type: GOES2km
+
+ IR_073 :
+ name: C10
+ wavelength: [7.24, 7.34, 7.44]
+ resolution: 2000
+ calibration:
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavenumber
+ units: mW m-2 sr-1 (cm-1)-1
+ brightness_temperature:
+ standard_name: toa_brightness_temperature
+ units: K
+ counts:
+ standard_name: counts
+ units: "1"
+ file_type: GOES2km
+
+ IR_085 :
+ name: C11
+ wavelength: [8.30, 8.50, 8.70]
+ resolution: 2000
+ calibration:
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavenumber
+ units: mW m-2 sr-1 (cm-1)-1
+ brightness_temperature:
+ standard_name: toa_brightness_temperature
+ units: K
+ counts:
+ standard_name: counts
+ units: "1"
+ file_type: GOES2km
+
+ IR_096 :
+ name: C12
+ wavelength: [9.42, 9.61, 9.80]
+ resolution: 2000
+ calibration:
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavenumber
+ units: mW m-2 sr-1 (cm-1)-1
+ brightness_temperature:
+ standard_name: toa_brightness_temperature
+ units: K
+ counts:
+ standard_name: counts
+ units: "1"
+ file_type: GOES2km
+
+ IR_103 :
+ name: C13
+ wavelength: [10.10, 10.35, 10.60]
+ resolution: 2000
+ calibration:
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavenumber
+ units: mW m-2 sr-1 (cm-1)-1
+ brightness_temperature:
+ standard_name: toa_brightness_temperature
+ units: K
+ counts:
+ standard_name: counts
+ units: "1"
+ file_type: GOES2km
+
+ IR_112 :
+ name: C14
+ wavelength: [10.80, 11.20, 11.60]
+ resolution: 2000
+ calibration:
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavenumber
+ units: mW m-2 sr-1 (cm-1)-1
+ brightness_temperature:
+ standard_name: toa_brightness_temperature
+ units: K
+ counts:
+ standard_name: counts
+ units: "1"
+ file_type: GOES2km
+
+ IR_123 :
+ name: C15
+ wavelength: [11.80, 12.30, 12.80]
+ resolution: 2000
+ calibration:
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavenumber
+ units: mW m-2 sr-1 (cm-1)-1
+ brightness_temperature:
+ standard_name: toa_brightness_temperature
+ units: K
+ counts:
+ standard_name: counts
+ units: "1"
+ file_type: GOES2km
+
+ IR_133 :
+ name: C16
+ wavelength: [13.00, 13.30, 13.60]
+ resolution: 2000
+ calibration:
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavenumber
+ units: mW m-2 sr-1 (cm-1)-1
+ brightness_temperature:
+ standard_name: toa_brightness_temperature
+ units: K
+ counts:
+ standard_name: counts
+ units: "1"
+ file_type: GOES2km
diff --git a/satpy/etc/readers/hima_netcdficare.yaml b/satpy/etc/readers/hima_netcdficare.yaml
new file mode 100644
index 0000000000..bd3bd70f92
--- /dev/null
+++ b/satpy/etc/readers/hima_netcdficare.yaml
@@ -0,0 +1,260 @@
+# References:
+# - Himawari Level 1.5 Image Data Format Description
+# - Radiometric Calibration of Himawari Level 1.5 Image Data in Equivalent
+# Spectral Blackbody Radiance
+# Netcdf built by Icare and Meteo France. Stored at Icare.
+
+reader:
+ name: hima_netcdficare
+ short_name: hima_NETCDF_ICARE
+ long_name: METEOFRANCE GEOSTATIONARY NETCDF BUILD for ICARE (Lille)
+ description: A reader for L1b NETCDF for all GEOSTATIONNARY retrieved from the ICARE service.
+ status: Defunct
+ supports_fsspec: false
+ sensors: [ahi]
+ default_channels: [VIS004, VIS005, VIS006, VIS008, IR_016, IR_022, IR_038, WV_062, WV_069, WV_073, IR_085, IR_096, IR_104, IR_112, IR_123, IR_132]
+ reader: !!python/name:satpy.readers.yaml_reader.FileYAMLReader
+
+file_types:
+
+ HIMA500m :
+ file_reader: !!python/name:satpy.readers.geos_netcdficare.NETCDF_ICARE
+ file_patterns: ['Jmultic500mNC4_{platform_shortname:6s}_{start_time:%Y%m%d%H%M}.nc',
+ 'Jmultic500mNC4_{platform_shortname:6s}_{start_time:%Y%m%d_%H%M}.nc']
+
+ HIMA1km :
+ file_reader: !!python/name:satpy.readers.geos_netcdficare.NETCDF_ICARE
+ file_patterns: ['Jmultic1kmNC4_{platform_shortname:6s}_{start_time:%Y%m%d%H%M}.nc',
+ 'Jmultic1kmNC4_{platform_shortname:6s}_{start_time:%Y%m%d_%H%M}.nc']
+
+ HIMA2km :
+ file_reader: !!python/name:satpy.readers.geos_netcdficare.NETCDF_ICARE
+ file_patterns: ['Jmultic2kmNC4_{platform_shortname:6s}_{start_time:%Y%m%d%H%M}.nc',
+ 'Jmultic2kmNC4_{platform_shortname:6s}_{start_time:%Y%m%d_%H%M}.nc']
+
+datasets:
+
+ VIS004:
+ name: B01
+ sensor: ahi
+ wavelength: [0.45,0.47,0.49]
+ resolution: [2000]
+ calibration:
+ reflectance:
+ standard_name: toa_bidirectional_reflectance
+ units: "%"
+ counts:
+ standard_name: counts
+ units: 1
+ file_type: [HIMA2km]
+
+ VIS005:
+ name: B02
+ sensor: ahi
+ wavelength: [0.49,0.51,0.53]
+ resolution: [2000]
+ calibration:
+ reflectance:
+ standard_name: toa_bidirectional_reflectance
+ units: "%"
+ counts:
+ standard_name: counts
+ units: 1
+ file_type: [HIMA2km]
+
+ VIS006:
+ name: B03
+ sensor: ahi
+ wavelength: [0.62,0.64,0.66]
+ resolution: [500, 2000]
+ calibration:
+ reflectance:
+ standard_name: toa_bidirectional_reflectance
+ units: "%"
+ counts:
+ standard_name: counts
+ units: 1
+ file_type: [HIMA500m, HIMA2km]
+
+ VIS008 :
+ name: B04
+ sensor: ahi
+ wavelength: [0.83, 0.85, 0.87]
+ resolution: [1000, 2000]
+ calibration:
+ reflectance:
+ standard_name: toa_bidirectional_reflectance
+ units: "%"
+ counts:
+ standard_name: counts
+ units: 1
+ file_type: [HIMA1km, HIMA2km]
+
+ IR_016 :
+ name: B05
+ sensor: ahi
+ wavelength: [1.5, 1.6, 1.7]
+ resolution: 2000
+ calibration:
+ reflectance:
+ standard_name: toa_bidirectional_reflectance
+ units: "%"
+ counts:
+ standard_name: counts
+ units: 1
+ file_type: [HIMA2km]
+
+ IR_022 :
+ name: B06
+ sensor: ahi
+ wavelength: [2.2, 2.3, 2.4]
+ resolution: 2000
+ calibration:
+ reflectance:
+ standard_name: toa_bidirectional_reflectance
+ units: "%"
+ counts:
+ standard_name: counts
+ units: 1
+ file_type: [HIMA2km]
+
+ IR_038 :
+ name: B07
+ resolution: 2000
+ sensor: ahi
+ wavelength: [3.7, 3.9, 4.1]
+ calibration:
+ brightness_temperature:
+ standard_name: toa_brightness_temperature
+ units: "K"
+ counts:
+ standard_name: counts
+ units: 1
+ # FUTURE: Split this in to multiple resolutions so each can be loaded
+ file_type: [HIMA2km]
+
+ WV_062 :
+ name: B08
+ sensor: ahi
+ wavelength: [6.0, 6.2, 6.4]
+ resolution: 2000
+ calibration:
+ brightness_temperature:
+ standard_name: toa_brightness_temperature
+ units: "K"
+ counts:
+ standard_name: counts
+ units: 1
+ file_type: [HIMA2km]
+
+ WV_069 :
+ name: B09
+ sensor: ahi
+ wavelength: [6.7, 6.9, 7.1]
+ resolution: 2000
+ calibration:
+ brightness_temperature:
+ standard_name: toa_brightness_temperature
+ units: "K"
+ counts:
+ standard_name: counts
+ units: 1
+ file_type: [HIMA2km]
+
+ WV_073 :
+ name: B10
+ sensor: ahi
+ wavelength: [7.1, 7.3, 7.5]
+ resolution: 2000
+ calibration:
+ brightness_temperature:
+ standard_name: toa_brightness_temperature
+ units: "K"
+ counts:
+ standard_name: counts
+ units: 1
+ file_type: [HIMA2km]
+
+ IR_085 :
+ name: B11
+ sensor: ahi
+ wavelength: [8.4, 8.6, 8.8]
+ resolution: 2000
+ calibration:
+ brightness_temperature:
+ standard_name: toa_brightness_temperature
+ units: "K"
+ counts:
+ standard_name: counts
+ units: 1
+ file_type: [HIMA2km]
+
+ IR_096 :
+ name: B12
+ sensor: ahi
+ wavelength: [9.4, 9.6, 9.8]
+ resolution: 2000
+ calibration:
+ brightness_temperature:
+ standard_name: toa_brightness_temperature
+ units: "K"
+ counts:
+ standard_name: counts
+ units: 1
+ file_type: [HIMA2km]
+
+ IR_104 :
+ name: B13
+ sensor: ahi
+ wavelength: [10.2, 10.4, 10.6]
+ resolution: 2000
+ calibration:
+ brightness_temperature:
+ standard_name: toa_brightness_temperature
+ units: "K"
+ counts:
+ standard_name: counts
+ units: 1
+ file_type: [HIMA2km]
+
+ IR_112 :
+ name: B14
+ sensor: ahi
+ wavelength: [11.0, 11.2, 11.4]
+ resolution: 2000
+ calibration:
+ brightness_temperature:
+ standard_name: toa_brightness_temperature
+ units: "K"
+ counts:
+ standard_name: counts
+ units: 1
+ file_type: [HIMA2km]
+
+ IR_123 :
+ name: B15
+ sensor: ahi
+ wavelength: [12.2, 12.4, 12.6]
+ resolution: 2000
+ calibration:
+ brightness_temperature:
+ standard_name: toa_brightness_temperature
+ units: "K"
+ counts:
+ standard_name: counts
+ units: 1
+ file_type: [HIMA2km]
+
+ IR_132 :
+ name: B16
+ sensor: ahi
+ wavelength: [13.1, 13.3, 13.5]
+ resolution: 2000
+ calibration:
+ brightness_temperature:
+ standard_name: toa_brightness_temperature
+ units: "K"
+ counts:
+ standard_name: counts
+ units: 1
+ file_type: [HIMA2km]
diff --git a/satpy/etc/readers/msg_netcdficare.yaml b/satpy/etc/readers/msg_netcdficare.yaml
new file mode 100644
index 0000000000..8a64c7ed01
--- /dev/null
+++ b/satpy/etc/readers/msg_netcdficare.yaml
@@ -0,0 +1,174 @@
+# References:
+# - MSG Level 1.5 Image Data Format Description
+# - Radiometric Calibration of MSG SEVIRI Level 1.5 Image Data in Equivalent
+# Spectral Blackbody Radiance
+
+reader:
+ name: msg_netcdficare
+ short_name: MSG L1B NETCDF ICARE METEO-FRANCE
+ long_name: METEOFRANCE GEOSTATIONARY NETCDF BUILD for ICARE (Lille)
+ description: A reader for L1b NETCDF for all GEOSTATIONNARY retrieved from the ICARE service.
+ status: Defunct
+ supports_fsspec: false
+ sensors: [seviri]
+ default_channels: [HRV, IR_016, IR_039, IR_087, IR_097, IR_108, IR_120, IR_134, VIS006, VIS008, WV_062, WV_073]
+ reader: !!python/name:satpy.readers.yaml_reader.FileYAMLReader
+
+file_types:
+
+ MSG3km :
+ file_reader: !!python/name:satpy.readers.geos_netcdficare.NETCDF_ICARE
+ file_patterns: ['Mmultic3kmNC4_{platform_shortname:5s}_{start_time:%Y%m%d%H%M}.nc',
+ 'Imultic3kmNC4_{platform_shortname:5s}_{start_time:%Y%m%d%H%M}.nc',
+ 'Mrsmultic3kmNC4_{platform_shortname:5s}_{start_time:%Y%m%d%H%M}.nc',
+ 'Mmultic3kmNC4_{platform_shortname:5s}_{start_time:%Y%m%d_%H%M}.nc',
+ 'Imultic3kmNC4_{platform_shortname:5s}_{start_time:%Y%m%d_%H%M}.nc',
+ 'Mrsmultic3kmNC4_{platform_shortname:5s}_{start_time:%Y%m%d_%H%M}.nc']
+
+ # platform_shortname:5s : msg01, ..., msg04
+
+ MSG1km :
+ file_reader: !!python/name:satpy.readers.geos_netcdficare.NETCDF_ICARE
+ file_patterns: ['Mmultic1kmNC4_{platform_shortname:5s}_{start_time:%Y%m%d%H%M}.nc',
+ 'Imultic1kmNC4_{platform_shortname:5s}_{start_time:%Y%m%d%H%M}.nc',
+ 'Mrsmultic1kmNC4_{platform_shortname:5s}_{start_time:%Y%m%d%H%M}.nc',
+ 'Mmultic1kmNC4_{platform_shortname:5s}_{start_time:%Y%m%d_%H%M}.nc',
+ 'Imultic1kmNC4_{platform_shortname:5s}_{start_time:%Y%m%d_%H%M}.nc',
+ 'Mrsmultic1kmNC4_{platform_shortname:5s}_{start_time:%Y%m%d_%H%M}.nc']
+ # platform_shortname:5s : msg01, ..., msg04
+
+datasets:
+
+ HRV:
+ name: HRV
+ sensor: seviri
+ resolution: 1000.134348869
+ wavelength: [0.5, 0.7, 0.9]
+ calibration:
+ reflectance:
+ standard_name: toa_bidirectional_reflectance
+ units: "%"
+ file_type: MSG1km
+
+ IR_016:
+ name: IR_016
+ sensor: seviri
+ resolution: 3000.403165817
+ wavelength: [1.5, 1.64, 1.78]
+ calibration:
+ reflectance:
+ standard_name: toa_bidirectional_reflectance
+ units: "%"
+ file_type: MSG3km
+
+ IR_039:
+ name: IR_039
+ sensor: seviri
+ resolution: 3000.403165817
+ wavelength: [3.48, 3.92, 4.36]
+ calibration:
+ brightness_temperature:
+ standard_name: brightness_temperature
+ units: "K"
+ file_type: MSG3km
+
+ IR_087:
+ name: IR_087
+ sensor: seviri
+ resolution: 3000.403165817
+ wavelength: [8.3, 8.7, 9.1]
+ calibration:
+ brightness_temperature:
+ standard_name: brightness_temperature
+ units: "K"
+ file_type: MSG3km
+
+ IR_097:
+ name: IR_097
+ sensor: seviri
+ resolution: 3000.403165817
+ wavelength: [9.38, 9.66, 9.94]
+ calibration:
+ brightness_temperature:
+ standard_name: brightness_temperature
+ units: "K"
+ file_type: MSG3km
+
+ IR_108:
+ name: IR_108
+ sensor: seviri
+ resolution: 3000.403165817
+ wavelength: [9.8, 10.8, 11.8]
+ calibration:
+ brightness_temperature:
+ standard_name: brightness_temperature
+ units: "K"
+ file_type: MSG3km
+
+ IR_120:
+ name: IR_120
+ sensor: seviri
+ resolution: 3000.403165817
+ wavelength: [11.0, 12.0, 13.0]
+ calibration:
+ brightness_temperature:
+ standard_name: brightness_temperature
+ units: "K"
+ file_type: MSG3km
+
+ IR_134:
+ name: IR_134
+ sensor: seviri
+ resolution: 3000.403165817
+ wavelength: [12.4, 13.4, 14.4]
+ calibration:
+ brightness_temperature:
+ standard_name: brightness_temperature
+ units: "K"
+ file_type: MSG3km
+
+ VIS006:
+ name: VIS006
+ sensor: seviri
+ resolution: 3000.403165817
+ wavelength: [0.56, 0.635, 0.71]
+ calibration:
+ reflectance:
+ standard_name: toa_bidirectional_reflectance
+ units: "%"
+ file_type: MSG3km
+
+ VIS008:
+ name: VIS008
+ sensor: seviri
+ resolution: 3000.403165817
+ wavelength: [0.74, 0.81, 0.88]
+ calibration:
+ reflectance:
+ standard_name: toa_bidirectional_reflectance
+ units: "%"
+ file_type: MSG3km
+
+ WV_062:
+ name: WV_062
+ sensor: seviri
+ resolution: 3000.403165817
+ wavelength: [5.35, 6.25, 7.15]
+ calibration:
+ brightness_temperature:
+ standard_name: brightness_temperature
+ units: "K"
+ file_type: MSG3km
+
+ WV_073:
+ name: WV_073
+ sensor: seviri
+ resolution: 3000.403165817
+ wavelength: [6.85, 7.35, 7.85]
+ calibration:
+ brightness_temperature:
+ standard_name: brightness_temperature
+ units: "K"
+ file_type: MSG3km
+
+
diff --git a/satpy/etc/readers/mtg_netcdficare.yaml b/satpy/etc/readers/mtg_netcdficare.yaml
new file mode 100644
index 0000000000..9b8db1478c
--- /dev/null
+++ b/satpy/etc/readers/mtg_netcdficare.yaml
@@ -0,0 +1,333 @@
+# References:
+# - MTG Level 1.5 Image Data Format Description
+# - Radiometric Calibration of MSG SEVIRI Level 1.5 Image Data in Equivalent
+# Spectral Blackbody Radiance
+# Netcdf built by Icare and Meteo France. Stored at Icare.
+
+reader:
+ name: mtg_netcdficare
+ short_name: mtg_NETCDF_ICARE
+ long_name: METEOFRANCE GEOSTATIONARY NETCDF BUILD for ICARE (Lille)
+ description: A reader for L1b NETCDF for all GEOSTATIONNARY retrieved from the ICARE service.
+ status: Defunct
+ supports_fsspec: false
+ sensors: [fci]
+ default_channels: [VIS004, VIS005, VIS006, VIS008, VIS009, IR_013, IR_016, IR_022, IR_038, WV_063, WV_073, IR_087, IR_097, IR_105, IR_123, IR_133]
+ reader: !!python/name:satpy.readers.yaml_reader.FileYAMLReader
+
+file_types:
+
+ MTG500m :
+ file_reader: !!python/name:satpy.readers.geos_netcdficare.NETCDF_ICARE
+ file_patterns: ['Multic500m_{platform_shortname:5s}_{start_time:%Y%m%d%H%M}.nc',
+ 'Multic500m_{platform_shortname:5s}_{start_time:%Y%m%d_%H%M%S}.nc',
+ 'Mmultic500mNC4_{platform_shortname:5s}_{start_time:%Y%m%d%H%M}.nc',
+ 'Mmultic500mNC4_{platform_shortname:5s}_{start_time:%Y%m%d_%H%M%S}.nc']
+
+ MTG1km :
+ file_reader: !!python/name:satpy.readers.geos_netcdficare.NETCDF_ICARE
+ file_patterns: ['Multic1km_{platform_shortname:5s}_{start_time:%Y%m%d%H%M}.nc',
+ 'Multic1km_{platform_shortname:5s}_{start_time:%Y%m%d_%H%M%S}.nc',
+ 'Mmultic1kmNC4_{platform_shortname:5s}_{start_time:%Y%m%d%H%M}.nc',
+ 'Mmultic1kmNC4_{platform_shortname:5s}_{start_time:%Y%m%d_%H%M%S}.nc']
+
+ MTG2km :
+ file_reader: !!python/name:satpy.readers.geos_netcdficare.NETCDF_ICARE
+ file_patterns: ['Multic2km_{platform_shortname:5s}_{start_time:%Y%m%d%H%M}.nc',
+ 'Multic2km_{platform_shortname:5s}_{start_time:%Y%m%d_%H%M%S}.nc',
+ 'Mmultic2kmNC4_{platform_shortname:5s}_{start_time:%Y%m%d%H%M}.nc',
+ 'Mmultic2kmNC4_{platform_shortname:5s}_{start_time:%Y%m%d_%H%M%S}.nc']
+
+datasets:
+
+ VIS004:
+ name: vis_04
+ sensor: fci
+ wavelength: [ 0.384, 0.444, 0.504 ]
+ resolution:
+ 1000: { file_type: MTG1km }
+ 2000: { file_type: MTG2km }
+ calibration:
+ counts:
+ standard_name: counts
+ units: "count"
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavenumber
+ units: mW m-2 sr-1 (cm-1)-1
+ reflectance:
+ standard_name: toa_bidirectional_reflectance
+ units: "%"
+ file_type: [MTG2km, MTG1km]
+ # file_type: abi_ahi_seviri_fci_netcdficare
+ # file_type: [mtg2km__netcdficare, mtg1km__netcdficare]
+
+ VIS005:
+ name: vis_05
+ sensor: fci
+ wavelength: [0.470, 0.510, 0.550]
+ resolution:
+ 1000: { file_type: MTG1km }
+ 2000: { file_type: MTG2km }
+ calibration:
+ counts:
+ standard_name: counts
+ units: "count"
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavenumber
+ units: mW m-2 sr-1 (cm-1)-1
+ reflectance:
+ standard_name: toa_bidirectional_reflectance
+ units: "%"
+ file_type: [MTG2km, MTG1km]
+
+ VIS006:
+ name: vis_06
+ sensor: fci
+ wavelength: [0.590, 0.640, 0.690]
+ resolution:
+ 500: { file_type: MTG500m }
+ 1000: { file_type: MTG1km }
+ 2000: { file_type: MTG2km }
+ calibration:
+ counts:
+ standard_name: counts
+ units: "count"
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavenumber
+ units: mW m-2 sr-1 (cm-1)-1
+ reflectance:
+ standard_name: toa_bidirectional_reflectance
+ units: "%"
+ file_type: [MTG2km, MTG1km, MTG500m]
+
+ VIS008 :
+ name: vis_08
+ sensor: fci
+ wavelength: [0.815, 0.865, 0.915]
+ resolution:
+ 1000: { file_type: MTG1km }
+ 2000: { file_type: MTG2km }
+ calibration:
+ counts:
+ standard_name: counts
+ units: "count"
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavenumber
+ units: mW m-2 sr-1 (cm-1)-1
+ reflectance:
+ standard_name: toa_bidirectional_reflectance
+ units: "%"
+ file_type: [MTG2km, MTG1km]
+
+ VIS009 :
+ name: vis_09
+ sensor: fci
+ wavelength: [0.894, 0.914, 0.934]
+ resolution:
+ 1000: { file_type: MTG1km }
+ 2000: { file_type: MTG2km }
+ calibration:
+ counts:
+ standard_name: counts
+ units: "count"
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavenumber
+ units: mW m-2 sr-1 (cm-1)-1
+ reflectance:
+ standard_name: toa_bidirectional_reflectance
+ units: "%"
+
+ IR_013 :
+ name: nir_13
+ sensor: fci
+ wavelength: [1.350, 1.380, 1.410]
+ resolution:
+ 1000: { file_type: MTG1km }
+ 2000: { file_type: MTG2km }
+ calibration:
+ counts:
+ standard_name: counts
+ units: "count"
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavenumber
+ units: mW m-2 sr-1 (cm-1)-1
+ reflectance:
+ standard_name: toa_bidirectional_reflectance
+ units: "%"
+
+ IR_016 :
+ name: nir_16
+ sensor: fci
+ wavelength: [1.560, 1.610, 1.660]
+ resolution:
+ 1000: { file_type: MTG1km }
+ 2000: { file_type: MTG2km }
+ calibration:
+ counts:
+ standard_name: counts
+ units: "count"
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavenumber
+ units: mW m-2 sr-1 (cm-1)-1
+ reflectance:
+ standard_name: toa_bidirectional_reflectance
+ units: "%"
+ file_type: [MTG2km, MTG1km]
+
+ IR_022 :
+ name: nir_22
+ sensor: fci
+ wavelength: [2.200, 2.250, 2.300]
+ resolution:
+ 500: { file_type: MTG500m }
+ 1000: { file_type: MTG1km }
+ 2000: { file_type: MTG2km }
+ calibration:
+ counts:
+ standard_name: counts
+ units: "count"
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavenumber
+ units: mW m-2 sr-1 (cm-1)-1
+ reflectance:
+ standard_name: toa_bidirectional_reflectance
+ units: "%"
+
+ IR_038 :
+ name: ir_38
+ sensor: fci
+ wavelength: [3.400, 3.800, 4.200]
+ resolution:
+ 1000: { file_type: MTG1km }
+ 2000: { file_type: MTG2km }
+ calibration:
+ counts:
+ standard_name: counts
+ units: "count"
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavenumber
+ units: mW m-2 sr-1 (cm-1)-1
+ brightness_temperature:
+ standard_name: toa_brightness_temperature
+ units: "K"
+ file_type: [MTG2km, MTG1km]
+
+ WV_063 :
+ name: wv_63
+ sensor: fci
+ wavelength: [5.300, 6.300, 7.300]
+ resolution:
+ 2000: { file_type: MTG2km }
+ calibration:
+ counts:
+ standard_name: counts
+ units: "count"
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavenumber
+ units: mW m-2 sr-1 (cm-1)-1
+ brightness_temperature:
+ standard_name: toa_brightness_temperature
+ units: "K"
+
+ WV_073 :
+ name: wv_73
+ sensor: fci
+ wavelength: [6.850, 7.350, 7.850]
+ resolution:
+ 2000: { file_type: MTG2km }
+ calibration:
+ counts:
+ standard_name: counts
+ units: "count"
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavenumber
+ units: mW m-2 sr-1 (cm-1)-1
+ brightness_temperature:
+ standard_name: toa_brightness_temperature
+ units: "K"
+
+ IR_087 :
+ name: ir_87
+ sensor: fci
+ wavelength: [8.300, 8.700, 9.100]
+ resolution:
+ 2000: { file_type: MTG2km }
+ calibration:
+ counts:
+ standard_name: counts
+ units: "count"
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavenumber
+ units: mW m-2 sr-1 (cm-1)-1
+ brightness_temperature:
+ standard_name: toa_brightness_temperature
+ units: "K"
+
+ IR_097 :
+ name: ir_97
+ sensor: fci
+ wavelength: [9.360, 9.660, 9.960]
+ resolution:
+ 2000: { file_type: MTG2km }
+ calibration:
+ counts:
+ standard_name: counts
+ units: "count"
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavenumber
+ units: mW m-2 sr-1 (cm-1)-1
+ brightness_temperature:
+ standard_name: toa_brightness_temperature
+ units: "K"
+
+ IR_105 :
+ name: ir_105
+ sensor: fci
+ wavelength: [9.800, 10.500, 11.200]
+ resolution:
+ 1000: { file_type: MTG1km }
+ 2000: { file_type: MTG2km }
+ calibration:
+ counts:
+ standard_name: counts
+ units: "count"
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavenumber
+ units: mW m-2 sr-1 (cm-1)-1
+ brightness_temperature:
+ standard_name: toa_brightness_temperature
+ units: "K"
+
+ IR_123 :
+ name: ir_123
+ sensor: fci
+ wavelength: [11.800, 12.300, 12.800]
+ resolution:
+ 2000: { file_type: MTG2km }
+ calibration:
+ counts:
+ standard_name: counts
+ units: "count"
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavenumber
+ units: mW m-2 sr-1 (cm-1)-1
+ brightness_temperature:
+ standard_name: toa_brightness_temperature
+ units: "K"
+
+ IR_133 :
+ name: ir_133
+ sensor: fci
+ wavelength: [12.700, 13.300, 13.900]
+ resolution:
+ 2000: { file_type: MTG2km }
+ calibration:
+ counts:
+ standard_name: counts
+ units: "count"
+ radiance:
+ standard_name: toa_outgoing_radiance_per_unit_wavenumber
+ units: mW m-2 sr-1 (cm-1)-1
+ brightness_temperature:
+ standard_name: toa_brightness_temperature
+ units: "K"
diff --git a/satpy/readers/geos_netcdficare.py b/satpy/readers/geos_netcdficare.py
new file mode 100644
index 0000000000..39fbef1030
--- /dev/null
+++ b/satpy/readers/geos_netcdficare.py
@@ -0,0 +1,765 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019 Satpy developers
+#
+# This file is part of satpy. Written by Meteo France in august 2024.
+#
+# 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 .
+
+"""Interface to GEOSTATIONNARY L1B NETCDF data from ICARE (Lille).
+
+Introduction
+------------
+
+The ``geos_netcdficare`` reader reads some geostationnary netcdf build by
+Meteo France and stored at Icare.
+
+The brightness temperature and albedo are calibrated.
+
+That has been stored by the ICARE Data and Services Center
+Data can be accessed via: http://www.icare.univ-lille1.fr
+
+This reader concerns the following netcdfs :
+
+. msg with a longitude near 0° :
+Mmultic3kmNC4_msg03_20231113_111500.nc
+Mmultic1kmNC4_msg03_20231113_111500.nc
+
+. Msg rapid scan with a longitude near 9.5° :
+Mrsmultic3kmNC4_msg03_20231113_111500.nc
+Mrsmultic1kmNC4_msg03_20231113_111500.nc
+
+. Msg with a longitude near 42° :
+Imultic3kmNC4_msg03_20231113_111500.nc
+Imultic1kmNC4_msg03_20231113_111500.nc
+
+. Himawari :
+Jmultic2kmNC4_hima09_20231113_111500.nc
+Jmultic1kmNC4_hima09_20231113_111500.nc
+Jmultic500mNC4_hima09_20231113_111500.nc
+
+. Goesr near -137° :
+Wmultic2kmNC4_goes16_202406281000.nc.
+The better resolution are not built at Lannion, only at Tahiti.
+
+. Goesr in -75° :
+Emultic2kmNC4_goes16_202406281000.nc
+Emultic1kmNC4_goes16_202406281000.nc
+Emultic500mNC4_goes16_202406281000.nc
+
+. Mtg :
+Mmultic2km_mtgi1_20240104_090000.nc
+Mmultic1km_mtgi1_20240104_090000.nc
+Mmultic500m_mtgi1_20240104_090000.nc
+
+
+Example:
+--------
+Here is an example how to read the data in satpy:
+
+ from satpy import Scene
+ import glob
+
+ filenames = glob.glob('data/*2019-03-01T12-00-00*.hdf')
+ scn = Scene(filenames = filenames, reader = 'hima_netcdficare')
+ scn.load(['true_color']) # scn.load(['VIS006'])
+
+ my_area = AreaDefinition(
+ 'my_area', 'zone', 'my_area',
+ '+proj=latlong +lon_0=0 +a=6378169 +b=6356583 +h=35785831 +x_0=0
+ +y_0=0 +pm=0',
+ 8500, 4000,
+ [-180., -80., 180., 80],
+ nprocs=16)
+
+ natscn = scn.resample(my_area, resampler='nearest')
+ natscn.save_dataset(composite_name, filename = filename_image_out)
+
+EXECUTION TIME :
+ 50 seconds for a 2 kms goesr airmass rgb disk.
+DATE OF CREATION :
+ 2024 16th october.
+LAST VERSIONS :
+
+AUTHOR :
+ Meteo France.
+
+"""
+
+import datetime as dt
+
+import numpy as np
+
+from satpy.readers._geos_area import get_area_definition, get_area_extent
+
+import xarray as xr
+
+from satpy.readers.file_handlers import BaseFileHandler
+
+import logging
+logger = logging.getLogger('netcdficare')
+
+# Planck :
+C1 = 1.1910427e-5
+C2 = 1.4387752
+
+
+class NETCDF_ICARE(BaseFileHandler) :
+ # Cf readers/file_handlers.py.
+
+ def __init__(self, filename, filename_info, filetype_info) :
+ """Init the file handler."""
+
+ super().__init__(filename, filename_info, filetype_info)
+
+ self.nc = xr.open_dataset(
+ self.filename, decode_cf=True, mask_and_scale=False,
+ chunks={"xc": "auto", "yc": "auto"})
+ self.metadata = {}
+
+ self.metadata["start_time"] = self.get_endOrStartTime(
+ "time_coverage_start")
+ self.metadata["end_time"] = self.get_endOrStartTime(
+ "time_coverage_end")
+
+ # message = "Reading: " + filename
+ # message += " start: " + format(self.start_time)
+ # message += " end: " + format(self.end_time)
+ # logger.info(message)
+
+ self.netcdfCommonAttributReading()
+ # __init__()
+
+ def netcdfCommonAttributReading(self) :
+ self.sensor = self.sensor_name()
+ # seviri
+
+ self.platform = self.platform_name()
+ # Meteosat-10
+
+ self.res()
+ # Resolution : 3000.4 m
+
+ self.actualLongitude = self.satlon()
+ self.projectionLongitude = self.projlon()
+
+ self.zone = self.nc.attrs["Area_of_acquisition"]
+ # globe.
+
+ def sensor_name(self) :
+ """Get the sensor name seviri, fci, abi, ahi.
+ """
+ variable = self.nc["satellite"]
+ platform = variable.attrs["id"] # msg1, msg01, MSG1...
+ platform = platform[:3] # msg, MSG
+ platform = platform.lower() # msg
+
+ pdict = {}
+ pdict["msg"] = "seviri"
+ pdict["mtg"] = "fci"
+ pdict["goe"] = "abi"
+ pdict["him"] = "ahi"
+
+ if platform in pdict :
+ sensor = pdict[platform]
+ else :
+ message = "Unsupported satellite platform : "
+ message += self.platform
+ raise NotImplementedError(message)
+ return sensor
+
+ def platform_name(self) :
+ # Icare and météo france use non-standard platform names.
+ # Change is needed for pyspectral :
+ # pyspectral/rsr_seviri_Meteosat-10.h5 in the call
+ # Calculator(platform_name, sensor, name).
+
+ variable = self.nc["satellite"]
+ platform = variable.attrs["id"] # msg1, msg01, MSG1...
+
+ pdict = {}
+ pdict["msg1"] = "Meteosat-08"
+ pdict["msg01"] = "Meteosat-08"
+ pdict["MSG1"] = "Meteosat-08"
+ pdict["msg2"] = "Meteosat-09"
+ pdict["msg02"] = "Meteosat-09"
+ pdict["MSG2"] = "Meteosat-09"
+ pdict["msg3"] = "Meteosat-10"
+ pdict["msg03"] = "Meteosat-10"
+ pdict["MSG3"] = "Meteosat-10"
+ pdict["msg4"] = "Meteosat-11"
+ pdict["msg04"] = "Meteosat-11"
+ pdict["MSG4"] = "Meteosat-11"
+ pdict["mtgi1"] = "Meteosat-12"
+ pdict["mtg1"] = "Meteosat-12"
+ pdict["MTG01"] = "Meteosat-12"
+ pdict["goes16"] = "GOES-16"
+ pdict["goes17"] = "GOES-17"
+ pdict["goes18"] = "GOES-18"
+ pdict["goes19"] = "GOES-19"
+ pdict["hima08"] = "Himawari-8"
+ pdict["hima09"] = "Himawari-9"
+
+ if platform in pdict :
+ platform = pdict[platform]
+ else :
+ message = "Unsupported satellite platform : " + platform
+ raise NotImplementedError(message)
+ return platform
+ # platform_name()
+
+ def satlon(self) :
+ """Get the satellite longitude."""
+ variable = self.nc["satellite"]
+ actualLongitude = variable.attrs["lon"]
+ return actualLongitude
+
+ def projlon(self):
+ """Get the projection longitude."""
+ variable = self.nc["geos"]
+ projectionLongitude = variable.attrs["longitude_of_projection_origin"]
+ return projectionLongitude
+
+ @property
+ def projection(self):
+ """Get the projection."""
+ return "geos"
+
+ def res(self) :
+ """Get the resolution.
+ The resolution can be read in the attribute geotransform
+ of the following variables :
+ GeosCoordinateSystem500m, GeosCoordinateSystem_h,
+ GeosCoordinateSystem1km, GeosCoordinateSystem2km,
+ GeosCoordinateSystem.
+ cfac, lfac, coff, loff can be read in the variables ImageNavigationxxx.
+ """
+
+ if "GeosCoordinateSystem500m" in self.nc :
+ # Mtg, himawari, goesr.
+ variable = self.nc["GeosCoordinateSystem500m"]
+ Xvariable = self.nc["X500m"]
+ Yvariable = self.nc["Y500m"]
+ navigationString = "ImageNavigation500m"
+
+ elif "GeosCoordinateSystem_h" in self.nc :
+ # Hrv from msg.
+ variable = self.nc["GeosCoordinateSystem_h"]
+ Xvariable = self.nc["X_h"]
+ Yvariable = self.nc["Y_h"]
+ navigationString = "ImageNavigation_h"
+
+ elif "GeosCoordinateSystem1km" in self.nc :
+ # Mtg, himawari, goesr.
+ variable = self.nc["GeosCoordinateSystem1km"]
+ Xvariable = self.nc["X1km"]
+ Yvariable = self.nc["Y1km"]
+ navigationString = "ImageNavigation1km"
+
+ elif "GeosCoordinateSystem2km" in self.nc :
+ # Mtg, himawari, goesr.
+ variable = self.nc["GeosCoordinateSystem2km"]
+ Xvariable = self.nc["X2km"]
+ Yvariable = self.nc["Y2km"]
+ navigationString = "ImageNavigation2km"
+
+ elif "GeosCoordinateSystem" in self.nc :
+ # Msg in 3 kms.
+ variable = self.nc["GeosCoordinateSystem"]
+ Xvariable = self.nc["X"]
+ Yvariable = self.nc["Y"]
+ navigationString = "ImageNavigation"
+
+ else :
+ message = "Variables GeosCoordinateSystemXX not founded."
+ raise NotImplementedError(message)
+
+ geotransform = variable.attrs["GeoTransform"]
+ # geotransform = -5570254, 3000.40604, 0, 5570254, 0, -3000.40604
+
+ chunksGeotransform = geotransform.split(", ")
+ self.resolution = float(chunksGeotransform[1])
+ # 3000.40604
+
+ self.X = Xvariable[:]
+ self.nbpix = self.X.shape[0]
+ self.Y = Yvariable[:]
+ self.nblig = self.Y.shape[0]
+
+ variable = self.nc[navigationString]
+ self.cfac = float(variable.attrs["CFAC"])
+ self.lfac = float(variable.attrs["LFAC"])
+ self.coff = float(variable.attrs["COFF"])
+ self.loff = float(variable.attrs["LOFF"])
+ # res()
+
+ def get_endOrStartTime(self, AttributeName) :
+ """Get the end or the start time. Global attribute of the netcdf.
+ AttributName : "time_coverage_start", "time_coverage_end"
+ """
+ attr = self.nc.attrs[AttributeName]
+ # YYYY-MM-DDTHH:MM:SSZNNN or YYYY-MM-DDTHH:MM:SSZ
+ # In some versions milliseconds are present, sometimes not.
+ lengthString = len(attr)
+ if lengthString == 22 :
+ # Goesr : 2024-06-28T10:00:21.1Z
+ stacq = dt.datetime.strptime(attr, "%Y-%m-%dT%H:%M:%S.%fZ")
+ elif lengthString == 20 :
+ # Mtg.
+ stacq = dt.datetime.strptime(attr, "%Y-%m-%dT%H:%M:%SZ")
+ else :
+ # Msg, hima.
+ stacq = dt.datetime.strptime(attr, "%Y-%m-%dT%H:%M:%SZ%f")
+ return stacq
+ # get_endOrStartTime()
+
+ @property
+ def start_time(self) :
+ return(self.metadata["start_time"])
+
+ @property
+ def end_time(self) :
+ return(self.metadata["end_time"])
+
+ @property
+ def alt(self) :
+ """Get the altitude."""
+ variable = self.nc["satellite"]
+ altitude = variable.attrs["dst"] # 36000000 meters.
+ altitude += 6378169. # equatorial radius of the earth.
+ return altitude
+
+ def prepare_metadata(self, variable) :
+ """Get the metadata for a channel variable.
+ Add the global attributes."""
+ mda = {}
+
+ attributs = variable.attrs
+ for name in attributs :
+ mda.update({name: attributs.get(name)})
+
+ mda.update({
+ "start_time": self.start_time,
+ "end_time": self.end_time,
+ "platform_name": self.platform,
+ "sensor": self.sensor,
+ "zone": self.zone,
+ "projection_altitude": self.alt,
+ "cfac": self.cfac,
+ "lfac": self.lfac,
+ "coff": self.coff,
+ "loff": self.loff,
+ "resolution": self.resolution,
+ "satellite_actual_longitude": self.actualLongitude,
+ "projection_longitude": self.projectionLongitude,
+ "projection_type": self.projection
+ })
+
+ mda.update(self.orbital_param())
+ return mda
+ # prepare_metadata().
+
+ def buildChannelCorrespondanceName(self) :
+ pdict = {}
+
+ # For mtg.
+ # vis_04 is the name in satpy.
+ # VIS004 is the icare/meteofrance netcdf name.
+ pdict["vis_04"] = "VIS004"
+ pdict["vis_05"] = "VIS005"
+ pdict["vis_06"] = "VIS006"
+ pdict["vis_08"] = "VIS008"
+ pdict["vis_09"] = "VIS009"
+ pdict["nir_13"] = "IR_013"
+ pdict["nir_16"] = "IR_016"
+ pdict["nir_22"] = "IR_022"
+ pdict["ir_38"] = "IR_038"
+ pdict["wv_63"] = "WV_063"
+ pdict["wv_73"] = "WV_073"
+ pdict["ir_87"] = "IR_087"
+ pdict["ir_97"] = "IR_097"
+ pdict["ir_105"] = "IR_105"
+ pdict["ir_123"] = "IR_123"
+ pdict["ir_133"] = "IR_133"
+
+ # For msg, the satpy and icare channel names are the same.
+ pdict["VIS006"] = "VIS006"
+ pdict["VIS008"] = "VIS008"
+ pdict["HRV"] = "HRV"
+ pdict["IR_016"] = "IR_016"
+ pdict["IR_039"] = "IR_039"
+ pdict["WV_062"] = "WV_062"
+ pdict["WV_073"] = "WV_073"
+ pdict["IR_087"] = "IR_087"
+ pdict["IR_097"] = "IR_097"
+ pdict["IR_108"] = "IR_108"
+ pdict["IR_120"] = "IR_120"
+ pdict["IR_134"] = "IR_134"
+
+ # For the goesr satellites :
+ pdict["C01"] = "VIS_004"
+ pdict["C02"] = "VIS_006"
+ pdict["C03"] = "VIS_008"
+ pdict["C04"] = "VIS_014"
+ pdict["C05"] = "VIS_016"
+ pdict["C06"] = "VIS_022"
+ pdict["C07"] = "IR_039"
+ pdict["C08"] = "IR_062"
+ pdict["C09"] = "IR_069"
+ pdict["C10"] = "IR_073"
+ pdict["C11"] = "IR_085"
+ pdict["C12"] = "IR_096"
+ pdict["C13"] = "IR_103"
+ pdict["C14"] = "IR_114"
+ pdict["C15"] = "IR_123"
+ pdict["C16"] = "IR_133"
+
+ # For himawari.
+ # BO1 : name in satpy. VIS004 : name in icare/meteofrance netcdf.
+ pdict["B01"] = "VIS004"
+ pdict["B02"] = "VIS005"
+ pdict["B03"] = "VIS006"
+ pdict["B04"] = "VIS008"
+ pdict["B05"] = "IR_016"
+ pdict["B06"] = "IR_022"
+ pdict["B07"] = "IR_038"
+ pdict["B08"] = "WV_062"
+ pdict["B09"] = "WV_069"
+ pdict["B10"] = "WV_073"
+ pdict["B11"] = "IR_085"
+ pdict["B12"] = "IR_096"
+ pdict["B13"] = "IR_104"
+ pdict["B14"] = "IR_112"
+ pdict["B15"] = "IR_123"
+ pdict["B16"] = "IR_132"
+ return pdict
+ # buildChannelCorrespondanceName()
+
+ def _get_dsname(self, ds_id) :
+ """Return the correct dataset name based on requested band.
+ ds_id = DataID(name='vis_08',
+ wavelength=WavelengthRange(...),
+ resolution=2000, calibration=,
+ modifiers=())
+ """
+ pdict = self.buildChannelCorrespondanceName()
+
+ satpyName = ds_id["name"]
+ if satpyName in pdict :
+ icareName = pdict[satpyName]
+ else :
+ message = "Soft not adaptated for this channel : ds_id = "
+ message += satpyName
+ raise NotImplementedError(message)
+
+ return icareName
+ # _get_dsname()
+
+ def channelAttributs(self, ds_get_name) :
+ if ds_get_name not in self.nc :
+ message = "Channel " + ds_get_name + "not founded "
+ message += "in the netcdf."
+ raise NotImplementedError(message)
+
+ self.mda = {}
+ self.scale_factor = {}
+ self.offset = {}
+ self.alpha = {}
+ self.beta = {}
+ self.nuc = {}
+ self.bandfactor = {}
+ self.backtocountVariable = {}
+
+ variable = self.nc[ds_get_name]
+ attributs = variable.attrs
+
+ self.scale_factor[ds_get_name] = attributs["scale_factor"]
+ self.offset[ds_get_name] = attributs["add_offset"]
+
+ if "nuc" in attributs :
+ # Brightness temperature.
+ self.alpha[ds_get_name] = attributs["alpha"]
+ self.beta[ds_get_name] = attributs["beta"]
+ self.nuc[ds_get_name] = attributs["nuc"]
+
+ backtocountName = "Temp_to_Native_count_" + ds_get_name
+
+ elif "bandfactor" in attributs :
+ # Albedo.
+ self.bandfactor[ds_get_name] = attributs["bandfactor"]
+ backtocountName = "Albedo_to_Native_count_" + ds_get_name
+
+ else :
+ message = "Nuc or bandfactor not founded in the attributs"
+ message += " of " + ds_get_name
+ raise NotImplementedError(message)
+
+ self.backtocountVariable[ds_get_name] = self.nc[backtocountName]
+ # (65536). Correspondence from 0 to 65535 towards
+ # the original spatial agency counts.
+
+ self.mda[ds_get_name] = self.prepare_metadata(variable)
+ # channelAttributs(ds_get_name)
+
+ def comebacktoNativeData(self, ds_get_name) :
+ """ Come back to the original counts of the hrit.
+ ds_get_name : meteo france name of a channel : IR_108. """
+
+ variable = self.nc[ds_get_name]
+ # Variable is between -9000 to 4000 (temperature)
+ # or between 0 to 10000 (albedo).
+
+ offset = self.offset[ds_get_name] # 0 or 273.15
+ variable += 32768 # 0 to 65535
+
+ if offset == 0. :
+ # Albedo.
+ name = "Albedo_to_Native_count_" + ds_get_name
+ else :
+ name = "Temp_to_Native_count_" + ds_get_name
+ """ Temp_to_Native_count_IR_062 """
+
+ backtocountVariable = self.nc[name] # (65536).
+ # Correspondence from 0 to 65535
+ # towards the original spatial agency counts.
+
+ arrayTableConversion = xr.DataArray.to_numpy(backtocountVariable)
+
+ tableContents = arrayTableConversion[variable[:]]
+ """ Come back to the original counts of the hrit.
+ tableau : 0 to 4095 if native datas coded with 12 bits. """
+
+ variable[:] = tableContents
+ return(variable)
+ # comebacktoNativeData(self, ds_get_name)
+
+ def comebacktoRadiance(self, ds_get_name) :
+ """ Come back to the radiance.
+ ds_get_name : meteo france name of a channel : IR_108. """
+
+ variable = self.nc[ds_get_name]
+ # Variable is between -9000 to 4000 (temperature)
+ # or between 0 to 10000 (albedo).
+
+ scale_factor = self.scale_factor[ds_get_name] # 0.01
+ offset = self.offset[ds_get_name] # 0 or 273.15
+
+ if offset == 0. :
+ # Visible channel.
+ bandfactor = self.bandfactor[ds_get_name]
+
+ # Variable is an albedo from 0 to 10000.
+ variable = variable * scale_factor / 100. * bandfactor
+ # => variable is a reflectance between 0 and 1.
+ # radiance in mWm-2sr-1(cm-1)-1
+ else :
+ # Brightness temperature.
+ nuc = self.nuc[ds_get_name]
+ alpha = self.alpha[ds_get_name]
+ beta = self.beta[ds_get_name]
+
+ variable = variable * scale_factor + offset
+ # variable becomes Kelvin.
+
+ variable = variable * alpha + beta
+ resul1 = C1 * np.power(nuc, 3.)
+ resul2 = C2 * nuc
+ val2 = np.exp(resul2 / variable) - 1.
+ variable = resul1 / val2
+ # Radiance in mWm-2sr-1(cm-1)-1
+ return(variable)
+ # comebacktoRadiance(self, ds_get_name)
+
+ def get_dataset(self, ds_id, ds_info) :
+ """Get the dataset.
+ ds_id["calibration"] = key["calibration"] =
+ ["brightness_temperature", "reflectance", "radiance", "counts"]
+ """
+ ds_get_name = self._get_dsname(ds_id)
+ # ds_get_name is the meteo France Icare name of the channel : IR_096.
+
+ self.channelAttributs(ds_get_name)
+
+ mda = self.mda[ds_get_name]
+ mda.update(ds_info)
+
+ calibration = ds_id["calibration"]
+
+ if calibration == "counts" :
+ # Come back to the original counts of the hrit...
+ variable = self.comebacktoNativeData(ds_get_name)
+
+ elif calibration == "radiance" :
+ # Come back to the radiance.
+ variable = self.comebacktoRadiance(ds_get_name)
+
+ elif calibration == "brightness_temperature" :
+ variable = self.nc[ds_get_name]
+ # WV_062 calibration.brightness_temperature, from -9000 to 4000
+ scale_factor = self.scale_factor[ds_get_name]
+ offset = self.offset[ds_get_name]
+ if offset != 273.15 :
+ message = "Soft not intended for a reflectance "
+ message += "with a wave length more than 3 microns. "
+ message += ds_get_name + " offset = " + str(offset)
+ raise NotImplementedError(message)
+
+ variable = variable * scale_factor + offset
+ # variable becomes Kelvin.
+
+ elif calibration == "reflectance" :
+ variable = self.nc[ds_get_name]
+ # VIS006 calibration.reflectance, from 0 to 10000
+ scale_factor = self.scale_factor[ds_get_name]
+ offset = self.offset[ds_get_name]
+ if offset != 0. :
+ message = "Soft not intended "
+ message += "for a brightness temperature "
+ message += "with a wave length less than 3 microns. "
+ message += ds_get_name + " offset = " + str(offset)
+ raise NotImplementedError(message)
+
+ variable = variable * scale_factor
+ # variable becomes an albedo between 0 and 100.
+
+ else :
+ message = "Calibration mode not expected : " + calibration
+ raise NotImplementedError(message)
+
+ variable = variable.rename(
+ {variable.dims[0] : "y", variable.dims[1] : "x"})
+ variable.attrs.update(mda)
+ return variable
+ # get_dataset()
+
+ def orbital_param(self) :
+ orb_param_dict = {
+ "orbital_parameters": {
+ "satellite_actual_longitude": self.actualLongitude,
+ "satellite_actual_latitude": 0.,
+ "satellite_actual_altitude": self.alt,
+ "satellite_nominal_longitude": self.projectionLongitude,
+ "satellite_nominal_latitude": 0,
+ "satellite_nominal_altitude": self.alt,
+ "projection_longitude": self.projectionLongitude,
+ "projection_latitude": 0.,
+ "projection_altitude": self.alt,
+ }
+ }
+ return orb_param_dict
+
+ def channelType(self, pdict, pdictResoAdesc, pdictResoPid, satellite) :
+ strNbpix = str(self.nbpix)
+ if strNbpix in pdictResoAdesc :
+ pdict["a_desc"] = pdictResoAdesc[strNbpix]
+ pdict["p_id"] = pdictResoPid[strNbpix]
+ else :
+ message = "Resolution " + str(self.nbpix)
+ message += " not expected for " + satellite
+ raise NotImplementedError(message)
+
+ return(pdict)
+
+ def get_area_def(self, ds_id) :
+ """Get the area def."""
+
+ pdict = {}
+ pdict["cfac"] = np.int32(self.cfac)
+ pdict["lfac"] = np.int32(self.lfac)
+ pdict["coff"] = np.float32(self.coff)
+ pdict["loff"] = np.float32(self.loff)
+
+ pdict["a"] = 6378169
+ pdict["b"] = 6356583.8
+ pdict["h"] = self.alt - pdict["a"]
+ # 36000000 mètres.
+ pdict["ssp_lon"] = self.projectionLongitude
+ pdict["ncols"] = self.nblig
+ pdict["nlines"] = self.nbpix
+ pdict["sweep"] = "y"
+
+ # Force scandir to SEVIRI default, not known from file
+ pdict["scandir"] = "S2N"
+ pdict["a_name"] = "geosmsg"
+
+ pdictResoAdesc = {}
+ pdictResoPid = {}
+
+ if self.sensor == "seviri" :
+ # msg.
+ pdict["scandir"] = "N2S"
+ pdict["a_name"] = "geosmsg"
+
+ pdictResoAdesc["3712"] = "MSG/SEVIRI low resolution channel area"
+ pdictResoPid["3712"] = "msg_lowres"
+ pdictResoAdesc["11136"] = "MSG/SEVIRI HRV channel area"
+ pdictResoPid["11136"] = "msg_hires"
+
+ pdict = self.channelType(
+ pdict, pdictResoAdesc, pdictResoPid, "msg")
+
+ elif self.sensor == "fci" :
+ # mtg.
+ pdict["scandir"] = "N2S"
+ pdict["a_name"] = "geosmtg"
+
+ pdictResoAdesc["5568"] = "MTG 2km channel area"
+ pdictResoPid["5568"] = "mtg_lowres"
+ pdictResoAdesc["11136"] = "MTG 1km channel area"
+ pdictResoPid["11136"] = "mtg_midres"
+ pdictResoAdesc["22272"] = "MTG 500m channel area"
+ pdictResoPid["22272"] = "mtg_hires"
+
+ pdict = self.channelType(
+ pdict, pdictResoAdesc, pdictResoPid, "mtg")
+
+ elif self.sensor == "ahi" :
+ # Himawari.
+ pdict["scandir"] = "N2S"
+ pdict["a_name"] = "geoshima"
+
+ pdictResoAdesc["5500"] = "HIMA 2km channel area"
+ pdictResoPid["5500"] = "hima_lowres"
+ pdictResoAdesc["11000"] = "HIMA 1km channel area"
+ pdictResoPid["11000"] = "hima_midres"
+ pdictResoAdesc["22000"] = "HIMA 500m channel area"
+ pdictResoPid["22000"] = "hima_hires"
+
+ pdict = self.channelType(
+ pdict, pdictResoAdesc, pdictResoPid, "hima")
+
+ elif self.sensor == "abi" :
+ # Goesr.
+ pdict["scandir"] = "N2S"
+ pdict["a_name"] = "geosgoesr"
+ pdict["sweep"] = "x"
+
+ pdictResoAdesc["5424"] = "GOESR 2km channel area"
+ pdictResoPid["5424"] = "goesr_lowres"
+ pdictResoAdesc["10848"] = "GOESR 1km channel area"
+ pdictResoPid["10848"] = "goesr_midres"
+ pdictResoAdesc["21696"] = "GOESR 500m channel area"
+ pdictResoPid["21696"] = "goesr_hires"
+
+ pdict = self.channelType(
+ pdict, pdictResoAdesc, pdictResoPid, "goesr")
+
+ else :
+ message = "Sensor " + self.sensor + " not expected."
+ raise NotImplementedError(message)
+
+ aex = get_area_extent(pdict)
+ area = get_area_definition(pdict, aex)
+
+ return area
+ # get_area_def()
diff --git a/satpy/tests/reader_tests/test_geos_netcdficare.py b/satpy/tests/reader_tests/test_geos_netcdficare.py
new file mode 100644
index 0000000000..f0157e6ec3
--- /dev/null
+++ b/satpy/tests/reader_tests/test_geos_netcdficare.py
@@ -0,0 +1,589 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019 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 .
+
+"""Tests for the Icare MeteoFrance netcdfs reader,
+ satpy/readers/geos_netcdficare.py.
+
+SYNTAXE :
+ pytest
+
+ Before, copy the satpy/tests/reader_tests/test_geos_netcdfcare.py file
+ into the pytest directory pytest_projec.
+
+EXECUTION TIME :
+ 4 minutes.
+DATE OF CREATION :
+ 2024 11th october.
+LAST VERSIONS :
+
+AUTHOR :
+ Météo France.
+"""
+
+import os
+
+import numpy as np
+
+from satpy.scene import Scene
+from satpy import find_files_and_readers
+
+from datetime import datetime
+from netCDF4 import Dataset
+
+import logging
+logger = logging.getLogger('netcdficare')
+
+
+class TestGeosNetcdfIcareReader() :
+ # Test of the geos_netcdficare reader.
+ # This reader has been build for the Icare Meteo France netcdfs.
+ '''
+ def test_mtg_netcdficare(self, tmp_path) :
+ """ A dummy netcdf is built.
+ A scene self.scn for the nir_16 product for the same date
+ is built. We check that the scene parameters are the same
+ as thoses in the dummy netcdf.
+ This procedure is called by pytest.
+ """
+ self.initMtg(tmp_path)
+ self.scn.load(['nir_16'])
+ self.values = self.scn.values()
+ self.checkingSceneParameter("nir_16")
+ # test_mtg_netcdficare()
+
+ def test_msg_netcdficare(self, tmp_path) :
+ self.initMsgHrv(tmp_path)
+ self.scn.load(['HRV'])
+ self.values = self.scn.values()
+ self.checkingSceneParameter("HRV")
+
+ self.initMsg(tmp_path)
+ self.scn.load(['convection'])
+ self.values = self.scn.values()
+ self.checkingSceneParameter("convection")
+ # test_msg_netcdficare()
+ '''
+ def test_hima_netcdficare(self, tmp_path) :
+ self.initHima(tmp_path)
+ self.scn.load(['B10'])
+ self.values = self.scn.values()
+ self.checkingSceneParameter("B10")
+ # test_hima_netcdficare()
+
+ '''
+ def test_goesr_netcdficare(self, tmp_path) :
+ self.initGoesr(tmp_path)
+ self.scn.load(['C02'])
+ self.values = self.scn.values()
+ self.checkingSceneParameter("C02")
+ # test_goesr_netcdficare()
+ '''
+ # ----------------------------------------------------- #
+ # typeImage : convection, airmass...
+ # ----------------------------------------------------- #
+ def checkingSceneParameter(self, typeImage) :
+ startTime = self.scn.start_time
+ startTimeString = startTime.strftime('%Y-%m-%dT%H:%M:%S')
+ # 2024-06-28T10:00:40
+ assert startTimeString == self.expectedStartTime
+
+ endTime = self.scn.end_time
+ endTimeString = endTime.strftime('%Y-%m-%dT%H:%M:%S')
+ # 2024-06-28T10:12:41
+ assert endTimeString == self.expectedEndTime
+
+ sensor = self.scn.sensor_names
+ for isensor in sensor :
+ capteur = isensor
+ assert capteur == self.expectedSensor
+
+ platform = "error"
+ altitude = -1.
+ longitude = 999.
+
+ for data_arr in self.values :
+ # values come from the scene.
+ if "platform_name" in data_arr.attrs :
+ platform = data_arr.attrs["platform_name"]
+ if "orbital_parameters" in data_arr.attrs :
+ subAttr = data_arr.attrs["orbital_parameters"]
+ if "satellite_actual_altitude" in subAttr :
+ altitude = subAttr["satellite_actual_altitude"]
+ if "satellite_actual_longitude" in data_arr.attrs :
+ longitude = data_arr.attrs["satellite_actual_longitude"]
+
+ longitude = float(int(longitude * 10.)) / 10.
+ assert platform == self.expectedPlatform
+ assert longitude == self.expectedLongitude
+ assert altitude == self.expectedAltitude
+
+ xr = self.scn.to_xarray_dataset()
+ matrice = xr[typeImage]
+ nbdim = len(matrice.shape)
+ if nbdim == 3 :
+ # RGB.
+ nblin = matrice.shape[1]
+ nbpix = matrice.shape[2]
+ elif nbdim == 2 :
+ # PGM.
+ nblin = matrice.shape[0]
+ nbpix = matrice.shape[1]
+ else :
+ print("Dimension of shape not expected : ", nbdim)
+ exit(1)
+
+ assert nblin == self.expectedNblin
+ assert nbpix == self.expectedNbpix
+
+ cfac = xr.attrs["cfac"]
+ assert cfac == self.expectedCfac
+ lfac = xr.attrs["lfac"]
+ assert lfac == self.expectedLfac
+ coff = xr.attrs["coff"]
+ assert coff == self.expectedCoff
+ loff = xr.attrs["loff"]
+ assert loff == self.expectedLoff
+
+ satpyId = xr.attrs["_satpy_id"]
+ # DataID(name='convection', resolution=3000.403165817)
+ # Cf satpy/dataset/dataid.py.
+
+ resolution = satpyId.get("resolution")
+ resolution = float(int(resolution * 10.)) / 10.
+ assert resolution == self.expectedResolution
+ # checkingSceneParameter()
+
+ def initMsg(self, tmp_path) :
+ """
+ A fake netcdf is built.
+ A scene is built with the reader to be tested, applied to this netcdf.
+ Called by test_msg_netcdficare().
+ """
+ self.netcdfName = tmp_path / "Mmultic3kmNC4_msg03_202406281000.nc"
+ self.filepath = tmp_path
+
+ listIR = {
+ "IR_039", "WV_062", "WV_073", "IR_087", "IR_097",
+ "IR_108", "IR_120", "IR_134"
+ }
+
+ self.buildNetcdf(
+ self.netcdfName, 3712, "msg03", x0=-5570254, dx=3000.40604,
+ y0=5570254, cfac=1.3642337E7, coff=1857.0,
+ listVisible={"VIS006", "VIS008", "IR_016"}, listIR=listIR,
+ coordinateSystemName="GeosCoordinateSystem",
+ nomImageNavigation="ImageNavigation",
+ nomX="X", nomY="Y", time_coverage_end="2024-06-28T10:12:41Z365")
+
+ # We will check that the parameters written in the dummy netcdf
+ # can be read.
+ self.expectedStartTime = "2024-06-28T10:00:09"
+ self.expectedEndTime = "2024-06-28T10:12:41"
+ actualAltitude = 35786691 + 6378169 # 42164860.0
+ actualLongitude = 0.1
+ self.expectedPlatform = "Meteosat-10"
+ self.expectedSensor = "seviri"
+ self.expectedAltitude = actualAltitude
+ self.expectedLongitude = actualLongitude
+ self.expectedCfac = 1.3642337E7
+ self.expectedLfac = 1.3642337E7
+ self.expectedCoff = 1857.0
+ self.expectedLoff = 1857.0
+ self.expectedResolution = 3000.4
+ self.expectedNblin = 3712
+ self.expectedNbpix = 3712
+
+ # To build a scene at the date 20240628_100000,
+ # a netcdf corresponding to msg_netcdficare
+ # is looked for in the filepath directory.
+ yaml_file = 'msg_netcdficare'
+ myfiles = find_files_and_readers(
+ base_dir=self.filepath, start_time=datetime(2024, 6, 28, 10, 0),
+ end_time=datetime(2024, 6, 28, 10, 0), reader=yaml_file)
+ logger.info("Found myfiles = ", myfiles)
+ # {'msg_netcdficare': ['/tmp/Mmultic3kmNC4_msg03_202406281000.nc']}
+
+ self.scn = Scene(filenames=myfiles, reader=yaml_file)
+ # initMsg()
+
+ def initMsgHrv(self, tmp_path) :
+ """
+ A fake netcdf is built.
+ A scene is built with the reader to be tested, applied to this netcdf.
+ Called by test_msg_netcdficare().
+ """
+ self.netcdfName = tmp_path / "Mmultic1kmNC4_msg03_202406281000.nc"
+ self.filepath = tmp_path
+
+ self.buildNetcdf(
+ self.netcdfName, 11136, "msg03", x0=-5571254., dx=1000.135,
+ y0=5570254., cfac=40927014, coff=5566.0, listVisible={"HRV"},
+ listIR={}, coordinateSystemName="GeosCoordinateSystem_h",
+ nomImageNavigation="ImageNavigation_h", nomX="X_h", nomY="Y_h",
+ time_coverage_end="2024-06-28T10:12:41Z365")
+
+ # We will check that the parameters written in the dummy netcdf
+ # can be read.
+ self.expectedStartTime = "2024-06-28T10:00:09"
+ self.expectedEndTime = "2024-06-28T10:12:41"
+ actualAltitude = 35786691 + 6378169 # 42164860.0
+ actualLongitude = 0.1
+ self.expectedPlatform = "Meteosat-10"
+ self.expectedSensor = "seviri"
+ self.expectedAltitude = actualAltitude
+ self.expectedLongitude = actualLongitude
+ self.expectedCfac = 40927014
+ self.expectedLfac = 40927014
+ self.expectedCoff = 5566.0
+ self.expectedLoff = 5566.0
+ self.expectedResolution = 1000.1
+ self.expectedNblin = 11136
+ self.expectedNbpix = 11136
+
+ # To build a scene at the date 20240628_100000,
+ # a netcdf corresponding to msg_netcdficare
+ # is looked for, in the filepath directory.
+ yaml_file = 'msg_netcdficare'
+ myfiles = find_files_and_readers(
+ base_dir=self.filepath, start_time=datetime(2024, 6, 28, 10, 0),
+ end_time=datetime(2024, 6, 28, 10, 0), reader=yaml_file)
+ # logger.info("Found myfiles = ", myfiles)
+ # {'msg_netcdficare': ['/tmp/Mmultic3kmNC4_msg03_202406281000.nc']}
+
+ self.scn = Scene(filenames=myfiles, reader=yaml_file)
+ # initMsgHrv()
+
+ def initHima(self, tmp_path) :
+ """
+ A fake netcdf is built.
+ A scene is built with the reader to be tested, applied to this netcdf.
+ Called by test_hima_netcdficare().
+ """
+ self.netcdfName = tmp_path / "Jmultic2kmNC4_hima09_202406281000.nc"
+ self.filepath = tmp_path
+
+ listVisible = {
+ "VIS004", "VIS005", "VIS006", "VIS008", "IR_016", "IR_022"
+ }
+ listIR = {
+ "IR_038", "WV_062", "WV_069", "WV_073", "IR_085", "IR_096",
+ "IR_104", "IR_112", "IR_123", "IR_132"
+ }
+ self.buildNetcdf(
+ self.netcdfName, 5500, "hima09", x0=-5500000, dx=2000.0000047,
+ y0=5500000, cfac=20466275., coff=2750.5,
+ listVisible=listVisible, listIR=listIR,
+ coordinateSystemName="GeosCoordinateSystem2km",
+ nomImageNavigation="ImageNavigation2km",
+ nomX="X2km", nomY="Y2km",
+ time_coverage_end="2024-06-28T10:08:41Z365")
+
+ # We will check that the parameters written in the dummy netcdf
+ # can be read.
+ self.expectedStartTime = "2024-06-28T10:00:09"
+ self.expectedEndTime = "2024-06-28T10:08:41"
+ actualAltitude = 35786691 + 6378169 # 42164860.0
+ actualLongitude = 0.1
+ self.expectedPlatform = "Himawari-9"
+ self.expectedSensor = "ahi"
+ self.expectedAltitude = actualAltitude
+ self.expectedLongitude = actualLongitude
+ self.expectedCfac = 20466275.
+ self.expectedLfac = 20466275.
+ self.expectedCoff = 2750.5
+ self.expectedLoff = 2750.5
+ self.expectedResolution = 2000.
+ self.expectedNblin = 5500
+ self.expectedNbpix = 5500
+
+ # To build a scene at the date 20240628_100000,
+ # a netcdf corresponding to msg_netcdficare
+ # is looked for in the filepath directory.
+ yaml_file = 'hima_netcdficare'
+ myfiles = find_files_and_readers(
+ base_dir=self.filepath, start_time=datetime(2024, 6, 28, 10, 0),
+ end_time=datetime(2024, 6, 28, 10, 0), reader=yaml_file)
+ # logger.info("Found myfiles = ", myfiles)
+ # {'msg_netcdficare': ['/tmp/Mmultic3kmNC4_msg03_202406281000.nc']}
+
+ self.scn = Scene(filenames=myfiles, reader=yaml_file)
+ # initHima()
+
+ def initMtg(self, tmp_path) :
+ """
+ A fake netcdf is built.
+ A scene is built with the reader to be tested, applied to this netcdf.
+ Called by test_mtg_netcdficare().
+ """
+ self.netcdfName = tmp_path / "Mmultic1kmNC4_mtgi1_202406281000.nc"
+ self.filepath = tmp_path
+
+ # self.buildNetcdf(
+ # self.netcdfName, 5568, "mtgi1",
+ # x0 = -5568000, dx = 2000.0000047, y0 = 5568000, cfac = 13642337, coff = 2784.5,
+ # listVisible= {"VIS004", "VIS005", "VIS006", "VIS008", "VIS009",
+ # "IR_013", "IR_016", "IR_022"},
+ # listIR={"IR_038", "WV_062", "WV_073", "IR_087",
+ # "IR_097", "IR_105", "IR_123", "IR_133"},
+ # coordinateSystemName = "GeosCoordinateSystem2km",
+ # nomImageNavigation = "ImageNavigation2km",
+ # nomX = "X2km", nomY = "Y2km",
+ # time_coverage_end = "2024-06-28T10:08:41Z365")
+
+ listVisible = {
+ "VIS004", "VIS005", "VIS006", "VIS008",
+ "VIS009", "IR_013", "IR_016", "IR_022"
+ }
+
+ self.buildNetcdf(
+ self.netcdfName, 11136, "mtgi1",
+ x0=-5568000, dx=1000.0000047, y0=5568000,
+ cfac=4.093316350596011E7, coff=5568.5,
+ listVisible=listVisible, listIR={"IR_038", "IR_105"},
+ coordinateSystemName="GeosCoordinateSystem1km",
+ nomImageNavigation="ImageNavigation1km",
+ nomX="X1km", nomY="Y1km", time_coverage_end="2024-06-28T10:08:41Z")
+
+ # We will check that the parameters written in the dummy netcdf
+ # can be read.
+ self.expectedStartTime = "2024-06-28T10:00:09"
+ self.expectedEndTime = "2024-06-28T10:08:41"
+ actualAltitude = 35786691 + 6378169 # 42164860.0
+ actualLongitude = 0.1
+ self.expectedPlatform = "Meteosat-12"
+ self.expectedSensor = "fci"
+ self.expectedAltitude = actualAltitude
+ self.expectedLongitude = actualLongitude
+ self.expectedCfac = 4.093316350596011E7 # 13642337.
+ self.expectedLfac = 4.093316350596011E7 # 13642337.
+ self.expectedCoff = 5568.5 # 2784.5
+ self.expectedLoff = 5568.5 # 2784.5
+ self.expectedResolution = 1000.
+ self.expectedNblin = 11136 # 5568
+ self.expectedNbpix = 11136 # 5568
+
+ # To build a scene at the date 20240628_100000,
+ # a netcdf corresponding to msg_netcdficare
+ # is looked for in the filepath directory.
+ yaml_file = 'mtg_netcdficare'
+ myfiles = find_files_and_readers(
+ base_dir=self.filepath, start_time=datetime(2024, 6, 28, 10, 0),
+ end_time=datetime(2024, 6, 28, 10, 0), reader=yaml_file)
+ # logger.info("Found myfiles = ", myfiles)
+ # {'msg_netcdficare': ['/tmp/Mmultic3kmNC4_msg03_202406281000.nc']}
+
+ self.scn = Scene(filenames=myfiles, reader=yaml_file)
+ # initMtg()
+
+ def initGoesr(self, tmp_path) :
+ """
+ A fake netcdf is built.
+ A scene is built with the reader to be tested, applied to this netcdf.
+ Called by test_goesr_netcdficare().
+ """
+ self.netcdfName = tmp_path / "Emultic2kmNC4_goes16_202406281000.nc"
+ self.filepath = tmp_path
+
+ listVisible = {
+ "VIS_004", "VIS_006", "VIS_008", "VIS_014", "VIS_016", "VIS_022"
+ }
+ listIR = {
+ "IR_039", "IR_062", "IR_069", "IR_073", "IR_085",
+ "IR_096", "IR_103", "IR_114", "IR_123", "IR_133"
+ }
+ self.buildNetcdf(
+ self.netcdfName, 5424, "goes16",
+ x0=-5434894.8, dx=2004.017288, y0=5434894.8,
+ cfac=20425338.9, coff=2712.5,
+ listVisible=listVisible, listIR=listIR,
+ coordinateSystemName="GeosCoordinateSystem2km",
+ nomImageNavigation="ImageNavigation2km",
+ nomX="X2km", nomY="Y2km",
+ time_coverage_end="2024-06-28T10:08:41.1Z")
+
+ # We will check that the parameters written in the dummy netcdf
+ # can be read.
+ self.expectedStartTime = "2024-06-28T10:00:09"
+ self.expectedEndTime = "2024-06-28T10:08:41"
+ actualAltitude = 35786691 + 6378169 # 42164860.0
+ actualLongitude = 0.1
+ self.expectedPlatform = "GOES-16"
+ self.expectedSensor = "abi"
+ self.expectedAltitude = actualAltitude
+ self.expectedLongitude = actualLongitude
+ self.expectedCfac = 20425338.9
+ self.expectedLfac = 20425338.9
+ self.expectedCoff = 2712.5
+ self.expectedLoff = 2712.5
+ self.expectedResolution = 2000.
+ self.expectedNblin = 5424
+ self.expectedNbpix = 5424
+
+ # To build a scene at the date 20240628_100000,
+ # a netcdf corresponding to msg_netcdficare
+ # is looked for in the filepath directory.
+ yaml_file = 'goesr_netcdficare'
+ myfiles = find_files_and_readers(
+ base_dir=self.filepath, start_time=datetime(2024, 6, 28, 10, 0),
+ end_time=datetime(2024, 6, 28, 10, 0), reader=yaml_file)
+
+ self.scn = Scene(filenames=myfiles, reader=yaml_file)
+ # initGoesr()
+
+ def buildNetcdf(
+ self, ncName, nbpix=3712, nomSatellite="msg03",
+ x0=-5570254, dx=3000.40604, y0=5570254,
+ cfac=1.3642337E7, coff=1857.0,
+ listVisible={}, listIR={},
+ coordinateSystemName="GeosCoordinateSystem",
+ nomImageNavigation="ImageNavigation",
+ nomX="X", nomY="Y",
+ time_coverage_end="2024-06-28T10:12:41Z365") :
+ """
+ ncName : tmp_path / Mmultic3kmNC4_msg03_202406281000.nc
+ A dummy icare Meteo France netcdf is built here.
+ Called by initMsg...
+ listVisible = {"VIS006", "VIS008", "IR_016"}
+ listIR = {"IR_039", "WV_062", "WV_073", "IR_087", "IR_097", "IR_108",
+ "IR_120", "IR_134"}
+ """
+ if os.path.exists(ncName) :
+ os.remove(ncName)
+ ncfileOut = Dataset(
+ ncName, mode="w", clobber=True,
+ diskless=False, persist=False, format='NETCDF4')
+
+ ncfileOut.createDimension(u'ny', nbpix)
+ ncfileOut.createDimension(u'nx', nbpix)
+ ncfileOut.createDimension(u'numerical_count', 65536)
+ ncfileOut.setncattr("time_coverage_start", "2024-06-28T10:00:09Z383")
+ ncfileOut.setncattr("time_coverage_end", time_coverage_end)
+ ncfileOut.setncattr("Area_of_acquisition", "globe")
+
+ fill_value = -32768
+ var = ncfileOut.createVariable(
+ "satellite", "c", zlib=True, complevel=4,
+ shuffle=True, fletcher32=False, contiguous=False,
+ chunksizes=None, endian='native', least_significant_digit=None)
+
+ var.setncattr("id", nomSatellite)
+ var.setncattr("dst", 35786691.)
+ var.setncattr("lon", float(0.1))
+
+ var = ncfileOut.createVariable(
+ "geos", "c", zlib=True, complevel=4, shuffle=True,
+ fletcher32=False, contiguous=False, chunksizes=None,
+ endian='native', least_significant_digit=None)
+ var.setncattr("longitude_of_projection_origin", 0.)
+
+ var = ncfileOut.createVariable(
+ coordinateSystemName, "c", zlib=True, complevel=4,
+ shuffle=True, fletcher32=False, contiguous=False,
+ chunksizes=None, endian='native', least_significant_digit=None)
+
+ stringGeotransform = str(x0) + ", " + str(dx) + ", 0, "
+ stringGeotransform += str(-x0) + ", 0, " + str(-dx)
+ # -5570254, 3000.40604, 0, 5570254, 0, -3000.40604
+ var.setncattr("GeoTransform", stringGeotransform)
+
+ var = ncfileOut.createVariable(
+ nomImageNavigation, "c", zlib=True, complevel=4,
+ shuffle=True, fletcher32=False, contiguous=False,
+ chunksizes=None, endian='native', least_significant_digit=None)
+ var.setncattr("CFAC", cfac)
+ var.setncattr("LFAC", cfac)
+ var.setncattr("COFF", coff)
+ var.setncattr("LOFF", coff)
+
+ var = ncfileOut.createVariable(
+ nomX, 'float32', u'nx', zlib=True, complevel=4,
+ shuffle=True, fletcher32=False, contiguous=False,
+ chunksizes=None, endian='native', least_significant_digit=None)
+ var[:] = np.array(([(x0 + dx * i) for i in range(nbpix)]))
+
+ var = ncfileOut.createVariable(
+ nomY, 'float32', u'ny', zlib=True, complevel=4,
+ shuffle=True, fletcher32=False, contiguous=False,
+ chunksizes=None, endian='native', least_significant_digit=None)
+ y0 = -x0
+ dy = -dx
+ var[:] = np.array(([(y0 + dy * i) for i in range(nbpix)]))
+
+ self.visibleChannelsCreation(
+ ncfileOut, fill_value, listVisible, nbpix, coordinateSystemName)
+ self.infrarougeChannelsCreation(
+ ncfileOut, fill_value, listIR, nbpix, coordinateSystemName)
+ ncfileOut.close
+ # buildNetcdf()
+
+ def visibleChannelsCreation(
+ self, ncfileOut, fill_value, listVisible, nbpix, coordinateSystemName) :
+
+ for channel in listVisible :
+ var = ncfileOut.createVariable(
+ channel, 'short', ('ny', 'nx'), zlib=True, complevel=4,
+ shuffle=True, fletcher32=False, contiguous=False,
+ chunksizes=None, endian='native',
+ least_significant_digit=None, fill_value=fill_value)
+ var[:] = np.array(
+ ([[i * 2 for i in range(nbpix)] for j in range(nbpix)]))
+ # Hundredths of albedo between 0 and 10000.
+ var.setncattr("scale_factor", 0.01)
+ var.setncattr("add_offset", 0.)
+ var.setncattr("bandfactor", 20.76)
+ var.setncattr("_CoordinateSystems", coordinateSystemName)
+
+ var = ncfileOut.createVariable(
+ "Albedo_to_Native_count_" + channel, 'short',
+ 'numerical_count', zlib=True, complevel=4, shuffle=True,
+ fletcher32=False, contiguous=False, chunksizes=None,
+ endian='native', least_significant_digit=None,
+ fill_value=-9999)
+ var[:] = np.array(([-9999 for i in range(65536)]))
+ # In order to come back to the native datas on 10, 12 or 16 bits.
+
+ def infrarougeChannelsCreation(
+ self, ncfileOut, fill_value, listIR, nbpix, coordinateSystemName) :
+
+ for channel in listIR :
+ var = ncfileOut.createVariable(
+ channel, 'short', ('ny', 'nx'), zlib=True, complevel=4,
+ shuffle=True, fletcher32=False, contiguous=False,
+ chunksizes=None, endian='native',
+ least_significant_digit=None, fill_value=fill_value)
+ var[:] = np.array(
+ ([[-9000 + j * 4 for i in range(nbpix)] for j in range(nbpix)])
+ )
+ # Hundredths of celcius degrees.
+ var.setncattr("scale_factor", 0.01)
+ var.setncattr("add_offset", 273.15)
+ var.setncattr("nuc", 1600.548)
+ var.setncattr("alpha", 0.9963)
+ var.setncattr("beta", 2.185)
+ var.setncattr("_CoordinateSystems", coordinateSystemName)
+
+ var = ncfileOut.createVariable(
+ "Temp_to_Native_count_" + channel, 'short',
+ 'numerical_count', zlib=True, complevel=4, shuffle=True,
+ fletcher32=False, contiguous=False, chunksizes=None,
+ endian='native', least_significant_digit=None,
+ fill_value=-9999)
+ var[:] = np.array(([-9999 for i in range(65536)]))
+ # In order to come back to the native datas on 10, 12 or 16 bits.
+
+ # class TestGeosNetcdfIcareReader.