[Git][debian-gis-team/pyspectral][upstream] New upstream version 0.13.6+ds

Antonio Valentino (@antonio.valentino) gitlab at salsa.debian.org
Sun Aug 10 17:55:08 BST 2025



Antonio Valentino pushed to branch upstream at Debian GIS Project / pyspectral


Commits:
e2645712 by Antonio Valentino at 2025-08-09T09:25:27+00:00
New upstream version 0.13.6+ds
- - - - -


14 changed files:

- .pre-commit-config.yaml
- AUTHORS.md
- CHANGELOG.md
- bin/composite_rsr_plot.py
- doc/index.rst
- doc/platforms_supported.rst
- pyspectral/blackbody.py
- pyspectral/etc/pyspectral.yaml
- pyspectral/rayleigh.py
- pyspectral/solar.py
- pyspectral/tests/test_blackbody.py
- pyspectral/utils.py
- rsr_convert_scripts/oli_reader.py → rsr_convert_scripts/oli_tirs_reader.py
- setup.py


Changes:

=====================================
.pre-commit-config.yaml
=====================================
@@ -7,7 +7,7 @@ repos:
     - id: flake8
       additional_dependencies: [flake8-docstrings, flake8-debugger, flake8-bugbear]
 -   repo: https://github.com/pycqa/isort
-    rev: 5.13.2
+    rev: 6.0.1
     hooks:
       - id: isort
         language_version: python3


=====================================
AUTHORS.md
=====================================
@@ -20,3 +20,5 @@ The following people have made contributions to this project:
 - [Rolf Helge Pfeiffer](https://github.com/HelgeCPH)
 - [Antonio Valentino](https://github.com/avalentino)
 - [Lu Liu (yukaribbba)](https://github.com/yukaribbba)
+- [Chung-Hsiang Horng (chorng)](https://github.com/chorng)
+- [Gerrit Holl (gerritholl)](https://github.com/gerritholl)


=====================================
CHANGELOG.md
=====================================
@@ -1,3 +1,31 @@
+## Version 0.13.6 (2025/07/09)
+
+### Issues Closed
+
+* [Issue 244](https://github.com/pytroll/pyspectral/issues/244) - Seeking References for “near_infrared_reflectance”
+* [Issue 238](https://github.com/pytroll/pyspectral/issues/238) - Eliminate division warning of blackbody_wn_rad2temp
+
+In this release 2 issues were closed.
+
+### Pull Requests Merged
+
+#### Bugs fixed
+
+* [PR 254](https://github.com/pytroll/pyspectral/pull/254) - Fix broken link to MSG interpretation guide
+* [PR 235](https://github.com/pytroll/pyspectral/pull/235) - Remove deprecated aerosol_type download_luts usage in rayleigh
+
+#### Features added
+
+* [PR 246](https://github.com/pytroll/pyspectral/pull/246) - Tiny fix simplifying the rsr plot code
+* [PR 240](https://github.com/pytroll/pyspectral/pull/240) - Fix rsr plotting to allow for similar (overlapping) bands
+
+#### Documentation changes
+
+* [PR 254](https://github.com/pytroll/pyspectral/pull/254) - Fix broken link to MSG interpretation guide
+
+In this release 5 pull requests were closed.
+
+
 ## Version 0.13.5 (2024/09/24)
 
 ### Pull Requests Merged


=====================================
bin/composite_rsr_plot.py
=====================================
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 #
-# Copyright (c) 2016-2022 Pytroll developers
+# Copyright (c) 2016-2022, 2024 Pytroll developers
 #
 #
 # This program is free software: you can redistribute it and/or modify
@@ -103,6 +103,10 @@ def get_arguments():
                        help="The wavelength range for the plot",
                        default=[None, None], type=float)
 
+    parser.add_argument("--exclude_bandnames", nargs='*',
+                        default=[],
+                        help="Sensor band names to exclude from the plot")
+
     return parser.parse_args()
 
 
@@ -139,6 +143,8 @@ if __name__ == "__main__":
     elif args.wavelength:
         req_wvl = args.wavelength
 
+    excluded_bandnames = args.exclude_bandnames
+
     figscale = 1.0
     if wvlmin:
         figscale = (wvlmax - wvlmin) / 4.
@@ -184,24 +190,21 @@ if __name__ == "__main__":
 
         else:
             wvlx = wvlmin
-            prev_band = None
+            prev_bands = []
             while wvlx < wvlmax:
                 bands = rsr.get_bandname_from_wavelength(wvlx, wavel_res, multiple_bands=True)
-
-                if isinstance(bands, list):
-                    b__ = bands[0]
-                    for b in bands[1:]:
-                        LOG.warning("Skipping band %s", str(b))
-                else:
-                    b__ = bands
-
                 wvlx = wvlx + wavel_res / 5.
-                if not b__:
+                if not bands:
                     continue
-                if b__ != prev_band:
-                    plt = plot_band(plt, b__, rsr,
-                                    platform_name_in_legend=(not no_platform_name_in_legend))
-                    prev_band = b__
+
+                if not isinstance(bands, list):
+                    bands = [bands]
+
+                for b__ in bands:
+                    if b__ not in excluded_bandnames and b__ not in prev_bands:
+                        plt = plot_band(plt, b__, rsr,
+                                        platform_name_in_legend=(not no_platform_name_in_legend))
+                    prev_bands.append(b__)
 
     if not something2plot:
         LOG.error("Nothing to plot!")


=====================================
doc/index.rst
=====================================
@@ -36,7 +36,7 @@ color imagery.
 .. _github: http://github.com/pytroll/pyspectral
 .. _mpop: http://www.github.com/pytroll/mpop
 .. _SatPy: http://www.github.com/pytroll/satpy
-.. _MSG Interpretation Guide: http://oiswww.eumetsat.org/WEBOPS/msg_interpretation/index.php 
+.. _MSG Interpretation Guide: https://resources.eumetrain.org/IntGuide/ 
 
 
 .. toctree::


=====================================
doc/platforms_supported.rst
=====================================
@@ -95,8 +95,17 @@ have been included in Pyspectral.
       - `rsr_viirs_Suomi-NPP.h5`
       - GSICS_
     * - Landsat-8 oli
-      - `rsr_oli_Landsat-8.h5`
-      - NASA-Landsat-OLI_
+      - `rsr_oli_tirs_Landsat-8.h5`
+      - NASA-Landsat-8-OLI_
+    * - Landsat-8 tirs
+      - `rsr_oli_tirs_Landsat-8.h5`
+      - NASA-Landsat-8-TIRS_
+    * - Landsat-9 oli-2
+      - `rsr_oli_tirs_Landsat-9.h5`
+      - NASA-Landsat-9-OLI_
+    * - Landsat-9 tirs-2
+      - `rsr_oli_tirs_Landsat-9.h5`
+      - NASA-Landsat-9-TIRS_
     * - FY-3D mersi-2
       - `rsr_mersi-2_FY-3D.h5`
       - CMA_ (Acquired via personal contact)
@@ -125,7 +134,10 @@ have been included in Pyspectral.
 .. _ESA-Sentinel-OLCI: https://sentinel.esa.int/documents/247904/322304/OLCI+SRF+%28NetCDF%29/15cfd7a6-b7bc-4051-87f8-c35d765ae43a
 .. _ESA-Sentinel-SLSTR: https://sentinel.esa.int/web/sentinel/technical-guides/sentinel-3-slstr/instrument/measured-spectral-response-function-data
 .. _ESA-Sentinel-MSI: https://earth.esa.int/documents/247904/685211/S2-SRF_COPE-GSEG-EOPG-TN-15-0007_3.0.xlsx
-.. _NASA-Landsat-OLI: https://landsat.gsfc.nasa.gov/wp-content/uploads/2013/06/Ball_BA_RSR.v1.1-1.xlsx
+.. _NASA-Landsat-8-OLI: https://landsat.gsfc.nasa.gov/wp-content/uploads/2014/09/Ball_BA_RSR.v1.2.xlsx
+.. _NASA-Landsat-9-OLI: https://landsat.gsfc.nasa.gov/wp-content/uploads/2024/03/L9_OLI2_Ball_BA_RSR.v2-1.xlsx
+.. _NASA-Landsat-8-TIRS: https://landsat.gsfc.nasa.gov/wp-content/uploads/2013/06/TIRS_Relative_Spectral_Responses.BA_.v1.xlsx
+.. _NASA-Landsat-9-TIRS: https://landsat.gsfc.nasa.gov/wp-content/uploads/2021-10/L9_TIRS2_Relative_Spectral_Responses.BA.v1.0.xlsx
 .. _NESDIS: https://ncc.nesdis.noaa.gov/J1VIIRS/J1VIIRSSpectralResponseFunctions.php
 .. _CMA: http://www.cma.gov.cn/en2014/
 .. _NWPSAF-MetImage: https://nwpsaf.eu/downloads/rtcoef_rttov12/ir_srf/rtcoef_metopsg_1_metimage_srf.html


=====================================
pyspectral/blackbody.py
=====================================
@@ -57,7 +57,7 @@ def blackbody_rad2temp(wavelength, radiance):
     if getattr(wavelength, "dtype", None) != radiance.dtype:
         # avoid a wavelength numpy scalar upcasting radiances (ex. 32-bit to 64-bit float)
         wavelength = radiance.dtype.type(wavelength)
-    with np.errstate(invalid='ignore'):
+    with np.errstate(divide='ignore', invalid='ignore'):
         return PLANCK_C1 / (wavelength * np.log(PLANCK_C2 / (radiance * wavelength**5) + 1.0))
 
 
@@ -73,7 +73,7 @@ def blackbody_wn_rad2temp(wavenumber, radiance):
         The derived temperature in Kelvin.
 
     """
-    with np.errstate(invalid='ignore'):
+    with np.errstate(divide='ignore', invalid='ignore'):
         return PLANCK_C1 * wavenumber / np.log((PLANCK_C2 * wavenumber**3) / radiance + 1.0)
 
 


=====================================
pyspectral/etc/pyspectral.yaml
=====================================
@@ -136,6 +136,16 @@ download_from_internet: True
 # Envisat-aatsr:
 #   path: /path/to/original/envisat/aatsr/data
 
+# Landsat-8-oli_tirs:
+#   path: D:\sat_data\
+#   oli: Ball_BA_RSR.v1.2.xlsx
+#   tirs: TIRS_Relative_Spectral_Responses.BA_.v1.xlsx
+
+# Landsat-9-oli_tirs:
+#   path: D:\sat_data\
+#   oli: L9_OLI2_Ball_BA_RSR.v2-1.xlsx
+#   tirs: L9_TIRS2_Relative_Spectral_Responses.BA.v1.0.xlsx
+
 # Sentinel-3A-slstr:
 #   path: /path/to/original/sentinel-3a/slstr/data
 #   ch1: SLSTR_FM02_S1_20150122.nc


=====================================
pyspectral/rayleigh.py
=====================================
@@ -170,7 +170,7 @@ class Rayleigh(RayleighConfigBaseClass):
 
         if not self._lutfiles_version_uptodate and self.do_download:
             LOG.info("Will download from internet...")
-            download_luts(aerosol_type=aerosol_type)
+            download_luts(aerosol_types=[aerosol_type])
 
         if (not os.path.exists(self.reflectance_lut_filename) or
                 not os.path.isfile(self.reflectance_lut_filename)):


=====================================
pyspectral/solar.py
=====================================
@@ -131,18 +131,21 @@ class SolarIrradianceSpectrum(object):
     def inband_solarflux(self, rsr, scale=1.0, **options):
         """Get the in band solar flux.
 
-        Derive the inband solar flux for a given instrument relative
+        Derive the in-band integrated solar flux for a given instrument relative
         spectral response valid for an earth-sun distance of one AU.
+
+        This is the total solar flux in the band in units of W/m^2.
         """
         return self._band_calculations(rsr, True, scale, **options)
 
     def inband_solarirradiance(self, rsr, scale=1.0, **options):
         """Get the in band solar irradiance.
 
-        Derive the inband solar irradiance for a given instrument relative
+        Derive the in-band spectral solar irradiance for a given instrument relative
         spectral response valid for an earth-sun distance of one AU.
 
-        (Same as the in band solar flux).
+        This is the total solar flux in the band convolved with the
+        spectral response function in units of W/m^2/micron.
         """
         return self._band_calculations(rsr, False, scale, **options)
 


=====================================
pyspectral/tests/test_blackbody.py
=====================================
@@ -19,6 +19,8 @@
 
 """Unit testing the Blackbody/Plack radiation derivation."""
 
+import warnings
+
 import dask
 import dask.array as da
 import numpy as np
@@ -110,3 +112,12 @@ class TestBlackbody:
         expected = np.array([290.3276916, 283.76115441,
                              302.4181330, 333.1414164]).reshape(2, 2)
         np.testing.assert_allclose(t__, expected)
+
+    def test_ignore_division_warning(self):
+        """Test that zero division warning is ignored."""
+        with warnings.catch_warnings():
+            warnings.simplefilter("error")
+            _ = blackbody_rad2temp(np.ones(1), np.zeros(1))
+            _ = blackbody_wn_rad2temp(np.ones(1), np.zeros(1))
+            _ = blackbody(np.ones(2), np.array([0, 1]))
+            _ = blackbody_wn(np.ones(2), np.array([0, 1]))


=====================================
pyspectral/utils.py
=====================================
@@ -87,7 +87,8 @@ INSTRUMENTS = {'Envisat': 'aatsr',
                'Electro-L-N2': 'msu-gs',
                'Sentinel-3A': ['olci', 'slstr'],
                'Sentinel-3B': ['olci', 'slstr'],
-               'Landsat-8': 'oli',
+               'Landsat-8': 'oli_tirs',
+               'Landsat-9': 'oli_tirs',
                'Meteosat-10': 'seviri',
                'Meteosat-11': 'seviri',
                'Meteosat-8': 'seviri',
@@ -108,10 +109,10 @@ INSTRUMENT_TRANSLATION_DASH2SLASH = {'avhrr-1': 'avhrr/1',
                                      'avhrr-2': 'avhrr/2',
                                      'avhrr-3': 'avhrr/3'}
 
-HTTP_PYSPECTRAL_RSR = "https://zenodo.org/records/13833977/files/pyspectral_rsr_data.tgz"
+HTTP_PYSPECTRAL_RSR = "https://zenodo.org/records/14008148/files/pyspectral_rsr_data.tgz"
 
 RSR_DATA_VERSION_FILENAME = "PYSPECTRAL_RSR_VERSION"
-RSR_DATA_VERSION = "v1.4.0"
+RSR_DATA_VERSION = "v1.4.1"
 
 
 ATM_CORRECTION_LUT_VERSION = {}


=====================================
rsr_convert_scripts/oli_reader.py → rsr_convert_scripts/oli_tirs_reader.py
=====================================
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 #
-# Copyright (c) 2017-2022 Pytroll developers
+# Copyright (c) 2017-2024 Pytroll developers
 #
 #
 # This program is free software: you can redistribute it and/or modify
@@ -17,29 +17,52 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-"""Landsat-8 OLI reader."""
+"""Landsat-8/9 OLI/TIRS reader.
+
+This reader generates spectral responses for OLI and TIRS instruments aboard Landsat-8 and -9.
+We assume that the instruments are one combined instrument from the user perspective, called `oli_tirs` rather
+than generating RSRs for the two instruments separately.
+The original spectral response data can be found at the links below.
+Landsat-8/OLI:
+ = https://landsat.gsfc.nasa.gov/wp-content/uploads/2014/09/Ball_BA_RSR.v1.2.xlsx
+Landsat-9/OLI-2:
+ = https://landsat.gsfc.nasa.gov/wp-content/uploads/2024/03/L9_OLI2_Ball_BA_RSR.v2-1.xlsx
+Landsat-8/TIRS:
+ = https://landsat.gsfc.nasa.gov/wp-content/uploads/2013/06/TIRS_Relative_Spectral_Responses.BA_.v1.xlsx
+Landsat-9/TIRS-2:
+ = https://landsat.gsfc.nasa.gov/wp-content/uploads/2021-10/L9_TIRS2_Relative_Spectral_Responses.BA.v1.0.xlsx
+"""
 
 import logging
-import os
+from pathlib import Path
 
 import numpy as np
-from xlrd import open_workbook
+import pandas as pd
 
 from pyspectral.raw_reader import InstrumentRSR
 from pyspectral.utils import convert2hdf5 as tohdf5
 
 LOG = logging.getLogger(__name__)
 
+OLI_BAND_NAMES = {"B1": "CoastalAerosol",
+                  "B2": "Blue",
+                  "B3": "Green",
+                  "B4": "Red",
+                  "B5": "NIR",
+                  "B6": "Cirrus",
+                  "B7": "SWIR1",
+                  "B8": "SWIR2",
+                  "B9": "Pan"}
 
-OLI_BAND_NAMES = {'CoastalAerosol': 'B1',
-                  'Blue': 'B2',
-                  'Green': 'B3',
-                  'Red': 'B4',
-                  'NIR': 'B5',
-                  'Cirrus': 'B9',
-                  'SWIR1': 'B6',
-                  'SWIR2': 'B7',
-                  'Pan': 'B8'}
+TIRS_SHEETNAMES_L8 = {"B10": "TIRS BA RSR",
+                      "B11": "TIRS BA RSR"}
+TIRS_BAND_NAMES_L8 = {"B10": "TIRS1 10.8um band average",
+                      "B11": "TIRS2 12.0um band average"}
+
+TIRS_SHEETNAMES_L9 = {"B10": "TIRS Band 10 BA RSR",
+                      "B11": "TIRS Band 11 BA RSR"}
+TIRS_BAND_NAMES_L9 = {"B10": "Band 10 Band=Average RSR",
+                      "B11": "Band 11 Band-Average RSR"}
 
 
 class OliRSR(InstrumentRSR):
@@ -48,12 +71,19 @@ class OliRSR(InstrumentRSR):
     def __init__(self, bandname, platform_name):
         """Read the Landsat OLI relative spectral responses for all channels."""
         super(OliRSR, self).__init__(bandname, platform_name)
-
-        self.instrument = 'oli'
+        self.instrument = "oli_tirs"
         self._get_options_from_config()
+        self.band = bandname
+        opts = self.options[f"{platform_name}-{self.instrument}"]
+        if bandname in OLI_BAND_NAMES:
+            self.path = Path(opts["path"]) / Path(opts["oli"])
+        elif bandname in TIRS_BAND_NAMES_L8:
+            self.path = Path(opts["path"]) / Path(opts["tirs"])
+        else:
+            raise ValueError(f"Unknown band name: {bandname}")
 
-        LOG.debug("Filename: %s", str(self.path))
-        if os.path.exists(self.path):
+        LOG.debug(f"Filename: {self.path}")
+        if self.path.exists():
             self._load()
         else:
             raise IOError("Couldn't find an existing file for this band: " +
@@ -61,25 +91,34 @@ class OliRSR(InstrumentRSR):
 
     def _load(self, scale=0.001):
         """Load the Landsat OLI relative spectral responses."""
-        with open_workbook(self.path) as wb_:
-            for sheet in wb_.sheets():
-                if sheet.name in ['Plot of AllBands', ]:
-                    continue
-                ch_name = OLI_BAND_NAMES.get(sheet.name.strip())
+        if self.band in OLI_BAND_NAMES:
+            df = pd.read_excel(self.path, engine="openpyxl", sheet_name=OLI_BAND_NAMES[self.band])
+            wvl = np.array(df["Wavelength"]) / 1000.
+            resp = np.array(df["BA RSR [watts]"])
+        else:
+            if self.platform_name == "Landsat-8":
+                sheet_name = TIRS_SHEETNAMES_L8[self.band]
+                band_name = TIRS_BAND_NAMES_L8[self.band]
+            elif self.platform_name == "Landsat-9":
+                sheet_name = TIRS_SHEETNAMES_L9[self.band]
+                band_name = TIRS_BAND_NAMES_L9[self.band]
+            else:
+                raise ValueError(f"Unknown platform: {self.platform_name}")
+            df = pd.read_excel(self.path, engine="openpyxl", sheet_name=sheet_name)
 
-                if ch_name != self.bandname:
-                    continue
+            wvl = np.array(df["wavelength [um]"]) / 1000.
+            resp = np.array(df[band_name])
 
-                wvl = sheet.col_values(0, 2)
-                resp = sheet.col_values(1, 2)
+        # Cut unneeded points
+        pts = np.argwhere(resp > 0.002)
+        wvl = np.squeeze(wvl[pts])
+        resp = np.squeeze(resp[pts])
 
-                self.rsr = {'wavelength': np.array(wvl) / 1000.,
-                            'response': np.array(resp)}
-                break
+        self.rsr = {"wavelength": wvl,
+                    "response": resp}
 
 
 if __name__ == "__main__":
-    bands = OLI_BAND_NAMES.values()
-    bands.sort()
-    for platform_name in ['Landsat-8', ]:
+    bands = sorted(OLI_BAND_NAMES.keys()) + sorted(TIRS_BAND_NAMES_L8.keys())
+    for platform_name in ["Landsat-8", "Landsat-9"]:
         tohdf5(OliRSR, platform_name, bands)


=====================================
setup.py
=====================================
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
-# Copyright (c) 2013-2022 Pytroll
+# Copyright (c) 2013-2024 Pytroll
 #
 #
 # This program is free software: you can redistribute it and/or modify
@@ -71,6 +71,7 @@ setup(name=NAME,
                       'matplotlib': ['matplotlib'],
                       'pandas': ['pandas'],
                       'tqdm': ['tqdm'],
+                      'openpyxl': ['openpyxl'],
                       'test': test_requires,
                       'dask': dask_extra},
       scripts=['bin/plot_rsr.py', 'bin/composite_rsr_plot.py',



View it on GitLab: https://salsa.debian.org/debian-gis-team/pyspectral/-/commit/e264571240f7feb500180494325c4a47bae93b1c

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/pyspectral/-/commit/e264571240f7feb500180494325c4a47bae93b1c
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20250810/f9779fec/attachment-0001.htm>


More information about the Pkg-grass-devel mailing list