[Git][debian-gis-team/python-geotiepoints][master] 7 commits: New upstream version 1.7.0

Antonio Valentino (@antonio.valentino) gitlab at salsa.debian.org
Tue Dec 26 07:53:01 GMT 2023



Antonio Valentino pushed to branch master at Debian GIS Project / python-geotiepoints


Commits:
2e9e4afc by Antonio Valentino at 2023-11-22T07:24:29+00:00
New upstream version 1.7.0
- - - - -
af632334 by Antonio Valentino at 2023-11-22T07:24:54+00:00
Update upstream source from tag 'upstream/1.7.0'

Update to upstream version '1.7.0'
with Debian dir bb112ea09fb07ef143e3245fd5d888e89beaa272
- - - - -
1bfe5952 by Antonio Valentino at 2023-11-22T07:26:20+00:00
New upstream release

- - - - -
0a73f14c by Antonio Valentino at 2023-11-22T07:33:05+00:00
Update d/copyright

- - - - -
814b684a by Antonio Valentino at 2023-11-22T07:44:51+00:00
Refresh patches

- - - - -
c14c2898 by Antonio Valentino at 2023-12-26T07:51:38+00:00
Build-depend on cython3 >= 3.0

- - - - -
b594f808 by Antonio Valentino at 2023-12-26T07:51:59+00:00
Set distribution to unstable

- - - - -


29 changed files:

- + .github/dependabot.yml
- .github/workflows/ci.yaml
- .github/workflows/deploy.yaml
- CHANGELOG.md
- continuous_integration/environment.yaml
- + cython_test.pyx
- + cython_test2.pyx
- debian/changelog
- debian/control
- debian/copyright
- debian/patches/0001-Skip-tests-using-external-data.patch
- − debian/patches/0002-Fix-tests-on-i386-arch.patch
- debian/patches/series
- doc/source/conf.py
- geotiepoints/__init__.py
- geotiepoints/_modis_interpolator.pyx
- geotiepoints/_modis_utils.pxd
- geotiepoints/_modis_utils.pyx
- geotiepoints/_simple_modis_interpolator.pyx
- geotiepoints/interpolator.py
- geotiepoints/modisinterpolator.py
- geotiepoints/multilinear.py
- geotiepoints/multilinear_cython.pyx
- geotiepoints/tests/__init__.py
- geotiepoints/tests/test_geointerpolator.py
- geotiepoints/tests/test_interpolator.py
- geotiepoints/version.py
- pyproject.toml
- setup.py


Changes:

=====================================
.github/dependabot.yml
=====================================
@@ -0,0 +1,11 @@
+# To get started with Dependabot version updates, you'll need to specify which
+# package ecosystems to update and where the package manifests are located.
+# Please see the documentation for all configuration options:
+# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
+
+version: 2
+updates:
+  - package-ecosystem: "github-actions" # See documentation for possible values
+    directory: "/" # Location of package manifests
+    schedule:
+      interval: "weekly"


=====================================
.github/workflows/ci.yaml
=====================================
@@ -7,13 +7,13 @@ jobs:
     runs-on: ${{ matrix.os }}
     continue-on-error: ${{ matrix.experimental }}
     strategy:
-      fail-fast: true
+      fail-fast: false
       matrix:
         os: ["windows-latest", "ubuntu-latest", "macos-latest"]
-        python-version: ["3.8", "3.9", "3.10"]
+        python-version: ["3.9", "3.11", "3.12"]
         experimental: [false]
         include:
-          - python-version: "3.9"
+          - python-version: "3.12"
             os: "ubuntu-latest"
             experimental: true
 
@@ -25,17 +25,18 @@ jobs:
 
     steps:
       - name: Checkout source
-        uses: actions/checkout at v2
+        uses: actions/checkout at v4
 
       - name: Setup Conda Environment
         uses: conda-incubator/setup-miniconda at v2
         with:
-          miniconda-version: "latest"
+          miniforge-variant: Mambaforge
+          miniforge-version: latest
+          use-mamba: true
+          channel-priority: strict
           python-version: ${{ matrix.python-version }}
-          mamba-version: "*"
-          channels: conda-forge,defaults
-          environment-file: continuous_integration/environment.yaml
           activate-environment: test-environment
+          environment-file: continuous_integration/environment.yaml
 
       - name: Install unstable dependencies
         if: matrix.experimental == true
@@ -74,15 +75,14 @@ jobs:
 #          cd doc && mkdir doctest && sphinx-build -E -n -b doctest ./source ./doctest && cd ..
 
       - name: Upload unittest coverage to Codecov
-        uses: codecov/codecov-action at v1
+        uses: codecov/codecov-action at v3
         with:
           flags: unittests
           file: ./coverage.xml
           env_vars: OS,PYTHON_VERSION,UNSTABLE
 
       - name: Coveralls Parallel
-        # See https://github.com/AndreMiras/coveralls-python-action/pull/16
-        uses: djhoese/coveralls-python-action at feature-cython-coverage
+        uses: AndreMiras/coveralls-python-action at develop
         with:
           flag-name: run-${{ matrix.test_number }}
           parallel: true
@@ -93,8 +93,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Coveralls Finished
-        # See https://github.com/AndreMiras/coveralls-python-action/pull/16
-        uses: djhoese/coveralls-python-action at feature-cython-coverage
+        uses: AndreMiras/coveralls-python-action at develop
         with:
           parallel-finished: true
 


=====================================
.github/workflows/deploy.yaml
=====================================
@@ -13,7 +13,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Checkout source
-        uses: actions/checkout at v2
+        uses: actions/checkout at v4
 
       - name: Create sdist
         shell: bash -l {0}
@@ -22,7 +22,7 @@ jobs:
           python -m build -s
 
       - name: Upload sdist to build artifacts
-        uses: actions/upload-artifact at v2
+        uses: actions/upload-artifact at v3
         with:
           name: sdist
           path: dist/*.tar.gz
@@ -44,12 +44,12 @@ jobs:
             docker-image: manylinux2014_x86_64
 
     steps:
-      - uses: actions/checkout at v2
+      - uses: actions/checkout at v4
       - run: |
           git fetch --prune --unshallow
 
       - name: Set up Python ${{ matrix.python-version }}
-        uses: actions/setup-python at v1
+        uses: actions/setup-python at v4
         with:
           python-version: "${{ matrix.python-version }}"
 
@@ -83,7 +83,7 @@ jobs:
           python -c "import geotiepoints; assert 'unknown' not in geotiepoints.__version__, 'incorrect version found'"
 
       - name: Upload wheel(s) as build artifacts
-        uses: actions/upload-artifact at v2
+        uses: actions/upload-artifact at v3
         with:
           name: wheels
           path: dist/*.whl
@@ -93,18 +93,18 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Download sdist artifact
-        uses: actions/download-artifact at v2
+        uses: actions/download-artifact at v3
         with:
           name: sdist
           path: dist
       - name: Download wheels artifact
-        uses: actions/download-artifact at v2
+        uses: actions/download-artifact at v3
         with:
           name: wheels
           path: dist
       - name: Publish package to PyPI
         if: github.event.action != 'published'
-        uses: pypa/gh-action-pypi-publish at v1.4.1
+        uses: pypa/gh-action-pypi-publish at v1.8.10
         with:
           user: __token__
           password: ${{ secrets.test_pypi_password }}
@@ -115,18 +115,18 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Download sdist artifact
-        uses: actions/download-artifact at v2
+        uses: actions/download-artifact at v3
         with:
           name: sdist
           path: dist
       - name: Download wheels artifact
-        uses: actions/download-artifact at v2
+        uses: actions/download-artifact at v3
         with:
           name: wheels
           path: dist
       - name: Publish package to PyPI
         if: github.event.action == 'published'
-        uses: pypa/gh-action-pypi-publish at v1.4.1
+        uses: pypa/gh-action-pypi-publish at v1.8.10
         with:
           user: __token__
           password: ${{ secrets.pypi_password }}


=====================================
CHANGELOG.md
=====================================
@@ -1,3 +1,30 @@
+## Version 1.7.0 (2023/11/21)
+
+### Issues Closed
+
+* [Issue 56](https://github.com/pytroll/python-geotiepoints/issues/56) - Upgrade to Cython 3.0 and check annotations ([PR 57](https://github.com/pytroll/python-geotiepoints/pull/57) by [@djhoese](https://github.com/djhoese))
+* [Issue 47](https://github.com/pytroll/python-geotiepoints/issues/47) - Help wanted: verify the interpolation of MERSI-2 1000M GEO to 250M GEO
+* [Issue 23](https://github.com/pytroll/python-geotiepoints/issues/23) - Docstring headers still include authors.
+* [Issue 21](https://github.com/pytroll/python-geotiepoints/issues/21) - Interpolation of MODIS lat/lons is incorrect
+* [Issue 18](https://github.com/pytroll/python-geotiepoints/issues/18) - Make the interpolators dask-compatible
+
+In this release 5 issues were closed.
+
+### Pull Requests Merged
+
+#### Bugs fixed
+
+* [PR 60](https://github.com/pytroll/python-geotiepoints/pull/60) - Add missing noexcept on cython function
+* [PR 46](https://github.com/pytroll/python-geotiepoints/pull/46) - Fix tests on i386 architectures
+
+#### Features added
+
+* [PR 61](https://github.com/pytroll/python-geotiepoints/pull/61) - Fix geogrid chunking to accept "auto" and to preserve dtype
+* [PR 57](https://github.com/pytroll/python-geotiepoints/pull/57) - Upgrade to Cython 3+ in building ([56](https://github.com/pytroll/python-geotiepoints/issues/56))
+
+In this release 4 pull requests were closed.
+
+
 ## Version 1.6.0 (2023/03/17)
 
 


=====================================
continuous_integration/environment.yaml
=====================================
@@ -4,18 +4,15 @@ channels:
 dependencies:
   - xarray
   - dask
-  - distributed
-  - toolz
   - Cython
   - sphinx
   - pillow
   - pyyaml
   - coveralls
   - coverage
-  - codecov
   - scipy
   - h5py
   - pytest
   - pytest-cov
-  - pyproj
+  - pyproj >=3
   - pyresample


=====================================
cython_test.pyx
=====================================
@@ -0,0 +1,17 @@
+# cython: language_level=3, boundscheck=False, cdivision=True, wraparound=False, initializedcheck=False, nonecheck=False
+cimport cython
+import numpy as np
+cimport numpy as np
+
+np.import_array()
+
+def test_func():
+    cdef np.ndarray[float, ndim=2] arr = np.zeros((5, 5), dtype=np.float32)
+    cdef float[:, ::1] arr_view = arr
+    _run(arr_view)
+
+cdef void _run(float[:, ::1] arr_view) noexcept nogil:
+    cdef float[:, :] tmp = _get_upper_left_corner(arr_view)
+
+cdef inline float[:, :] _get_upper_left_corner(float[:, ::1] arr) noexcept nogil:
+    return arr[:1, :1]


=====================================
cython_test2.pyx
=====================================
@@ -0,0 +1,34 @@
+# cython: language_level=3, boundscheck=False, cdivision=True, wraparound=False, initializedcheck=False, nonecheck=False
+cimport cython
+import numpy as np
+cimport numpy as np
+
+np.import_array()
+
+def test_func():
+    cdef np.ndarray[float, ndim=2] arr = np.zeros((5, 5), dtype=np.float32)
+    cdef float[:, ::1] arr_view = arr
+    t = Test(5.0)
+    t.call_me(arr_view)
+
+
+cdef class Test:
+
+    cdef float _a
+
+    def __cinit__(self, float a):
+        self._a = a
+
+    cdef void call_me(self, float[:, ::1] my_arr) noexcept:
+        with nogil:
+            self._call_me(my_arr)
+
+    cdef void _call_me(self, float[:, ::1] my_arr) noexcept nogil:
+        cdef Py_ssize_t idx
+        cdef float[:, :] my_arr2 = _get_upper_left_corner(my_arr)
+        for idx in range(my_arr2.shape[0]):
+            my_arr2[idx, 0] = self._a
+
+
+cdef inline float[:, :] _get_upper_left_corner(float[:, ::1] arr) noexcept nogil:
+    return arr[:3, :3]


=====================================
debian/changelog
=====================================
@@ -1,9 +1,18 @@
-python-geotiepoints (1.6.0-3) UNRELEASED; urgency=medium
+python-geotiepoints (1.7.0-1) unstable; urgency=medium
 
-  * Team upload.
+  [ Bas Couwenberg ]
   * Switch to dh-sequence-*.
 
- -- Bas Couwenberg <sebastic at debian.org>  Wed, 30 Aug 2023 18:34:22 +0200
+  [ Antonio Valentino ]
+  * New upstream release.
+  * Update d/copyright.
+  * debian/patches:
+    - Drop 0002-Fix-tests-on-i386-arch.patch, applied upstream.
+    - Refresh remaining patches.
+  * debian/control:
+    - Build-depend on cython3 >= 3.0.
+
+ -- Antonio Valentino <antonio.valentino at tiscali.it>  Tue, 26 Dec 2023 07:51:44 +0000
 
 python-geotiepoints (1.6.0-2) unstable; urgency=medium
 


=====================================
debian/control
=====================================
@@ -9,7 +9,7 @@ Build-Depends: debhelper-compat (= 13),
                dh-python,
                dh-sequence-numpy3,
                dh-sequence-python3,
-               cython3,
+               cython3 (>= 3.0),
                pybuild-plugin-pyproject,
                python3-all-dev,
                python3-dask,


=====================================
debian/copyright
=====================================
@@ -4,12 +4,8 @@ Upstream-Contact: Adam Dybbroe, Martin Raspaud <martin.raspaud at smhi.se>
 Source: https://github.com/pytroll/python-geotiepoints
 
 Files: *
-Copyright: 2010-2022, PyTroll Team
-                      Martin Raspaud <martin.raspaud at smhi.se>
+Copyright: 2010-2023, Python-geotiepoints developers
                       Adam Dybbroe <adam.dybbroe at smhi.se>
-                      Panu Lahtinen <panu.lahtinen at fmi.fi>
-                      PyTroll community
-                      Python-geotiepoints developers
 License: GPL-3+
 
 Files: geotiepoints/version.py


=====================================
debian/patches/0001-Skip-tests-using-external-data.patch
=====================================
@@ -7,8 +7,7 @@ Forwarded: not-needed
  geotiepoints/tests/test_modis.py                     | 5 +++++
  geotiepoints/tests/test_modisinterpolator.py         | 3 +++
  geotiepoints/tests/test_simple_modis_interpolator.py | 7 +++++++
- setup.py                                             | 2 +-
- 4 files changed, 16 insertions(+), 1 deletion(-)
+ 3 files changed, 15 insertions(+)
 
 diff --git a/geotiepoints/tests/test_modis.py b/geotiepoints/tests/test_modis.py
 index c039683..1f52c76 100644
@@ -100,16 +99,3 @@ index 3af03ef..34ccc24 100644
  def test_nonstandard_scan_size():
      lon1, lat1 = load_1km_lonlat_as_xarray_dask()
      # remove 1 row from the end
-diff --git a/setup.py b/setup.py
-index 79c78dd..8dec19c 100644
---- a/setup.py
-+++ b/setup.py
-@@ -113,7 +113,7 @@ if __name__ == "__main__":
-                        "Programming Language :: Cython",
-                        "Topic :: Scientific/Engineering"],
-           url="https://github.com/pytroll/python-geotiepoints",
--          packages=['geotiepoints'],
-+          packages=['geotiepoints', 'geotiepoints.tests'],
-           # packages=find_packages(),
-           setup_requires=['numpy', 'cython'],
-           python_requires='>=3.7',


=====================================
debian/patches/0002-Fix-tests-on-i386-arch.patch deleted
=====================================
@@ -1,35 +0,0 @@
-From: Antonio Valentino <antonio.valentino at tiscali.it>
-Date: Mon, 12 Jun 2023 06:29:36 +0000
-Subject: Fix tests on i386 arch
-
-Forwarded: https://github.com/pytroll/python-geotiepoints/pull/46
----
- geotiepoints/tests/test_interpolator.py | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
-diff --git a/geotiepoints/tests/test_interpolator.py b/geotiepoints/tests/test_interpolator.py
-index 156d4cc..0ed409b 100644
---- a/geotiepoints/tests/test_interpolator.py
-+++ b/geotiepoints/tests/test_interpolator.py
-@@ -349,12 +349,12 @@ class TestSingleGridInterpolator:
-         fine_y = np.arange(32)
- 
-         res = grid_interpolator.interpolate((fine_y, fine_x), method="cubic")
--        np.testing.assert_allclose(res, self.expected)
-+        np.testing.assert_allclose(res, self.expected, atol=2e-9)
- 
-     def test_interpolate_slices(self, grid_interpolator):
-         """Test that interpolation from slices is working."""
-         res = grid_interpolator.interpolate_slices((slice(0, 32), slice(0, 16)), method="cubic")
--        np.testing.assert_allclose(res, self.expected)
-+        np.testing.assert_allclose(res, self.expected, atol=2e-9)
- 
-     @pytest.mark.parametrize("chunks, expected_chunks", [(10, (10, 10)),
-                                                          ((10, 5), (10, 5))])
-@@ -376,5 +376,5 @@ class TestSingleGridInterpolator:
-             assert res.chunks[0][0] == v_chunk
-             assert res.chunks[1][0] == h_chunk
- 
--            np.testing.assert_allclose(res, self.expected)
-+            np.testing.assert_allclose(res, self.expected, atol=2e-9)
-             assert interpolate.called


=====================================
debian/patches/series
=====================================
@@ -1,2 +1 @@
 0001-Skip-tests-using-external-data.patch
-0002-Fix-tests-on-i386-arch.patch


=====================================
doc/source/conf.py
=====================================
@@ -74,7 +74,7 @@ master_doc = 'index'
 
 # General information about the project.
 project = u'python-geotiepoints'
-copyright = u'2012-%d, Pytroll Team' % datetime.utcnow().year  # noqa
+copyright = u'2012-%d, Python-geotiepoints Developers' % datetime.utcnow().year  # noqa
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the


=====================================
geotiepoints/__init__.py
=====================================
@@ -1,29 +1,18 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-# Copyright (c) 2010-2018.
-
-# Author(s):
-
-#   Adam Dybbroe <adam.dybbroe at smhise>
-#   Martin Raspaud <martin.raspaud at smhi.se>
-#   Panu Lahtinen <panu.lahtinen at fmi.fi>
-
+# Copyright (c) 2010-2018 Python-geotiepoints developers
+#
 # 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/>.
-
-"""Interpolation of geographical tiepoints.
-"""
+"""Interpolation of geographical tiepoints."""
 
 from multiprocessing import Pool
 


=====================================
geotiepoints/_modis_interpolator.pyx
=====================================
@@ -1,3 +1,4 @@
+# cython: language_level=3, boundscheck=False, cdivision=True, wraparound=False, initializedcheck=False, nonecheck=False
 cimport cython
 from ._modis_utils cimport lonlat2xyz, xyz2lonlat, floating, deg2rad
 from .simple_modis_interpolator import scanline_mapblocks
@@ -10,6 +11,8 @@ DEF R = 6370.997
 # Aqua altitude in km
 DEF H = 709.0
 
+np.import_array()
+
 
 @scanline_mapblocks
 def interpolate(
@@ -35,12 +38,8 @@ def interpolate(
     return interp.interpolate(lon1, lat1, satz1)
 
 
- at cython.boundscheck(False)
- at cython.cdivision(True)
- at cython.wraparound(False)
- at cython.initializedcheck(False)
 cdef void _compute_expansion_alignment(floating[:, :] satz_a, floating [:, :] satz_b, int scan_width,
-                                       floating[:, ::1] c_expansion, floating[:, ::1] c_alignment) nogil:
+                                       floating[:, ::1] c_expansion, floating[:, ::1] c_alignment) noexcept nogil:
     """Fill in expansion and alignment.
     
     Input angles should be in degrees and will be converted to radians.
@@ -71,48 +70,24 @@ cdef void _compute_expansion_alignment(floating[:, :] satz_a, floating [:, :] sa
             c_alignment[i, j] = 4 * e * sin(zeta) / denominator
 
 
-cdef inline floating _compute_phi(floating zeta) nogil:
+cdef inline floating _compute_phi(floating zeta) noexcept nogil:
     return asin(R * sin(zeta) / (R + H))
 
 
-cdef inline floating _compute_theta(floating zeta, floating phi) nogil:
+cdef inline floating _compute_theta(floating zeta, floating phi) noexcept nogil:
     return zeta - phi
 
 
-cdef inline floating _compute_zeta(floating phi) nogil:
+cdef inline floating _compute_zeta(floating phi) noexcept nogil:
     return asin((R + H) * sin(phi) / R)
 
 
- at cython.boundscheck(False)
- at cython.cdivision(True)
- at cython.wraparound(False)
- at cython.initializedcheck(False)
-cdef inline floating[:, :] _get_upper_left_corner(floating[:, ::1] arr) nogil:
-    return arr[:-1, :-1]
-
-
- at cython.boundscheck(False)
- at cython.cdivision(True)
- at cython.wraparound(False)
- at cython.initializedcheck(False)
-cdef inline floating[:, :] _get_upper_right_corner(floating[:, ::1] arr) nogil:
-    return arr[:-1, 1:]
-
-
- at cython.boundscheck(False)
- at cython.cdivision(True)
- at cython.wraparound(False)
- at cython.initializedcheck(False)
-cdef inline floating[:, :] _get_lower_right_corner(floating[:, ::1] arr) nogil:
-    return arr[1:, 1:]
+cdef inline floating[:, :] _get_upper_left_corner(floating[:, ::1] arr) noexcept nogil:
+    return arr[:arr.shape[0] - 1, :arr.shape[1] - 1]
 
 
- at cython.boundscheck(False)
- at cython.cdivision(True)
- at cython.wraparound(False)
- at cython.initializedcheck(False)
-cdef inline floating[:, :] _get_lower_left_corner(floating[:, ::1] arr) nogil:
-    return arr[1:, :-1]
+cdef inline floating[:, :] _get_upper_right_corner(floating[:, ::1] arr) noexcept nogil:
+    return arr[:arr.shape[0] - 1, 1:]
 
 
 cdef class MODISInterpolator:
@@ -132,8 +107,6 @@ cdef class MODISInterpolator:
     cdef int _fine_resolution
     cdef Py_ssize_t _factor_5km
 
-    @cython.cdivision(True)
-    @cython.wraparound(False)
     def __cinit__(self, unsigned int coarse_resolution, unsigned int fine_resolution, unsigned int coarse_scan_width=0):
         if coarse_resolution == 1000:
             self._coarse_scan_length = 10
@@ -154,11 +127,7 @@ cdef class MODISInterpolator:
         # partial rows/columns to repeat: 5km->1km => 2, 5km->500m => 4, 5km->250m => 8
         self._factor_5km = self._fine_pixels_per_coarse_pixel // self._coarse_pixels_per_1km * 2
 
-    @cython.boundscheck(False)
-    @cython.cdivision(True)
-    @cython.wraparound(False)
-    @cython.initializedcheck(False)
-    cdef interpolate(
+    cdef tuple interpolate(
             self,
             np.ndarray[floating, ndim=2] lon1,
             np.ndarray[floating, ndim=2] lat1,
@@ -238,15 +207,11 @@ cdef class MODISInterpolator:
             x, y = self._get_coords_5km()
         return x, y
 
-    @cython.boundscheck(False)
-    @cython.cdivision(True)
-    @cython.wraparound(False)
-    @cython.initializedcheck(False)
     cdef void _get_coords_1km(
             self,
             floating[::1] x_view,
             floating[::1] y_view,
-    ) nogil:
+    ) noexcept nogil:
         cdef unsigned int scan_idx
         cdef int i
         cdef int fine_idx
@@ -263,9 +228,7 @@ cdef class MODISInterpolator:
         for i in range(self._fine_pixels_per_coarse_pixel):
             x_view[(self._fine_scan_width - self._fine_pixels_per_coarse_pixel) + i] = self._fine_pixels_per_coarse_pixel + i
 
-    @cython.boundscheck(False)
-    @cython.cdivision(True)
-    @cython.initializedcheck(False)
+    @cython.wraparound(True)
     cdef tuple _get_coords_5km(self):
         cdef np.ndarray[np.float32_t, ndim=1] y = np.arange(self._fine_pixels_per_coarse_pixel * self._coarse_scan_length, dtype=np.float32) - 2
         cdef np.ndarray[np.float32_t, ndim=1] x = (np.arange(self._fine_scan_width, dtype=np.float32) - 2) % self._fine_pixels_per_coarse_pixel
@@ -285,10 +248,6 @@ cdef class MODISInterpolator:
             x[-1] = 11
         return x, y
 
-    @cython.boundscheck(False)
-    @cython.cdivision(True)
-    @cython.wraparound(False)
-    @cython.initializedcheck(False)
     cdef void _interpolate_lons_lats(self,
                                      unsigned int scans,
                                      floating[:, ::1] coarse_lons,
@@ -306,7 +265,7 @@ cdef class MODISInterpolator:
                                      floating[:, :, ::1] fine_xyz,
                                      floating[:, ::1] fine_lons,
                                      floating[:, ::1] fine_lats,
-                                     ) nogil:
+                                     ) noexcept nogil:
         cdef floating[:, ::1] lons_scan, lats_scan, satz_scan
         cdef floating[:, ::1] new_lons_scan, new_lats_scan
         cdef Py_ssize_t scan_idx
@@ -333,10 +292,6 @@ cdef class MODISInterpolator:
 
             xyz2lonlat(fine_xyz, new_lons_scan, new_lats_scan)
 
-    @cython.boundscheck(False)
-    @cython.cdivision(True)
-    @cython.wraparound(False)
-    @cython.initializedcheck(False)
     cdef void _get_atrack_ascan(
             self,
             floating[:, ::1] satz,
@@ -348,7 +303,7 @@ cdef class MODISInterpolator:
             floating[:, ::1] c_ali_fine,
             floating[:, ::1] a_track,
             floating[:, ::1] a_scan
-    ) nogil:
+    ) noexcept nogil:
         cdef floating[:, :] satz_a_view = _get_upper_left_corner(satz)
         cdef floating[:, :] satz_b_view = _get_upper_right_corner(satz)
         cdef Py_ssize_t scan_idx
@@ -370,10 +325,6 @@ cdef class MODISInterpolator:
             c_exp_fine, c_ali_fine,
             a_track, a_scan)
 
-    @cython.boundscheck(False)
-    @cython.cdivision(True)
-    @cython.wraparound(False)
-    @cython.initializedcheck(False)
     cdef void _calculate_atrack_ascan(
             self,
             floating[::1] coords_x,
@@ -382,7 +333,7 @@ cdef class MODISInterpolator:
             floating[:, ::1] c_ali_full,
             floating[:, ::1] a_track,
             floating[:, ::1] a_scan,
-    ) nogil:
+    ) noexcept nogil:
         cdef Py_ssize_t i, j
         cdef floating s_s, s_t
         for j in range(coords_y.shape[0]):
@@ -392,10 +343,6 @@ cdef class MODISInterpolator:
                 a_track[j, i] = s_t
                 a_scan[j, i] = s_s + s_s * (1 - s_s) * c_exp_full[j, i] + s_t * (1 - s_t) * c_ali_full[j, i]
 
-    @cython.boundscheck(False)
-    @cython.cdivision(True)
-    @cython.wraparound(False)
-    @cython.initializedcheck(False)
     cdef void _compute_fine_xyz(
             self,
             floating[:, ::1] a_track_view,
@@ -406,14 +353,14 @@ cdef class MODISInterpolator:
             floating[:, ::1] data_tiepoint_c_view,
             floating[:, ::1] data_tiepoint_d_view,
             floating[:, :, ::1] xyz_comp_view,
-    ) nogil:
+    ) noexcept nogil:
         cdef Py_ssize_t k
         cdef floating[:, :] comp_a_view, comp_b_view, comp_c_view, comp_d_view
         for k in range(3):  # xyz
-            comp_a_view = xyz_view[:-1, :-1, k]  # upper left
-            comp_b_view = xyz_view[:-1, 1:, k]  # upper right
+            comp_a_view = xyz_view[:xyz_view.shape[0] - 1, :xyz_view.shape[1] - 1, k]  # upper left
+            comp_b_view = xyz_view[:xyz_view.shape[0] - 1, 1:, k]  # upper right
             comp_c_view = xyz_view[1:, 1:, k]  # lower right
-            comp_d_view = xyz_view[1:, :-1, k]  # lower left
+            comp_d_view = xyz_view[1:, :xyz_view.shape[1] - 1, k]  # lower left
             self._expand_tiepoint_array(comp_a_view, data_tiepoint_a_view)
             self._expand_tiepoint_array(comp_b_view, data_tiepoint_b_view)
             self._expand_tiepoint_array(comp_c_view, data_tiepoint_c_view)
@@ -429,10 +376,6 @@ cdef class MODISInterpolator:
                 k,
             )
 
-    @cython.boundscheck(False)
-    @cython.cdivision(True)
-    @cython.wraparound(False)
-    @cython.initializedcheck(False)
     cdef void _compute_fine_xyz_component(
             self,
             floating[:, ::1] a_track_view,
@@ -443,7 +386,7 @@ cdef class MODISInterpolator:
             floating[:, ::1] data_d_2d_view,
             floating[:, :, ::1] xyz_comp_view,
             Py_ssize_t k,
-    ) nogil:
+    ) noexcept nogil:
         cdef Py_ssize_t i, j
         cdef floating scan1_tmp, scan2_tmp, atrack1, ascan1
         for i in range(a_scan_view.shape[0]):
@@ -458,25 +401,17 @@ cdef class MODISInterpolator:
             self,
             floating[:, :] input_arr,
             floating[:, ::1] output_arr,
-    ) nogil:
+    ) noexcept nogil:
         if self._coarse_scan_length == 10:
             self._expand_tiepoint_array_1km(input_arr, output_arr)
         else:
             self._expand_tiepoint_array_5km(input_arr, output_arr)
 
-    @cython.boundscheck(False)
-    @cython.cdivision(True)
-    @cython.wraparound(False)
-    @cython.initializedcheck(False)
-    cdef void _expand_tiepoint_array_1km(self, floating[:, :] input_arr, floating[:, ::1] expanded_arr) nogil:
+    cdef void _expand_tiepoint_array_1km(self, floating[:, :] input_arr, floating[:, ::1] expanded_arr) noexcept nogil:
         self._expand_tiepoint_array_1km_main(input_arr, expanded_arr)
         self._expand_tiepoint_array_1km_right_column(input_arr, expanded_arr)
 
-    @cython.boundscheck(False)
-    @cython.cdivision(True)
-    @cython.wraparound(False)
-    @cython.initializedcheck(False)
-    cdef void _expand_tiepoint_array_1km_main(self, floating[:, :] input_arr, floating[:, ::1] expanded_arr) nogil:
+    cdef void _expand_tiepoint_array_1km_main(self, floating[:, :] input_arr, floating[:, ::1] expanded_arr) noexcept nogil:
         cdef floating tiepoint_value
         cdef Py_ssize_t row_idx, col_idx, length_repeat_cycle, width_repeat_cycle, half_coarse_pixel_fine_offset, row_offset, col_offset
         cdef Py_ssize_t row_repeat_offset, col_repeat_offset
@@ -502,15 +437,11 @@ cdef class MODISInterpolator:
             tiepoint_value = input_arr[row_idx, col_idx]
             self._expand_tiepoint_array_1km_with_repeat(tiepoint_value, expanded_arr, row_offset, col_offset)
 
-    @cython.boundscheck(False)
-    @cython.cdivision(True)
-    @cython.wraparound(False)
-    @cython.initializedcheck(False)
     cdef void _expand_tiepoint_array_1km_right_column(
             self,
             floating[:, :] input_arr,
             floating[:, ::1] expanded_arr
-    ) nogil:
+    ) noexcept nogil:
         cdef floating tiepoint_value
         cdef Py_ssize_t row_idx, col_idx, length_repeat_cycle, width_repeat_cycle, half_coarse_pixel_fine_offset, row_offset, col_offset
         cdef Py_ssize_t row_repeat_offset, col_repeat_offset
@@ -525,15 +456,11 @@ cdef class MODISInterpolator:
         self._expand_tiepoint_array_1km_right_column_top_row(input_arr, expanded_arr)
         self._expand_tiepoint_array_1km_right_column_bottom_row(input_arr, expanded_arr)
 
-    @cython.boundscheck(False)
-    @cython.cdivision(True)
-    @cython.wraparound(False)
-    @cython.initializedcheck(False)
     cdef void _expand_tiepoint_array_1km_right_column_top_row(
             self,
             floating[:, :] input_arr,
             floating[:, ::1] expanded_arr
-    ) nogil:
+    ) noexcept nogil:
         cdef floating tiepoint_value
         cdef Py_ssize_t row_idx, col_idx, row_offset, col_offset
         row_idx = 0
@@ -543,15 +470,11 @@ cdef class MODISInterpolator:
         col_offset = col_idx * self._fine_pixels_per_coarse_pixel + self._fine_pixels_per_coarse_pixel
         self._expand_tiepoint_array_1km_with_repeat(tiepoint_value, expanded_arr, row_offset, col_offset)
 
-    @cython.boundscheck(False)
-    @cython.cdivision(True)
-    @cython.wraparound(False)
-    @cython.initializedcheck(False)
     cdef void _expand_tiepoint_array_1km_right_column_bottom_row(
             self,
             floating[:, :] input_arr,
             floating[:, ::1] expanded_arr
-    ) nogil:
+    ) noexcept nogil:
         cdef floating tiepoint_value
         cdef Py_ssize_t row_idx, col_idx, row_offset, col_offset
         row_idx = input_arr.shape[0] - 1
@@ -561,17 +484,13 @@ cdef class MODISInterpolator:
         col_offset = col_idx * self._fine_pixels_per_coarse_pixel + self._fine_pixels_per_coarse_pixel
         self._expand_tiepoint_array_1km_with_repeat(tiepoint_value, expanded_arr, row_offset, col_offset)
 
-    @cython.boundscheck(False)
-    @cython.cdivision(True)
-    @cython.wraparound(False)
-    @cython.initializedcheck(False)
     cdef void _expand_tiepoint_array_1km_with_repeat(
             self,
             floating tiepoint_value,
             floating[:, ::1] expanded_arr,
             Py_ssize_t row_offset,
             Py_ssize_t col_offset,
-    ) nogil:
+    ) noexcept nogil:
         cdef Py_ssize_t length_repeat_cycle, width_repeat_cycle
         cdef Py_ssize_t row_repeat_offset, col_repeat_offset
         for length_repeat_cycle in range(self._fine_pixels_per_coarse_pixel):
@@ -580,22 +499,14 @@ cdef class MODISInterpolator:
                 col_repeat_offset = col_offset + width_repeat_cycle
                 expanded_arr[row_repeat_offset, col_repeat_offset] = tiepoint_value
 
-    @cython.boundscheck(False)
-    @cython.cdivision(True)
-    @cython.wraparound(False)
-    @cython.initializedcheck(False)
-    cdef void _expand_tiepoint_array_5km(self, floating[:, :] input_arr, floating[:, ::1] expanded_arr) nogil:
+    cdef void _expand_tiepoint_array_5km(self, floating[:, :] input_arr, floating[:, ::1] expanded_arr) noexcept nogil:
         self._expand_tiepoint_array_5km_main(input_arr, expanded_arr)
         self._expand_tiepoint_array_5km_left(input_arr, expanded_arr)
         if self._coarse_scan_width == 270:
             self._expand_tiepoint_array_5km_270_extra_column(input_arr, expanded_arr)
         self._expand_tiepoint_array_5km_right(input_arr, expanded_arr)
 
-    @cython.boundscheck(False)
-    @cython.cdivision(True)
-    @cython.wraparound(False)
-    @cython.initializedcheck(False)
-    cdef void _expand_tiepoint_array_5km_main(self, floating[:, :] input_arr, floating[:, ::1] expanded_arr) nogil:
+    cdef void _expand_tiepoint_array_5km_main(self, floating[:, :] input_arr, floating[:, ::1] expanded_arr) noexcept nogil:
         cdef floating tiepoint_value
         cdef Py_ssize_t row_idx, col_idx, row_offset, col_offset
         for row_idx in range(input_arr.shape[0]):
@@ -610,15 +521,11 @@ cdef class MODISInterpolator:
                     col_offset,
                     self._fine_pixels_per_coarse_pixel)
 
-    @cython.boundscheck(False)
-    @cython.cdivision(True)
-    @cython.wraparound(False)
-    @cython.initializedcheck(False)
     cdef void _expand_tiepoint_array_5km_270_extra_column(
             self,
             floating[:, :] input_arr,
             floating[:, ::1] expanded_arr
-    ) nogil:
+    ) noexcept nogil:
         """Copy an extra coarse pixel column between the main copied area and the right-most columns."""
         cdef floating tiepoint_value
         cdef Py_ssize_t row_idx, col_idx, row_offset, col_offset
@@ -634,43 +541,31 @@ cdef class MODISInterpolator:
                 col_offset,
                 self._fine_pixels_per_coarse_pixel)
 
-    @cython.boundscheck(False)
-    @cython.cdivision(True)
-    @cython.wraparound(False)
-    @cython.initializedcheck(False)
     cdef void _expand_tiepoint_array_5km_left(
             self,
             floating[:, :] input_arr,
             floating[:, ::1] expanded_arr,
-    ) nogil:
+    ) noexcept nogil:
         self._expand_tiepoint_array_5km_edges(input_arr, expanded_arr, 0, 0)
 
-    @cython.boundscheck(False)
-    @cython.cdivision(True)
-    @cython.wraparound(False)
-    @cython.initializedcheck(False)
     cdef void _expand_tiepoint_array_5km_right(
             self,
             floating[:, :] input_arr,
             floating[:, ::1] expanded_arr,
-    ) nogil:
+    ) noexcept nogil:
         self._expand_tiepoint_array_5km_edges(
             input_arr,
             expanded_arr,
             input_arr.shape[1] - 1,
             expanded_arr.shape[1] - self._factor_5km)
 
-    @cython.boundscheck(False)
-    @cython.cdivision(True)
-    @cython.wraparound(False)
-    @cython.initializedcheck(False)
     cdef void _expand_tiepoint_array_5km_edges(
             self,
             floating[:, :] input_arr,
             floating[:, ::1] expanded_arr,
             Py_ssize_t course_col_idx,
             Py_ssize_t fine_col_idx,
-    ) nogil:
+    ) noexcept nogil:
         cdef floating tiepoint_value
         cdef Py_ssize_t row_idx, row_offset
         for row_idx in range(input_arr.shape[0]):
@@ -683,10 +578,6 @@ cdef class MODISInterpolator:
                 fine_col_idx,
                 self._factor_5km)
 
-    @cython.boundscheck(False)
-    @cython.cdivision(True)
-    @cython.wraparound(False)
-    @cython.initializedcheck(False)
     cdef void _expand_tiepoint_array_5km_with_repeat(
             self,
             floating tiepoint_value,
@@ -694,7 +585,7 @@ cdef class MODISInterpolator:
             Py_ssize_t row_offset,
             Py_ssize_t col_offset,
             Py_ssize_t col_width,
-    ) nogil:
+    ) noexcept nogil:
         cdef Py_ssize_t length_repeat_cycle, width_repeat_cycle
         for length_repeat_cycle in range(self._fine_pixels_per_coarse_pixel * 2):
             for width_repeat_cycle in range(col_width):


=====================================
geotiepoints/_modis_utils.pxd
=====================================
@@ -1,3 +1,4 @@
+# cython: language_level=3, boundscheck=False, cdivision=True, wraparound=False, initializedcheck=False, nonecheck=False
 cimport numpy as np
 
 ctypedef fused floating:
@@ -8,7 +9,7 @@ cdef void lonlat2xyz(
         floating[:, ::1] lons,
         floating[:, ::1] lats,
         floating[:, :, ::1] xyz,
-) nogil
+) noexcept nogil
 
 cdef void xyz2lonlat(
         floating[:, :, ::1] xyz,
@@ -16,7 +17,7 @@ cdef void xyz2lonlat(
         floating[:, ::1] lats,
         bint low_lat_z=*,
         floating thr=*,
-) nogil
+) noexcept nogil
 
-cdef floating rad2deg(floating x) nogil
-cdef floating deg2rad(floating x) nogil
+cdef floating rad2deg(floating x) noexcept nogil
+cdef floating deg2rad(floating x) noexcept nogil


=====================================
geotiepoints/_modis_utils.pyx
=====================================
@@ -1,3 +1,4 @@
+# cython: language_level=3, boundscheck=False, cdivision=True, wraparound=False, initializedcheck=False, nonecheck=False
 from functools import wraps
 
 cimport cython
@@ -5,6 +6,8 @@ cimport numpy as np
 from libc.math cimport asin, sin, cos, sqrt, acos, M_PI
 import numpy as np
 
+np.import_array()
+
 try:
     import dask.array as da
 except ImportError:
@@ -20,14 +23,11 @@ except ImportError:
 DEF EARTH_RADIUS = 6370997.0
 
 
- at cython.boundscheck(False)
- at cython.cdivision(True)
- at cython.wraparound(False)
 cdef void lonlat2xyz(
         floating[:, ::1] lons,
         floating[:, ::1] lats,
         floating[:, :, ::1] xyz,
-) nogil:
+) noexcept nogil:
     """Convert lons and lats to cartesian coordinates."""
     cdef Py_ssize_t i, j, k
     cdef floating lon_rad, lat_rad
@@ -40,15 +40,12 @@ cdef void lonlat2xyz(
             xyz[i, j, 2] = EARTH_RADIUS * sin(lat_rad)
 
 
- at cython.boundscheck(False)
- at cython.cdivision(True)
- at cython.wraparound(False)
 cdef void xyz2lonlat(
         floating[:, :, ::1] xyz,
         floating[:, ::1] lons,
         floating[:, ::1] lats,
         bint low_lat_z=True,
-        floating thr=0.8) nogil:
+        floating thr=0.8) noexcept nogil:
     """Get longitudes from cartesian coordinates."""
     cdef Py_ssize_t i, j
     cdef np.float64_t x, y, z
@@ -68,17 +65,15 @@ cdef void xyz2lonlat(
                 lats[i, j] = _sign(z) * (90 - rad2deg(asin(sqrt(x ** 2 + y ** 2) / EARTH_RADIUS)))
 
 
-cdef inline int _sign(floating x) nogil:
+cdef inline int _sign(floating x) noexcept nogil:
     return 1 if x > 0 else (-1 if x < 0 else 0)
 
 
- at cython.cdivision(True)
-cdef inline floating rad2deg(floating x) nogil:
+cdef inline floating rad2deg(floating x) noexcept nogil:
     return x * (180.0 / M_PI)
 
 
- at cython.cdivision(True)
-cdef inline floating deg2rad(floating x) nogil:
+cdef inline floating deg2rad(floating x) noexcept nogil:
     return x * (M_PI / 180.0)
 
 
@@ -173,7 +168,7 @@ def _rechunk_dask_arrays_if_needed(args, rows_per_scan: int):
     row_chunks = first_arr.chunks[0]
     col_chunks = first_arr.chunks[1]
     num_rows = first_arr.shape[0]
-    num_cols = first_arr.shape[-1]
+    num_cols = first_arr.shape[1]
     good_row_chunks = all(x % rows_per_scan == 0 for x in row_chunks)
     good_col_chunks = len(col_chunks) == 1 and col_chunks[0] != num_cols
     all_orig_chunks = [arr.chunks for arr in args if hasattr(arr, "chunks")]


=====================================
geotiepoints/_simple_modis_interpolator.pyx
=====================================
@@ -1,3 +1,4 @@
+# cython: language_level=3, boundscheck=False, cdivision=True, wraparound=False, initializedcheck=False, nonecheck=False
 cimport cython
 
 from ._modis_utils cimport floating
@@ -7,6 +8,7 @@ cimport numpy as np
 import numpy as np
 from scipy.ndimage import map_coordinates
 
+np.import_array()
 
 def interpolate_geolocation_cartesian(
         np.ndarray[floating, ndim=2] lon_array,
@@ -69,7 +71,7 @@ def interpolate_geolocation_cartesian(
 cdef void _compute_yx_coordinate_arrays(
         unsigned int res_factor,
         floating[:, :, ::1] coordinates,
-) nogil:
+) noexcept nogil:
     cdef Py_ssize_t i, j
     for j in range(coordinates.shape[1]):
         for i in range(coordinates.shape[2]):
@@ -85,7 +87,7 @@ cdef void _compute_interpolated_xyz_scan(
         floating[:, :, ::1] coordinates_view,
         floating[:, :, ::1] xyz_input_view,
         floating[:, :, ::1] xyz_result_view,
-) nogil:
+) noexcept nogil:
     cdef Py_ssize_t comp_index
     cdef floating[:, :] input_view, result_view
     with gil:
@@ -137,7 +139,7 @@ cdef void _call_map_coordinates(
 cdef void _extrapolate_xyz_rightmost_columns(
         floating[:, :] result_view,
         int num_columns,
-) nogil:
+) noexcept nogil:
     cdef Py_ssize_t row_idx, col_offset
     cdef floating last_interp_col_diff
     for row_idx in range(result_view.shape[0]):
@@ -155,7 +157,7 @@ cdef void _extrapolate_xyz_rightmost_columns(
 cdef void _interpolate_xyz_250(
         floating[:, :] result_view,
         floating[:, :, ::1] coordinates_view,
-) nogil:
+) noexcept nogil:
     cdef Py_ssize_t col_idx
     cdef floating m, b
     cdef floating[:] result_col_view
@@ -194,7 +196,7 @@ cdef void _interpolate_xyz_250(
 cdef void _interpolate_xyz_500(
         floating[:, :] result_view,
         floating[:, :, ::1] coordinates_view,
-) nogil:
+) noexcept nogil:
     cdef Py_ssize_t col_idx
     cdef floating m, b
     for col_idx in range(result_view.shape[1]):
@@ -231,7 +233,7 @@ cdef inline floating _calc_slope_250(
         floating[:] result_view,
         floating[:, ::1] y,
         Py_ssize_t offset,
-) nogil:
+) noexcept nogil:
     return (result_view[offset + 3] - result_view[offset]) / \
            (y[offset + 3, 0] - y[offset, 0])
 
@@ -245,7 +247,7 @@ cdef inline floating _calc_offset_250(
         floating[:, ::1] y,
         floating m,
         Py_ssize_t offset,
-) nogil:
+) noexcept nogil:
     return result_view[offset + 3] - m * y[offset + 3, 0]
 
 
@@ -257,7 +259,7 @@ cdef inline floating _calc_slope_500(
         floating[:] result_view,
         floating[:, ::1] y,
         Py_ssize_t offset,
-) nogil:
+) noexcept nogil:
     return (result_view[offset + 1] - result_view[offset]) / \
            (y[offset + 1, 0] - y[offset, 0])
 
@@ -271,5 +273,5 @@ cdef inline floating _calc_offset_500(
         floating[:, ::1] y,
         floating m,
         Py_ssize_t offset,
-) nogil:
+) noexcept nogil:
     return result_view[offset + 1] - m * y[offset + 1, 0]


=====================================
geotiepoints/interpolator.py
=====================================
@@ -1,25 +1,17 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-# Copyright (c) 2013-2018 PyTroll community
-
-# Author(s):
-
-#   Martin Raspaud <martin.raspaud at smhi.se>
-
+# Copyright (c) 2013-2018 Python-geotiepoints developers
+#
 # 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/>.
-
 """Generic interpolation routines."""
 
 import numpy as np
@@ -218,9 +210,6 @@ class Interpolator:
         if np.array_equal(self.hrow_indices, self.row_indices):
             return self._interp1d()
 
-        xpoints, ypoints = np.meshgrid(self.hrow_indices,
-                                       self.hcol_indices)
-
         for num, data in enumerate(self.tie_data):
             spl = RectBivariateSpline(self.row_indices,
                                       self.col_indices,
@@ -229,8 +218,7 @@ class Interpolator:
                                       kx=self.kx_,
                                       ky=self.ky_)
 
-            new_data_ = spl.ev(xpoints.ravel(), ypoints.ravel())
-            self.new_data[num] = new_data_.reshape(xpoints.shape).T.copy(order='C')
+            self.new_data[num] = spl(self.hrow_indices, self.hcol_indices, grid=True)
 
     def _interp1d(self):
         """Interpolate in one dimension."""
@@ -287,38 +275,29 @@ class SingleGridInterpolator:
         """Interpolate (lazily) to a dask array."""
         from dask.base import tokenize
         import dask.array as da
+        from dask.array.core import normalize_chunks
         v_fine_points, h_fine_points = fine_points
         shape = len(v_fine_points), len(h_fine_points)
 
-        try:
-            v_chunk_size, h_chunk_size = chunks
-        except TypeError:
-            v_chunk_size, h_chunk_size = chunks, chunks
-
-        vchunks = range(0, shape[0], v_chunk_size)
-        hchunks = range(0, shape[1], h_chunk_size)
+        chunks = normalize_chunks(chunks, shape, dtype=self.values.dtype)
 
-        token = tokenize(v_chunk_size, h_chunk_size, self.points, self.values, fine_points, method)
+        token = tokenize(chunks, self.points, self.values, fine_points, method)
         name = 'interpolate-' + token
 
-        dskx = {(name, i, j): (self.interpolate_slices,
-                               (slice(vcs, min(vcs + v_chunk_size, shape[0])),
-                                slice(hcs, min(hcs + h_chunk_size, shape[1]))),
-                               method
-                               )
-                for i, vcs in enumerate(vchunks)
-                for j, hcs in enumerate(hchunks)
-                }
+        dskx = {(name, ) + position: (self.interpolate_slices,
+                                      slices,
+                                      method)
+                for position, slices in _enumerate_chunk_slices(chunks)}
 
         res = da.Array(dskx, name, shape=list(shape),
-                       chunks=(v_chunk_size, h_chunk_size),
+                       chunks=chunks,
                        dtype=self.values.dtype)
         return res
 
     def interpolate_numpy(self, fine_points, method="linear"):
         """Interpolate to a numpy array."""
         fine_x, fine_y = np.meshgrid(*fine_points, indexing='ij')
-        return self.interpolator((fine_x, fine_y), method=method)
+        return self.interpolator((fine_x, fine_y), method=method).astype(self.values.dtype)
 
     def interpolate_slices(self, fine_points, method="linear"):
         """Interpolate using slices.
@@ -333,6 +312,18 @@ class SingleGridInterpolator:
         return self.interpolate_numpy(fine_points, method=method)
 
 
+def _enumerate_chunk_slices(chunks):
+    """Enumerate chunks with slices."""
+    for position in np.ndindex(tuple(map(len, (chunks)))):
+        slices = []
+        for pos, chunk in zip(position, chunks):
+            chunk_size = chunk[pos]
+            offset = sum(chunk[:pos])
+            slices.append(slice(offset, offset + chunk_size))
+
+        yield (position, slices)
+
+
 class MultipleGridInterpolator:
     """Interpolator that works on multiple data arrays."""
 


=====================================
geotiepoints/modisinterpolator.py
=====================================
@@ -1,22 +1,15 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-# Copyright (c) 2018 PyTroll community
-
-# Author(s):
-
-#   Martin Raspaud <martin.raspaud at smhi.se>
-
+# Copyright (c) 2018-2023 Python-geotiepoints developers
+#
 # 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/>.
 """Interpolation of MODIS data using satellite zenith angle.


=====================================
geotiepoints/multilinear.py
=====================================
@@ -17,8 +17,7 @@ def mlinspace(smin, smax, orders):
 
 
 class MultilinearInterpolator:
-
-    '''Multilinear interpolation
+    """Multilinear interpolation.
 
     Methods
     -------
@@ -55,14 +54,14 @@ class MultilinearInterpolator:
 
     interpolated_values = interp(random_points)
     exact_values = f(random_points)
-    '''
+    """
 
     __grid__ = None
 
     def __init__(self, smin, smax, orders, values=None, dtype=np.float64):
-        self.smin = np.array(smin, dtype=dtype)
-        self.smax = np.array(smax, dtype=dtype)
-        self.orders = np.array(orders, dtype=np.int_)
+        self.smin = np.array(smin, dtype=dtype, copy=False)
+        self.smax = np.array(smax, dtype=dtype, copy=False)
+        self.orders = np.array(orders, dtype=np.int_, copy=False)
         self.d = len(orders)
         self.dtype = dtype
         if values is not None:


=====================================
geotiepoints/multilinear_cython.pyx
=====================================
@@ -1,71 +1,61 @@
+# cython: language_level=3, boundscheck=False, cdivision=True, wraparound=False, initializedcheck=False, nonecheck=False
 
-# generation options
-
-# some helper functions
-
-from libc.math cimport fmin, fmax, floor
 cimport cython
 from cython.parallel import prange,parallel
+from cython cimport floating
 
 cimport numpy as np
 import numpy as np
 
-ctypedef fused floating:
-    float
-    double
+np.import_array()
+
 
 def multilinear_interpolation(floating[:] smin, floating[:] smax, long[:] orders, floating[:,::1] values, floating[:,::1] s):
 
-    cdef int d = np.size(s,0)
-    cdef int n_s = np.size(s,1)
-    cdef int n_v = np.size(values,0)
+    cdef Py_ssize_t d = s.shape[0]
+    cdef Py_ssize_t n_s = s.shape[1]
+    cdef Py_ssize_t n_v = values.shape[0]
 
     if floating is float:
         dtype = np.single
     else:
         dtype = np.double
 
-    cdef floating[:,::1] result = np.zeros((n_v,n_s), dtype=dtype)
+    cdef np.ndarray[floating, ndim=2] result_arr = np.empty((n_v, n_s), dtype=dtype)
+    cdef floating[:, ::1] result = result_arr
     cdef floating[:] vals
     cdef floating[:] res
 
+    if d > 4:
+        raise Exception("Can't interpolate in dimension strictly greater than 5")
 
-    for i in range(n_v):
-        vals = values[i,:]
-        res = result[i,:]
-        if False:
-            pass
-        elif d==1:
-            multilinear_interpolation_1d(smin, smax, orders, vals, n_s, s, res)
-        elif d==2:
-            multilinear_interpolation_2d(smin, smax, orders, vals, n_s, s, res)
-        elif d==3:
-            multilinear_interpolation_3d(smin, smax, orders, vals, n_s, s, res)
-        elif d==4:
-            multilinear_interpolation_4d(smin, smax, orders, vals, n_s, s, res)
-        else:
-            raise Exception("Can't interpolate in dimension strictly greater than 5")
-
-    return np.array(result,dtype=dtype)
-
- at cython.boundscheck(False)
- at cython.cdivision(True)
- at cython.wraparound(False)
-cdef multilinear_interpolation_1d(floating[:] smin, floating[:] smax,
-                                  long[:] orders, floating[:] V,
-                                  int n_s, floating[:,::1] s, floating[:] output):
-
-    cdef int d = 1
+    with nogil:
+        for i in range(n_v):
+            vals = values[i, :]
+            res = result[i, :]
+            if d == 1:
+                multilinear_interpolation_1d(smin, smax, orders, vals, n_s, s, res)
+            elif d == 2:
+                multilinear_interpolation_2d(smin, smax, orders, vals, n_s, s, res)
+            elif d == 3:
+                multilinear_interpolation_3d(smin, smax, orders, vals, n_s, s, res)
+            elif d == 4:
+                multilinear_interpolation_4d(smin, smax, orders, vals, n_s, s, res)
+
+    return result_arr
 
-    cdef int i
 
+cdef void multilinear_interpolation_1d(floating[:] smin, floating[:] smax,
+                                       long[:] orders, floating[:] V,
+                                       int n_s, floating[:,::1] s, floating[:] output) noexcept nogil:
 
+    cdef int i
     cdef floating lam_0, s_0, sn_0, snt_0
     cdef int order_0 = orders[0]
     cdef int q_0
     cdef floating v_0
     cdef floating v_1
-    with nogil, parallel():
+    with parallel():
         for i in prange(n_s):
 
             # (s_1, ..., s_d) : evaluation point
@@ -88,18 +78,11 @@ cdef multilinear_interpolation_1d(floating[:] smin, floating[:] smax,
             output[i] = (1-lam_0)*(v_0) + (lam_0)*(v_1)
 
 
- at cython.boundscheck(False)
- at cython.cdivision(True)
- at cython.wraparound(False)
-cdef multilinear_interpolation_2d(floating[:] smin, floating[:] smax,
-                                  long[:] orders, floating[:] V,
-                                  int n_s, floating[:,::1] s, floating[:] output):
-
-    cdef int d = 2
+cdef void multilinear_interpolation_2d(floating[:] smin, floating[:] smax,
+                                       long[:] orders, floating[:] V,
+                                       int n_s, floating[:,::1] s, floating[:] output) noexcept nogil:
 
     cdef int i
-
-
     cdef floating lam_0, s_0, sn_0, snt_0
     cdef floating lam_1, s_1, sn_1, snt_1
     cdef int order_0 = orders[0]
@@ -111,7 +94,7 @@ cdef multilinear_interpolation_2d(floating[:] smin, floating[:] smax,
     cdef floating v_01
     cdef floating v_10
     cdef floating v_11
-    with nogil, parallel():
+    with parallel():
         for i in prange(n_s):
 
             # (s_1, ..., s_d) : evaluation point
@@ -140,18 +123,10 @@ cdef multilinear_interpolation_2d(floating[:] smin, floating[:] smax,
             output[i] = (1-lam_0)*((1-lam_1)*(v_00) + (lam_1)*(v_01)) + (lam_0)*((1-lam_1)*(v_10) + (lam_1)*(v_11))
 
 
- at cython.boundscheck(False)
- at cython.cdivision(True)
- at cython.wraparound(False)
-cdef multilinear_interpolation_3d(floating[:] smin, floating[:] smax,
-                                  long[:] orders, floating[:] V,
-                                  int n_s, floating[:,::1] s, floating[:] output):
-
-    cdef int d = 3
-
+cdef void multilinear_interpolation_3d(floating[:] smin, floating[:] smax,
+                                       long[:] orders, floating[:] V,
+                                       int n_s, floating[:,::1] s, floating[:] output) noexcept nogil:
     cdef int i
-
-
     cdef floating lam_0, s_0, sn_0, snt_0
     cdef floating lam_1, s_1, sn_1, snt_1
     cdef floating lam_2, s_2, sn_2, snt_2
@@ -171,7 +146,7 @@ cdef multilinear_interpolation_3d(floating[:] smin, floating[:] smax,
     cdef floating v_101
     cdef floating v_110
     cdef floating v_111
-    with nogil, parallel():
+    with parallel():
         for i in prange(n_s):
 
             # (s_1, ..., s_d) : evaluation point
@@ -208,18 +183,11 @@ cdef multilinear_interpolation_3d(floating[:] smin, floating[:] smax,
             output[i] = (1-lam_0)*((1-lam_1)*((1-lam_2)*(v_000) + (lam_2)*(v_001)) + (lam_1)*((1-lam_2)*(v_010) + (lam_2)*(v_011))) + (lam_0)*((1-lam_1)*((1-lam_2)*(v_100) + (lam_2)*(v_101)) + (lam_1)*((1-lam_2)*(v_110) + (lam_2)*(v_111)))
 
 
- at cython.boundscheck(False)
- at cython.cdivision(True)
- at cython.wraparound(False)
-cdef multilinear_interpolation_4d(floating[:] smin, floating[:] smax,
-                                  long[:] orders, floating[:] V,
-                                  int n_s, floating[:,::1] s, floating[:] output):
-
-    cdef int d = 4
+cdef void multilinear_interpolation_4d(floating[:] smin, floating[:] smax,
+                                       long[:] orders, floating[:] V,
+                                       int n_s, floating[:,::1] s, floating[:] output) noexcept nogil:
 
     cdef int i
-
-
     cdef floating lam_0, s_0, sn_0, snt_0
     cdef floating lam_1, s_1, sn_1, snt_1
     cdef floating lam_2, s_2, sn_2, snt_2
@@ -251,7 +219,7 @@ cdef multilinear_interpolation_4d(floating[:] smin, floating[:] smax,
     cdef floating v_1101
     cdef floating v_1110
     cdef floating v_1111
-    with nogil, parallel():
+    with parallel():
         for i in prange(n_s):
 
             # (s_1, ..., s_d) : evaluation point
@@ -300,18 +268,10 @@ cdef multilinear_interpolation_4d(floating[:] smin, floating[:] smax,
             output[i] = (1-lam_0)*((1-lam_1)*((1-lam_2)*((1-lam_3)*(v_0000) + (lam_3)*(v_0001)) + (lam_2)*((1-lam_3)*(v_0010) + (lam_3)*(v_0011))) + (lam_1)*((1-lam_2)*((1-lam_3)*(v_0100) + (lam_3)*(v_0101)) + (lam_2)*((1-lam_3)*(v_0110) + (lam_3)*(v_0111)))) + (lam_0)*((1-lam_1)*((1-lam_2)*((1-lam_3)*(v_1000) + (lam_3)*(v_1001)) + (lam_2)*((1-lam_3)*(v_1010) + (lam_3)*(v_1011))) + (lam_1)*((1-lam_2)*((1-lam_3)*(v_1100) + (lam_3)*(v_1101)) + (lam_2)*((1-lam_3)*(v_1110) + (lam_3)*(v_1111))))
 
 
- at cython.boundscheck(False)
- at cython.cdivision(True)
- at cython.wraparound(False)
-cdef multilinear_interpolation_5d(floating[:] smin, floating[:] smax,
-                                  long[:] orders, floating[:] V,
-                                  int n_s, floating[:,::1] s, floating[:] output):
-
-    cdef int d = 5
-
+cdef void multilinear_interpolation_5d(floating[:] smin, floating[:] smax,
+                                       long[:] orders, floating[:] V,
+                                       int n_s, floating[:,::1] s, floating[:] output) noexcept nogil:
     cdef int i
-
-
     cdef floating lam_0, s_0, sn_0, snt_0
     cdef floating lam_1, s_1, sn_1, snt_1
     cdef floating lam_2, s_2, sn_2, snt_2
@@ -363,7 +323,7 @@ cdef multilinear_interpolation_5d(floating[:] smin, floating[:] smax,
     cdef floating v_11101
     cdef floating v_11110
     cdef floating v_11111
-    with nogil, parallel():
+    with parallel():
         for i in prange(n_s):
 
             # (s_1, ..., s_d) : evaluation point


=====================================
geotiepoints/tests/__init__.py
=====================================
@@ -1,6 +1,4 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017 Adam.Dybbroe
+# Copyright (c) 2017 Python-geotiepoints developers
 #
 # 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


=====================================
geotiepoints/tests/test_geointerpolator.py
=====================================
@@ -246,6 +246,40 @@ class TestGeoGridInterpolator:
         np.testing.assert_allclose(lons[0, :], lons_expected, rtol=5e-5)
         np.testing.assert_allclose(lats[:, 0], lats_expected, rtol=5e-5)
 
+    def test_geogrid_interpolation_preserves_dtype(self):
+        """Test that the interpolator works with both explicit tie-point arrays and swath definition objects."""
+        x_points = np.array([0, 1, 3, 7])
+        y_points = np.array([0, 1, 3, 7, 15])
+
+        interpolator = GeoGridInterpolator((y_points, x_points),
+                                           TIE_LONS.astype(np.float32), TIE_LATS.astype(np.float32))
+
+        lons, lats = interpolator.interpolate_to_shape((16, 8))
+
+        assert lons.dtype == np.float32
+        assert lats.dtype == np.float32
+
+    def test_chunked_geogrid_interpolation(self):
+        """Test that the interpolator works with both explicit tie-point arrays and swath definition objects."""
+        dask = pytest.importorskip("dask")
+
+        x_points = np.array([0, 1, 3, 7])
+        y_points = np.array([0, 1, 3, 7, 15])
+
+        interpolator = GeoGridInterpolator((y_points, x_points),
+                                           TIE_LONS.astype(np.float32), TIE_LATS.astype(np.float32))
+
+        lons, lats = interpolator.interpolate_to_shape((16, 8), chunks=4)
+
+        assert lons.chunks == ((4, 4, 4, 4), (4, 4))
+        assert lats.chunks == ((4, 4, 4, 4), (4, 4))
+
+        with dask.config.set({"array.chunk-size": 64}):
+
+            lons, lats = interpolator.interpolate_to_shape((16, 8), chunks="auto")
+            assert lons.chunks == ((4, 4, 4, 4), (4, 4))
+            assert lats.chunks == ((4, 4, 4, 4), (4, 4))
+
     def test_geogrid_interpolation_can_extrapolate(self):
         """Test that the interpolator can also extrapolate given the right parameters."""
         x_points = np.array([0, 1, 3, 7])


=====================================
geotiepoints/tests/test_interpolator.py
=====================================
@@ -140,7 +140,8 @@ def grid_interpolator():
                      [2, 2, 2, 1],
                      [0, 3, 3, 3],
                      [1, 2, 1, 2],
-                     [4, 4, 4, 4]])
+                     [4, 4, 4, 4]],
+                    dtype=np.float64)
 
     return SingleGridInterpolator((ypoints, xpoints), data)
 
@@ -349,12 +350,12 @@ class TestSingleGridInterpolator:
         fine_y = np.arange(32)
 
         res = grid_interpolator.interpolate((fine_y, fine_x), method="cubic")
-        np.testing.assert_allclose(res, self.expected)
+        np.testing.assert_allclose(res, self.expected, atol=2e-9)
 
     def test_interpolate_slices(self, grid_interpolator):
         """Test that interpolation from slices is working."""
         res = grid_interpolator.interpolate_slices((slice(0, 32), slice(0, 16)), method="cubic")
-        np.testing.assert_allclose(res, self.expected)
+        np.testing.assert_allclose(res, self.expected, atol=2e-9)
 
     @pytest.mark.parametrize("chunks, expected_chunks", [(10, (10, 10)),
                                                          ((10, 5), (10, 5))])
@@ -376,5 +377,23 @@ class TestSingleGridInterpolator:
             assert res.chunks[0][0] == v_chunk
             assert res.chunks[1][0] == h_chunk
 
-            np.testing.assert_allclose(res, self.expected)
+            np.testing.assert_allclose(res, self.expected, atol=2e-9)
             assert interpolate.called
+
+    def test_interpolate_preserves_dtype(self):
+        """Test that interpolation is preserving the dtype."""
+        xpoints = np.array([0, 3, 7, 15])
+        ypoints = np.array([0, 3, 7, 15, 31])
+        data = np.array([[0, 1, 0, 1],
+                        [2, 2, 2, 1],
+                        [0, 3, 3, 3],
+                        [1, 2, 1, 2],
+                        [4, 4, 4, 4]],
+                        dtype=np.float32)
+
+        grid_interpolator = SingleGridInterpolator((ypoints, xpoints), data)
+        fine_x = np.arange(16)
+        fine_y = np.arange(32)
+
+        res = grid_interpolator.interpolate((fine_y, fine_x), method="cubic")
+        assert res.dtype == data.dtype


=====================================
geotiepoints/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 = " (HEAD -> main, tag: v1.6.0)"
-    git_full = "4f3fd0b5b3d39987a6b17619287f761a04e01ea8"
-    git_date = "2023-03-17 14:32:34 +0100"
+    git_refnames = " (HEAD -> main, tag: v1.7.0)"
+    git_full = "2e8b790f6fc7679544b3a1c6b68fc01a122deb00"
+    git_date = "2023-11-21 10:02:32 +0100"
     keywords = {"refnames": git_refnames, "full": git_full, "date": git_date}
     return keywords
 


=====================================
pyproject.toml
=====================================
@@ -1,5 +1,5 @@
 [build-system]
-requires = ["setuptools", "wheel", "oldest-supported-numpy", "Cython", "versioneer"]
+requires = ["setuptools", "wheel", "oldest-supported-numpy", "Cython>=3", "versioneer"]
 build-backend = "setuptools.build_meta"
 
 [tool.coverage.run]


=====================================
setup.py
=====================================
@@ -1,31 +1,22 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-# Copyright (c) 2012-2018 PyTroll community
-
-# Author(s):
-
-#   Adam Dybbroe <adam.dybbroe at smhi.se>
-#   Martin Raspaud <martin.raspaud at smhi.se>
-
+# Copyright (c) 2012-2023 Python-geotiepoints developers
+#
 # 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/>.
-
 """Setting up the geo_interpolator project."""
 
 import sys
 
-from setuptools import setup
+from setuptools import setup, find_packages
 import versioneer
 import numpy as np
 from Cython.Build import build_ext
@@ -77,7 +68,7 @@ except ValueError:
 cython_directives = {
     "language_level": "3",
 }
-define_macros = []
+define_macros = [("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION")]
 if cython_coverage:
     print("Enabling directives/macros for Cython coverage support")
     cython_directives.update({
@@ -88,9 +79,9 @@ if cython_coverage:
         ("CYTHON_TRACE", "1"),
         ("CYTHON_TRACE_NOGIL", "1"),
     ])
-    for ext in EXTENSIONS:
-        ext.define_macros = define_macros
-        ext.cython_directives.update(cython_directives)
+for ext in EXTENSIONS:
+    ext.define_macros = define_macros
+    ext.cython_directives.update(cython_directives)
 
 cmdclass = versioneer.get_cmdclass(cmdclass={"build_ext": build_ext})
 
@@ -113,10 +104,8 @@ if __name__ == "__main__":
                        "Programming Language :: Cython",
                        "Topic :: Scientific/Engineering"],
           url="https://github.com/pytroll/python-geotiepoints",
-          packages=['geotiepoints'],
-          # packages=find_packages(),
-          setup_requires=['numpy', 'cython'],
-          python_requires='>=3.7',
+          packages=find_packages(),
+          python_requires='>=3.9',
           cmdclass=cmdclass,
           install_requires=requirements,
           ext_modules=EXTENSIONS,



View it on GitLab: https://salsa.debian.org/debian-gis-team/python-geotiepoints/-/compare/f37fa8561ae64c5e68c513fb2fb185c4ce13e9bf...b594f8080e5cc649dbae6d77f8d72fc800194d79

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/python-geotiepoints/-/compare/f37fa8561ae64c5e68c513fb2fb185c4ce13e9bf...b594f8080e5cc649dbae6d77f8d72fc800194d79
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/20231226/df8f41ea/attachment-0001.htm>


More information about the Pkg-grass-devel mailing list