[Git][debian-gis-team/pyorbital][upstream] New upstream version 1.8.3

Antonio Valentino (@antonio.valentino) gitlab at salsa.debian.org
Sat Jun 29 12:03:43 BST 2024



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


Commits:
f294445a by Antonio Valentino at 2024-06-29T11:44:55+02:00
New upstream version 1.8.3
- - - - -


9 changed files:

- .github/workflows/ci.yaml
- .github/workflows/deploy-sdist.yaml
- CHANGELOG.md
- continuous_integration/environment.yaml
- pyorbital/astronomy.py
- pyorbital/tests/__init__.py
- pyorbital/tests/test_astronomy.py
- pyorbital/tests/test_orbital.py
- pyorbital/version.py


Changes:

=====================================
.github/workflows/ci.yaml
=====================================
@@ -13,10 +13,10 @@ jobs:
       fail-fast: true
       matrix:
         os: ["windows-latest", "ubuntu-latest", "macos-latest"]
-        python-version: ["3.9", "3.10", "3.11"]
+        python-version: ["3.9", "3.11", "3.12"]
         experimental: [false]
         include:
-          - python-version: "3.11"
+          - python-version: "3.12"
             os: "ubuntu-latest"
             experimental: true
 
@@ -89,7 +89,7 @@ jobs:
           pytest --cov=pyorbital pyorbital/tests --cov-report=xml --cov-report=
 
       - name: Upload unittest coverage to Codecov
-        uses: codecov/codecov-action at v3
+        uses: codecov/codecov-action at v4
         with:
           flags: unittests
           file: ./coverage.xml


=====================================
.github/workflows/deploy-sdist.yaml
=====================================
@@ -19,7 +19,7 @@ jobs:
 
       - name: Publish package to PyPI
         if: github.event.action == 'published'
-        uses: pypa/gh-action-pypi-publish at v1.8.11
+        uses: pypa/gh-action-pypi-publish at v1.9.0
         with:
           user: __token__
           password: ${{ secrets.pypi_password }}
\ No newline at end of file


=====================================
CHANGELOG.md
=====================================
@@ -1,3 +1,20 @@
+## Version 1.8.3 (2024/06/25)
+
+### Issues Closed
+
+* [Issue 151](https://github.com/pytroll/pyorbital/issues/151) - Issue Calculating Accurate View Zenith Angles on Terra Satellite Overpasses
+
+In this release 1 issue was closed.
+
+### Pull Requests Merged
+
+#### Bugs fixed
+
+* [PR 156](https://github.com/pytroll/pyorbital/pull/156) - Fix dtype preservation in astronomy functions
+
+In this release 1 pull request was closed.
+
+
 ## Version 1.8.2 (2024/02/05)
 
 ### Issues Closed


=====================================
continuous_integration/environment.yaml
=====================================
@@ -19,7 +19,6 @@ dependencies:
   - coverage
   - codecov
   - behave
-  - mock
   - zarr
   - geoviews
   - pytest


=====================================
pyorbital/astronomy.py
=====================================
@@ -1,28 +1,42 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
-
+#
 # Copyright (c) 2011, 2013
-
+#
 # Author(s):
-
+#
 #   Martin Raspaud <martin.raspaud at smhi.se>
-
+#
 # This program 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.
-
+#
 # This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+"""Angle and time-based astronomy functions.
 
-"""Astronomy module.
 Parts taken from http://www.geoastro.de/elevaz/basics/index.htm
+
+Note on argument types
+----------------------
+
+Many of these functions accept Python datetime objects,
+numpy datetime64 objects, or anything that can be turned
+into a numpy array of datetime64 objects. These objects are inherently
+64-bit so if other arguments (ex. longitude and latitude arrays) are
+32-bit floats internal operations will be automatically promoted to
+64-bit floating point numbers. Where possible these are then converted
+back to 32-bit before being returned. In general scalar inputs will also
+produce scalar outputs.
+
 """
+import datetime
 
 import numpy as np
 
@@ -42,12 +56,14 @@ def jdays2000(utc_time):
 def jdays(utc_time):
     """Get the julian day of *utc_time*.
     """
-    return jdays2000(utc_time) + 2451545
+    return jdays2000(utc_time) + 2451545.0
 
 
 def _days(dt):
     """Get the days (floating point) from *d_t*.
     """
+    if hasattr(dt, "shape"):
+        dt = np.asanyarray(dt, dtype=np.timedelta64)
     return dt / np.timedelta64(1, 'D')
 
 
@@ -117,6 +133,7 @@ def _local_hour_angle(utc_time, longitude, right_ascension):
 
 def get_alt_az(utc_time, lon, lat):
     """Return sun altitude and azimuth from *utc_time*, *lon*, and *lat*.
+
     lon,lat in degrees
     The returned angles are given in radians.
     """
@@ -125,10 +142,13 @@ def get_alt_az(utc_time, lon, lat):
 
     ra_, dec = sun_ra_dec(utc_time)
     h__ = _local_hour_angle(utc_time, lon, ra_)
-    return (np.arcsin(np.sin(lat) * np.sin(dec) +
-                      np.cos(lat) * np.cos(dec) * np.cos(h__)),
-            np.arctan2(-np.sin(h__), (np.cos(lat) * np.tan(dec) -
-                                      np.sin(lat) * np.cos(h__))))
+    alt_az = (np.arcsin(np.sin(lat) * np.sin(dec) +
+                        np.cos(lat) * np.cos(dec) * np.cos(h__)),
+              np.arctan2(-np.sin(h__), (np.cos(lat) * np.tan(dec) -
+                                        np.sin(lat) * np.cos(h__))))
+    if not isinstance(lon, float):
+        alt_az = (alt_az[0].astype(lon.dtype), alt_az[1].astype(lon.dtype))
+    return alt_az
 
 
 def cos_zen(utc_time, lon, lat):
@@ -141,7 +161,10 @@ def cos_zen(utc_time, lon, lat):
 
     r_a, dec = sun_ra_dec(utc_time)
     h__ = _local_hour_angle(utc_time, lon, r_a)
-    return (np.sin(lat) * np.sin(dec) + np.cos(lat) * np.cos(dec) * np.cos(h__))
+    csza = (np.sin(lat) * np.sin(dec) + np.cos(lat) * np.cos(dec) * np.cos(h__))
+    if not isinstance(lon, float):
+        csza = csza.astype(lon.dtype)
+    return csza
 
 
 def sun_zenith_angle(utc_time, lon, lat):
@@ -149,13 +172,15 @@ def sun_zenith_angle(utc_time, lon, lat):
     lon,lat in degrees.
     The angle returned is given in degrees
     """
-    return np.rad2deg(np.arccos(cos_zen(utc_time, lon, lat)))
+    sza = np.rad2deg(np.arccos(cos_zen(utc_time, lon, lat)))
+    if not isinstance(lon, float):
+        sza = sza.astype(lon.dtype)
+    return sza
 
 
 def sun_earth_distance_correction(utc_time):
     """Calculate the sun earth distance correction, relative to 1 AU.
     """
-
     # Computation according to
     #  https://web.archive.org/web/20150117190838/http://curious.astro.cornell.edu/question.php?number=582
     # with
@@ -175,11 +200,10 @@ def sun_earth_distance_correction(utc_time):
     #       "=" 1 - 0.0167 * np.cos(theta)
 
     corr = 1 - 0.0167 * np.cos(2 * np.pi * (jdays2000(utc_time) - 3) / 365.25636)
-
     return corr
 
 
-def observer_position(time, lon, lat, alt):
+def observer_position(utc_time, lon, lat, alt):
     """Calculate observer ECI position.
 
     http://celestrak.com/columns/v02n03/
@@ -188,7 +212,7 @@ def observer_position(time, lon, lat, alt):
     lon = np.deg2rad(lon)
     lat = np.deg2rad(lat)
 
-    theta = (gmst(time) + lon) % (2 * np.pi)
+    theta = (gmst(utc_time) + lon) % (2 * np.pi)
     c = 1 / np.sqrt(1 + F * (F - 2) * np.sin(lat)**2)
     sq = c * (1 - F)**2
 
@@ -199,6 +223,32 @@ def observer_position(time, lon, lat, alt):
 
     vx = -MFACTOR * y  # kilometers/second
     vy = MFACTOR * x
-    vz = 0
-
+    vz = _float_to_sibling_result(0.0, vx)
+
+    if not isinstance(lon, float):
+        x = x.astype(lon.dtype, copy=False)
+        y = y.astype(lon.dtype, copy=False)
+        z = z.astype(lon.dtype, copy=False)
+        vx = vx.astype(lon.dtype, copy=False)
+        vy = vy.astype(lon.dtype, copy=False)
+        vz = vz.astype(lon.dtype, copy=False)  # type: ignore[union-attr]
     return (x, y, z), (vx, vy, vz)
+
+
+def _float_to_sibling_result(result_to_convert, template_result):
+    """Convert a scalar to the same type as another return type.
+
+    This is mostly used to make a static value consistent with the types of
+    other returned values.
+
+    """
+    if isinstance(template_result, float):
+        return result_to_convert
+    # get any array like object that might be wrapped by our template (ex. xarray DataArray)
+    array_like = template_result if hasattr(template_result, "__array_function__") else template_result.data
+    array_convert = np.asarray(result_to_convert, like=array_like)
+    if not hasattr(template_result, "__array_function__"):
+        # the template result has some wrapper class (likely xarray DataArray)
+        # recreate the wrapper object
+        array_convert = template_result.__class__(array_convert)
+    return array_convert


=====================================
pyorbital/tests/__init__.py
=====================================
@@ -20,25 +20,3 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 """The tests package."""
-
-from pyorbital.tests import (test_aiaa, test_tlefile, test_orbital,
-                             test_astronomy, test_geoloc)
-import unittest
-
-
-def suite():
-    """The global test suite."""
-    mysuite = unittest.TestSuite()
-    # Test the documentation strings
-    # mysuite.addTests(doctest.DocTestSuite(image))
-    # Use the unittests also
-    mysuite.addTests(test_aiaa.suite())
-    mysuite.addTests(test_tlefile.suite())
-    mysuite.addTests(test_orbital.suite())
-    mysuite.addTests(test_astronomy.suite())
-    mysuite.addTests(test_geoloc.suite())
-    return mysuite
-
-
-if __name__ == '__main__':
-    unittest.TextTestRunner(verbosity=2).run(suite())


=====================================
pyorbital/tests/test_astronomy.py
=====================================
@@ -20,39 +20,97 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import unittest
-
 from datetime import datetime
+
+import dask.array as da
+import numpy as np
+import numpy.typing as npt
+import pytest
+
 import pyorbital.astronomy as astr
 
+try:
+    from xarray import DataArray
+except ImportError:
+    DataArray = None
+
+
+def _create_dask_array(input_list: list, dtype: npt.DTypeLike) -> da.Array:
+    np_arr = np.array(input_list, dtype=dtype)
+    return da.from_array(np_arr)
+
 
-class TestAstronomy(unittest.TestCase):
+def _create_xarray_numpy(input_list: list, dtype: npt.DTypeLike) -> DataArray:
+    np_arr = np.array(input_list, dtype=dtype)
+    return DataArray(np_arr)
 
-    def setUp(self):
-        pass
 
-    def test_jdays(self):
+def _create_xarray_dask(input_list: list, dtype: npt.DTypeLike) -> DataArray:
+    dask_arr = _create_dask_array(input_list, dtype)
+    return DataArray(dask_arr)
+
+
+class TestAstronomy:
+
+    @pytest.mark.parametrize(
+        ("dt", "exp_jdays", "exp_j2000"),
+        [
+            (datetime(2000, 1, 1, 12, 0), 2451545.0, 0),
+            (datetime(2009, 10, 8, 14, 30), 2455113.1041666665, 3568.1041666666665),
+        ]
+    )
+    def test_jdays(self, dt, exp_jdays, exp_j2000):
         """Test julian day functions."""
-        t = datetime(2000, 1, 1, 12, 0)
-        self.assertEqual(astr.jdays(t), 2451545.0)
-        self.assertEqual(astr.jdays2000(t), 0)
-        t = datetime(2009, 10, 8, 14, 30)
-        self.assertEqual(astr.jdays(t), 2455113.1041666665)
-        self.assertEqual(astr.jdays2000(t), 3568.1041666666665)
-
-    def test_sunangles(self):
+        assert astr.jdays(dt) == exp_jdays
+        assert astr.jdays2000(dt) == exp_j2000
+
+    @pytest.mark.parametrize(
+        ("lon", "lat", "exp_theta"),
+        [
+            # Norrkoping
+            (16.1833, 58.6167, 60.371433482557833),
+            (0.0, 0.0, 1.8751916863323426),
+        ]
+    )
+    @pytest.mark.parametrize(
+        ("dtype", "array_construct"),
+        [
+            (None, None),
+            (np.float32, np.array),
+            (np.float64, np.array),
+            (np.float32, _create_dask_array),
+            (np.float64, _create_dask_array),
+            (np.float32, _create_xarray_numpy),
+            (np.float64, _create_xarray_numpy),
+            (np.float32, _create_xarray_dask),
+            (np.float64, _create_xarray_dask),
+        ]
+    )
+    def test_sunangles(self, lon, lat, exp_theta, dtype, array_construct):
         """Test the sun-angle calculations."""
-        lat, lon = 58.6167, 16.1833  # Norrkoping
+        if array_construct is None and dtype is not None:
+            pytest.skip(reason="Xarray dependency unavailable")
+
         time_slot = datetime(2011, 9, 23, 12, 0)
+        abs_tolerance = 1e-8
+        if dtype is not None:
+            lon = array_construct([lon], dtype=dtype)
+            lat = array_construct([lat], dtype=dtype)
+            if np.dtype(dtype).itemsize < 8:
+                abs_tolerance = 1e-4
 
         sun_theta = astr.sun_zenith_angle(time_slot, lon, lat)
-        self.assertAlmostEqual(sun_theta, 60.371433482557833, places=8)
-        sun_theta = astr.sun_zenith_angle(time_slot, 0., 0.)
-        self.assertAlmostEqual(sun_theta, 1.8751916863323426, places=8)
+        if dtype is None:
+            assert sun_theta == pytest.approx(exp_theta, abs=abs_tolerance)
+            assert isinstance(sun_theta, float)
+        else:
+            assert sun_theta.dtype == dtype
+            np.testing.assert_allclose(sun_theta, exp_theta, atol=abs_tolerance)
+            assert isinstance(sun_theta, type(lon))
 
     def test_sun_earth_distance_correction(self):
         """Test the sun-earth distance correction."""
         utc_time = datetime(2022, 6, 15, 12, 0, 0)
         corr = astr.sun_earth_distance_correction(utc_time)
         corr_exp = 1.0156952156742332
-        self.assertAlmostEqual(corr, corr_exp, places=8)
+        assert corr == pytest.approx(corr_exp, abs=1e-8)


=====================================
pyorbital/tests/test_orbital.py
=====================================
@@ -24,10 +24,7 @@
 """
 
 import unittest
-try:
-    from unittest import mock
-except ImportError:
-    import mock
+from unittest import mock
 from datetime import datetime, timedelta
 
 import numpy as np


=====================================
pyorbital/version.py
=====================================
@@ -26,9 +26,9 @@ def get_keywords():
     # setup.py/versioneer.py will grep for the variable names, so they must
     # each be defined on a line of their own. _version.py will just call
     # get_keywords().
-    git_refnames = " (tag: v1.8.2)"
-    git_full = "18340caf3a5ac94afad70b1a0016c3a8b294124c"
-    git_date = "2024-02-05 15:54:10 +0100"
+    git_refnames = " (HEAD -> main, tag: v1.8.3)"
+    git_full = "5b4f33698773d89a2224c39c22ced639b34b039d"
+    git_date = "2024-06-25 21:13:20 -0500"
     keywords = {"refnames": git_refnames, "full": git_full, "date": git_date}
     return keywords
 



View it on GitLab: https://salsa.debian.org/debian-gis-team/pyorbital/-/commit/f294445a0274290cd6f275497730a86f1f6295f1

-- 
This project does not include diff previews in email notifications.
View it on GitLab: https://salsa.debian.org/debian-gis-team/pyorbital/-/commit/f294445a0274290cd6f275497730a86f1f6295f1
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/20240629/aaeb0c54/attachment-0001.htm>


More information about the Pkg-grass-devel mailing list