drizzle 2.0.0__tar.gz → 2.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of drizzle might be problematic. Click here for more details.

Files changed (64) hide show
  1. drizzle-2.1.0/.github/workflows/ci.yml +39 -0
  2. {drizzle-2.0.0 → drizzle-2.1.0}/CHANGES.rst +25 -2
  3. {drizzle-2.0.0/drizzle.egg-info → drizzle-2.1.0}/PKG-INFO +8 -38
  4. {drizzle-2.0.0 → drizzle-2.1.0}/README.rst +3 -3
  5. {drizzle-2.0.0 → drizzle-2.1.0}/drizzle/resample.py +23 -18
  6. drizzle-2.1.0/drizzle/tests/helpers.py +221 -0
  7. {drizzle-2.0.0 → drizzle-2.1.0}/drizzle/tests/test_resample.py +277 -489
  8. {drizzle-2.0.0 → drizzle-2.1.0}/drizzle/tests/test_utils.py +92 -48
  9. {drizzle-2.0.0 → drizzle-2.1.0}/drizzle/utils.py +56 -10
  10. {drizzle-2.0.0 → drizzle-2.1.0/drizzle.egg-info}/PKG-INFO +8 -38
  11. {drizzle-2.0.0 → drizzle-2.1.0}/drizzle.egg-info/SOURCES.txt +1 -0
  12. {drizzle-2.0.0 → drizzle-2.1.0}/drizzle.egg-info/requires.txt +2 -1
  13. {drizzle-2.0.0 → drizzle-2.1.0}/pyproject.toml +5 -5
  14. {drizzle-2.0.0 → drizzle-2.1.0}/setup.py +18 -6
  15. {drizzle-2.0.0 → drizzle-2.1.0}/src/cdrizzleapi.c +13 -2
  16. {drizzle-2.0.0 → drizzle-2.1.0}/src/cdrizzlebox.c +179 -32
  17. {drizzle-2.0.0 → drizzle-2.1.0}/src/cdrizzlebox.h +3 -1
  18. {drizzle-2.0.0 → drizzle-2.1.0}/src/cdrizzlemap.c +91 -5
  19. {drizzle-2.0.0 → drizzle-2.1.0}/src/cdrizzlemap.h +5 -0
  20. {drizzle-2.0.0 → drizzle-2.1.0}/src/cdrizzleutil.c +1 -1
  21. {drizzle-2.0.0 → drizzle-2.1.0}/src/tests/pandokia_fct.h +6 -6
  22. {drizzle-2.0.0 → drizzle-2.1.0}/src/tests/utest_cdrizzle.c +3 -1
  23. {drizzle-2.0.0 → drizzle-2.1.0}/tox.ini +3 -0
  24. drizzle-2.0.0/.github/workflows/ci.yml +0 -34
  25. {drizzle-2.0.0 → drizzle-2.1.0}/.clang-format +0 -0
  26. {drizzle-2.0.0 → drizzle-2.1.0}/.coveragerc +0 -0
  27. {drizzle-2.0.0 → drizzle-2.1.0}/.flake8 +0 -0
  28. {drizzle-2.0.0 → drizzle-2.1.0}/.github/CODEOWNERS +0 -0
  29. {drizzle-2.0.0 → drizzle-2.1.0}/.github/dependabot.yml +0 -0
  30. {drizzle-2.0.0 → drizzle-2.1.0}/.github/workflows/build.yml +0 -0
  31. {drizzle-2.0.0 → drizzle-2.1.0}/.gitignore +0 -0
  32. {drizzle-2.0.0 → drizzle-2.1.0}/.readthedocs.yaml +0 -0
  33. {drizzle-2.0.0 → drizzle-2.1.0}/.ruff.toml +0 -0
  34. {drizzle-2.0.0 → drizzle-2.1.0}/CODE_OF_CONDUCT.md +0 -0
  35. {drizzle-2.0.0 → drizzle-2.1.0}/LICENSE.rst +0 -0
  36. {drizzle-2.0.0 → drizzle-2.1.0}/MANIFEST.in +0 -0
  37. {drizzle-2.0.0 → drizzle-2.1.0}/docs/Makefile +0 -0
  38. {drizzle-2.0.0 → drizzle-2.1.0}/docs/_templates/autosummary/base.rst +0 -0
  39. {drizzle-2.0.0 → drizzle-2.1.0}/docs/_templates/autosummary/class.rst +0 -0
  40. {drizzle-2.0.0 → drizzle-2.1.0}/docs/_templates/autosummary/module.rst +0 -0
  41. {drizzle-2.0.0 → drizzle-2.1.0}/docs/conf.py +0 -0
  42. {drizzle-2.0.0 → drizzle-2.1.0}/docs/drizzle/api.rst +0 -0
  43. {drizzle-2.0.0 → drizzle-2.1.0}/docs/drizzle/index.rst +0 -0
  44. {drizzle-2.0.0 → drizzle-2.1.0}/docs/drizzle/user.rst +0 -0
  45. {drizzle-2.0.0 → drizzle-2.1.0}/docs/exts/numfig.py +0 -0
  46. {drizzle-2.0.0 → drizzle-2.1.0}/docs/index.rst +0 -0
  47. {drizzle-2.0.0 → drizzle-2.1.0}/docs/make.bat +0 -0
  48. {drizzle-2.0.0 → drizzle-2.1.0}/docs/rtd_environment.yaml +0 -0
  49. {drizzle-2.0.0 → drizzle-2.1.0}/drizzle/__init__.py +0 -0
  50. {drizzle-2.0.0 → drizzle-2.1.0}/drizzle/tests/__init__.py +0 -0
  51. {drizzle-2.0.0 → drizzle-2.1.0}/drizzle/tests/test_cdrizzle.py +0 -0
  52. {drizzle-2.0.0 → drizzle-2.1.0}/drizzle/tests/test_overlap_calc.py +0 -0
  53. {drizzle-2.0.0 → drizzle-2.1.0}/drizzle/util.py +0 -0
  54. {drizzle-2.0.0 → drizzle-2.1.0}/drizzle.egg-info/dependency_links.txt +0 -0
  55. {drizzle-2.0.0 → drizzle-2.1.0}/drizzle.egg-info/not-zip-safe +0 -0
  56. {drizzle-2.0.0 → drizzle-2.1.0}/drizzle.egg-info/top_level.txt +0 -0
  57. {drizzle-2.0.0 → drizzle-2.1.0}/setup.cfg +0 -0
  58. {drizzle-2.0.0 → drizzle-2.1.0}/src/cdrizzleblot.c +0 -0
  59. {drizzle-2.0.0 → drizzle-2.1.0}/src/cdrizzleblot.h +0 -0
  60. {drizzle-2.0.0 → drizzle-2.1.0}/src/cdrizzleutil.h +0 -0
  61. {drizzle-2.0.0 → drizzle-2.1.0}/src/driz_portability.h +0 -0
  62. {drizzle-2.0.0 → drizzle-2.1.0}/src/tests/.clang-format +0 -0
  63. {drizzle-2.0.0 → drizzle-2.1.0}/src/tests/drizzletest.h +0 -0
  64. {drizzle-2.0.0 → drizzle-2.1.0}/src/tests/fct.h +0 -0
@@ -0,0 +1,39 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ - '*.x'
8
+ tags:
9
+ - "*"
10
+ pull_request:
11
+ schedule:
12
+ - cron: '0 6 * * 1'
13
+
14
+ concurrency:
15
+ group: ${{ github.workflow }}-${{ github.ref }}
16
+ cancel-in-progress: true
17
+
18
+ jobs:
19
+ test:
20
+ uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v1
21
+ with:
22
+ envs: |
23
+ - linux: check-style
24
+ - linux: check-security
25
+ - linux: py310-xdist
26
+ - linux: py311-xdist
27
+ - linux: py312-xdist
28
+ # `tox` does not currently respect `requires-python` versions when creating testing environments;
29
+ # if this breaks, add an upper pin to `requires-python` and revert this py3 to the latest working version
30
+ - linux: py3-cov-xdist
31
+ coverage: codecov
32
+ - macos: py3-xdist
33
+ - windows: py3-xdist
34
+ # exclude Python 1.13.4, see https://github.com/python/cpython/issues/135151
35
+ python-version: '>=3.13.5 <3.14'
36
+ - linux: py3-devdeps-xdist
37
+ toxdeps: tox-uv
38
+ secrets:
39
+ CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
@@ -4,16 +4,39 @@
4
4
  Release Notes
5
5
  =============
6
6
 
7
+
8
+ 2.0.2 (unreleased)
9
+ ==================
10
+
11
+ - Fix a bug in ``resample.Drizzle`` due to which initially, before adding
12
+ the first image, ``Drizzle.output_img`` is not filled with ``NaN`` when
13
+ ``fillval`` is either ``INDEF``, ``NAN``, ``None`` *and* the ``Drizzle``
14
+ object was initialized with ``out_img=None``. [#170]
15
+
16
+ - Fixes a crash when ``Drizzle`` is initialized with ``disable_ctx``
17
+ set to ``True``. [#180]
18
+
19
+
20
+ 2.0.1 (2025-01-28)
21
+ ==================
22
+
23
+ - Update ``utils.calc_pixmap`` code to be ready for upcoming changes in GWCS
24
+ due to which inverse WCS transformations will respect bounding box by
25
+ allowing the caller of ``utils.calc_pixmap`` to disable the bounding box(es)
26
+ on both or one of the input WCS when computing the pixel map. [#164]
27
+
28
+
7
29
  2.0.0 (2024-10-23)
8
30
  ==================
9
31
 
10
- - Backward incompatible major re-design of API. Backward compatibility was
32
+ - Backward incompatible major re-design of API to make the code I/O agnostic.
33
+ Removed FITS-specific code. Backward compatibility was
11
34
  maintained with JWST and Roman pipelines only. [#134]
12
35
 
13
36
  - Deprecated module ``util``. New software should not import from this
14
37
  module as it will be removed in a future release. [#134]
15
38
 
16
- - Bug fix: exposure time was undefined when in_units were not cps. [#134]
39
+ - Bug fix: exposure time was undefined when ``in_units`` were not "cps". [#134]
17
40
 
18
41
  - BUG FIX: ``cdrizzle.tdriz`` signature was out of date. [#134]
19
42
 
@@ -1,40 +1,8 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: drizzle
3
- Version: 2.0.0
3
+ Version: 2.1.0
4
4
  Summary: A package for combining dithered images into a single image
5
5
  Author-email: STScI <help@stsci.edu>
6
- License: Copyright (C) 2011,2014 Association of Universities for Research in
7
- Astronomy (AURA)
8
-
9
- Redistribution and use in source and binary forms, with or without
10
- modification, are permitted provided that the following conditions
11
- are met:
12
-
13
- 1. Redistributions of source code must retain the above
14
- copyright notice, this list of conditions and the following
15
- disclaimer.
16
-
17
- 2. Redistributions in binary form must reproduce the above
18
- copyright notice, this list of conditions and the following
19
- disclaimer in the documentation and/or other materials
20
- provided with the distribution.
21
-
22
- 3. The name of AURA and its representatives may not be used to
23
- endorse or promote products derived from this software without
24
- specific prior written permission.
25
-
26
- THIS SOFTWARE IS PROVIDED BY AURA ``AS IS'' AND ANY EXPRESS OR
27
- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
28
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29
- ARE DISCLAIMED. IN NO EVENT SHALL AURA BE LIABLE FOR ANY DIRECT,
30
- INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34
- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
36
- OF THE POSSIBILITY OF SUCH DAMAGE.
37
-
38
6
  Project-URL: Homepage, https://github.com/spacetelescope/drizzle
39
7
  Project-URL: Bug Tracker, https://github.com/spacetelescope/drizzle/issues
40
8
  Project-URL: Documentation, http://spacetelescope.github.io/drizzle/
@@ -43,8 +11,9 @@ Requires-Python: >=3.10
43
11
  Description-Content-Type: text/x-rst
44
12
  License-File: LICENSE.rst
45
13
  Requires-Dist: numpy
46
- Requires-Dist: astropy
47
14
  Provides-Extra: test
15
+ Requires-Dist: astropy; extra == "test"
16
+ Requires-Dist: gwcs; extra == "test"
48
17
  Requires-Dist: pytest; extra == "test"
49
18
  Requires-Dist: pytest-cov; extra == "test"
50
19
  Requires-Dist: pytest-doctestplus; extra == "test"
@@ -55,6 +24,7 @@ Requires-Dist: sphinx-automodapi; extra == "docs"
55
24
  Requires-Dist: sphinx-rtd-theme; extra == "docs"
56
25
  Requires-Dist: matplotlib; extra == "docs"
57
26
  Requires-Dist: pytest-doctestplus; extra == "docs"
27
+ Dynamic: license-file
58
28
 
59
29
  drizzle Documentation
60
30
  =====================
@@ -107,8 +77,8 @@ Requirements
107
77
  The Drizzle Algorithm
108
78
  ---------------------
109
79
 
110
- This section has been extracted from Chapter 2 of
111
- `The DrizzlePac Handbook <http://www.stsci.edu/hst/HST_overview/drizzlepac/documents/handbooks/drizzlepac.pdf>`_ [Driz2012]_
80
+ This section has been extracted from Chapter 3 of
81
+ `The DrizzlePac Handbook <https://hst-docs.stsci.edu/drizzpac>`_ [Driz2025]_
112
82
 
113
83
  There are a family of linear reconstruction techniques that, at two opposite
114
84
  extremes, are represented by the interlacing and shift-and-add techniques, with
@@ -199,4 +169,4 @@ for identifying cosmic rays in the original image. Blot requires the user
199
169
  to provide the world coordinate system (WCS)-based transformations in the
200
170
  form of a pixel map array as input.
201
171
 
202
- .. [Driz2012] Gonzaga, S., Hack, W., Fruchter, A., Mack, J., eds. 2012, The DrizzlePac Handbook. (Baltimore, STScI)
172
+ .. [Driz2025] Anand, G. S., Mack, J., et al., 2025, The DrizzlePac Handbook”, Version 3.0, (Baltimore: STScI)
@@ -49,8 +49,8 @@ Requirements
49
49
  The Drizzle Algorithm
50
50
  ---------------------
51
51
 
52
- This section has been extracted from Chapter 2 of
53
- `The DrizzlePac Handbook <http://www.stsci.edu/hst/HST_overview/drizzlepac/documents/handbooks/drizzlepac.pdf>`_ [Driz2012]_
52
+ This section has been extracted from Chapter 3 of
53
+ `The DrizzlePac Handbook <https://hst-docs.stsci.edu/drizzpac>`_ [Driz2025]_
54
54
 
55
55
  There are a family of linear reconstruction techniques that, at two opposite
56
56
  extremes, are represented by the interlacing and shift-and-add techniques, with
@@ -141,4 +141,4 @@ for identifying cosmic rays in the original image. Blot requires the user
141
141
  to provide the world coordinate system (WCS)-based transformations in the
142
142
  form of a pixel map array as input.
143
143
 
144
- .. [Driz2012] Gonzaga, S., Hack, W., Fruchter, A., Mack, J., eds. 2012, The DrizzlePac Handbook. (Baltimore, STScI)
144
+ .. [Driz2025] Anand, G. S., Mack, J., et al., 2025, The DrizzlePac Handbook”, Version 3.0, (Baltimore: STScI)
@@ -6,7 +6,7 @@ import numpy as np
6
6
 
7
7
  from drizzle import cdrizzle
8
8
 
9
- __all__ = ["blot_image", "Drizzle"]
9
+ __all__ = ["Drizzle", "blot_image"]
10
10
 
11
11
  SUPPORTED_DRIZZLE_KERNELS = [
12
12
  "square",
@@ -76,23 +76,24 @@ class Drizzle:
76
76
  information about which input image has contributed to the corresponding
77
77
  pixel in the resampled data array. Context image uses 32 bit integers to
78
78
  encode this information and hence it can keep track of only 32 input images.
79
- First bit corresponds to the first input image, second bit corrsponds to the
80
- second input image, and so on. We call this (0-indexed) order "context ID"
81
- which is represented by the ``ctx_id`` parameter/property. If the number of
79
+ The first bit corresponds to the first input image, the second bit
80
+ corresponds to the second input image, and so on.
81
+ We call this (0-indexed) order "context ID" which is represented by
82
+ the ``ctx_id`` parameter/property. If the number of
82
83
  input images exceeds 32, then it is necessary to have multiple context
83
- images ("planes") to hold information about all input images with the first
84
+ images ("planes") to hold information about all input images, with the first
84
85
  plane encoding which of the first 32 images contributed to the output data
85
- pixel, second plane representing next 32 input images (number 33-64), etc.
86
- For this reason, context array is either a 2D array (if the total number
87
- of resampled images is less than 33) of the type `numpy.int32` and shape
88
- ``(ny, nx)`` or a a 3D array of shape ``(np, ny, nx)`` where ``nx`` and
89
- ``ny`` are dimensions of image's data. ``np`` is the number of "planes"
90
- equal to ``(number of input images - 1) // 32 + 1``. If a bit at position
91
- ``k`` in a pixel with coordinates ``(p, y, x)`` is 0 then input image number
92
- ``32 * p + k`` (0-indexed) did not contribute to the output data pixel
93
- with array coordinates ``(y, x)`` and if that bit is 1 then input image
94
- number ``32 * p + k`` did contribute to the pixel ``(y, x)`` in the
95
- resampled image.
86
+ pixel, the second plane representing next 32 input images (number 33-64),
87
+ etc. For this reason, context array is either a 2D array (if the total
88
+ number of resampled images is less than 33) of the type `numpy.int32` and
89
+ shape ``(ny, nx)`` or a a 3D array of shape ``(np, ny, nx)`` where ``nx``
90
+ and ``ny`` are dimensions of the image data. ``np`` is the number of
91
+ "planes" computed as ``(number of input images - 1) // 32 + 1``. If a bit at
92
+ position ``k`` in a pixel with coordinates ``(p, y, x)`` is 0, then input
93
+ image number ``32 * p + k`` (0-indexed) did not contribute to the output
94
+ data pixel with array coordinates ``(y, x)`` and if that bit is 1, then
95
+ input image number ``32 * p + k`` did contribute to the pixel ``(y, x)``
96
+ in the resampled image.
96
97
 
97
98
  As an example, let's assume we have 8 input images. Then, when ``out_ctx``
98
99
  pixel values are displayed using binary representation (and decimal in
@@ -413,7 +414,11 @@ class Drizzle:
413
414
  )
414
415
 
415
416
  if out_img is None:
416
- self._out_img = np.zeros(out_shape, dtype=np.float32)
417
+ if self._fillval.upper() in ["INDEF", "NAN"]:
418
+ fillval = np.nan
419
+ else:
420
+ fillval = float(self._fillval)
421
+ self._out_img = np.full(out_shape, fillval, dtype=np.float32)
417
422
  else:
418
423
  self._out_img = out_img
419
424
 
@@ -548,7 +553,7 @@ class Drizzle:
548
553
 
549
554
  self._alloc_output_arrays(
550
555
  out_shape=self._out_shape,
551
- max_ctx_id=max(self._max_ctx_id, self._ctx_id),
556
+ max_ctx_id=self._max_ctx_id,
552
557
  out_img=None,
553
558
  out_wht=None,
554
559
  out_ctx=None,
@@ -0,0 +1,221 @@
1
+ import os
2
+
3
+ import gwcs
4
+ import numpy as np
5
+ from gwcs.coordinate_frames import CelestialFrame, Frame2D
6
+
7
+ from astropy import coordinates as coord
8
+ from astropy import units
9
+ from astropy import wcs as fits_wcs
10
+ from astropy.io import fits
11
+ from astropy.modeling.models import (
12
+ Mapping,
13
+ Pix2Sky_TAN,
14
+ Polynomial2D,
15
+ RotateNative2Celestial,
16
+ Shift,
17
+ )
18
+ from astropy.modeling.projections import AffineTransformation2D
19
+
20
+ __all__ = ["wcs_from_file"]
21
+
22
+ TEST_DIR = os.path.abspath(os.path.dirname(__file__))
23
+ DATA_DIR = os.path.join(TEST_DIR, 'data')
24
+
25
+
26
+ def wcs_from_file(filename, ext=None, return_data=False, crpix_shift=None,
27
+ wcs_type="fits"):
28
+ """
29
+ Read the WCS from a ".fits" file.
30
+
31
+ Parameters
32
+ ----------
33
+ filename : str
34
+ Name of the file to load WCS from.
35
+
36
+ ext : int, None, optional
37
+ Extension number to load the WCS from. When `None`, the WCS will be
38
+ loaded from the first extension containing a WCS.
39
+
40
+ return_data : bool, optional
41
+ When `True`, this function will return a tuple with first item
42
+ being the WCS and the second item being the image data array.
43
+
44
+ crpix_shift : tuple, None, optional
45
+ A tuple of two values to be added to header CRPIX values before
46
+ creating the WCS. This effectively introduces a constant shift
47
+ in the image coordinate system.
48
+
49
+ wcs_type : {"fits", "gwcs"}, optional
50
+ Return either a FITS WCS or a gwcs.
51
+
52
+ Returns
53
+ -------
54
+ WCS or tuple of WCS and image data
55
+
56
+ """
57
+ full_file_name = os.path.join(DATA_DIR, filename)
58
+ path = os.path.join(DATA_DIR, full_file_name)
59
+
60
+ def get_shape(hdr):
61
+ naxis1 = hdr.get("WCSNAX1", hdr.get("NAXIS1"))
62
+ naxis2 = hdr.get("WCSNAX2", hdr.get("NAXIS2"))
63
+ if naxis1 is None or naxis2 is None:
64
+ return None
65
+ return (naxis2, naxis1)
66
+
67
+ def data_from_hdr(hdr, data=None, shape=None):
68
+ if data is not None:
69
+ return data
70
+ bitpix = hdr.get("BITPIX", -32)
71
+ dtype = fits.hdu.BITPIX2DTYPE[bitpix]
72
+ shape = get_shape(hdr) or shape
73
+ if shape is None:
74
+ return None
75
+ return np.zeros(shape, dtype=dtype)
76
+
77
+ if os.path.splitext(filename)[1] in [".hdr", ".txt"]:
78
+ hdul = None
79
+ hdr = fits.Header.fromfile(
80
+ path,
81
+ sep='\n',
82
+ endcard=False,
83
+ padding=False
84
+ )
85
+
86
+ else:
87
+ with fits.open(path) as fits_hdul:
88
+ hdul = fits.HDUList([hdu.copy() for hdu in fits_hdul])
89
+
90
+ if ext is None and hdul is not None:
91
+ for k, u in enumerate(hdul):
92
+ if "CTYPE1" in u.header:
93
+ ext = k
94
+ break
95
+
96
+ hdr = hdul[ext].header
97
+
98
+ if crpix_shift is not None and "CRPIX1" in hdr:
99
+ hdr["CRPIX1"] += crpix_shift[0]
100
+ hdr["CRPIX2"] += crpix_shift[1]
101
+
102
+ result = fits_wcs.WCS(hdr, hdul)
103
+ shape = get_shape(hdr)
104
+ result.array_shape = shape
105
+
106
+ if wcs_type == "gwcs":
107
+ result = _gwcs_from_hst_fits_wcs(result)
108
+
109
+ if return_data:
110
+ if hdul is None:
111
+ data = data_from_hdr(hdr, data=None, shape=shape)
112
+ return (result, data)
113
+
114
+ result = (result, )
115
+ if not isinstance(return_data, (list, tuple)):
116
+ return_data = [ext]
117
+ for ext in return_data:
118
+ data = data_from_hdr(
119
+ hdul[ext].header,
120
+ data=hdul[ext].data,
121
+ shape=shape
122
+ )
123
+ result = result + (data, )
124
+
125
+ return result
126
+
127
+
128
+ def _gwcs_from_hst_fits_wcs(w):
129
+ # NOTE: this function ignores table distortions
130
+ def coeffs_to_poly(mat, degree):
131
+ pol = Polynomial2D(degree=degree)
132
+ for i in range(mat.shape[0]):
133
+ for j in range(mat.shape[1]):
134
+ if 0 < i + j <= degree:
135
+ setattr(pol, f'c{i}_{j}', mat[i, j])
136
+ return pol
137
+
138
+ nx, ny = w.pixel_shape
139
+ x0, y0 = w.wcs.crpix - 1
140
+
141
+ cd = w.wcs.piximg_matrix
142
+
143
+ if w.sip is None:
144
+ # construct GWCS:
145
+ det2sky = (
146
+ (Shift(-x0) & Shift(-y0)) |
147
+ Pix2Sky_TAN() | RotateNative2Celestial(*w.wcs.crval, 180)
148
+ )
149
+ else:
150
+ cfx, cfy = np.dot(cd, [w.sip.a.ravel(), w.sip.b.ravel()])
151
+ a = np.reshape(cfx, w.sip.a.shape)
152
+ b = np.reshape(cfy, w.sip.b.shape)
153
+ a[1, 0] = cd[0, 0]
154
+ a[0, 1] = cd[0, 1]
155
+ b[1, 0] = cd[1, 0]
156
+ b[0, 1] = cd[1, 1]
157
+
158
+ polx = coeffs_to_poly(a, w.sip.a_order)
159
+ poly = coeffs_to_poly(b, w.sip.b_order)
160
+
161
+ sip = Mapping((0, 1, 0, 1)) | (polx & poly)
162
+
163
+ # construct GWCS:
164
+ det2sky = (
165
+ (Shift(-x0) & Shift(-y0)) | sip |
166
+ Pix2Sky_TAN() | RotateNative2Celestial(*w.wcs.crval, 180)
167
+ )
168
+
169
+ detector_frame = Frame2D(
170
+ name="detector",
171
+ axes_names=("x", "y"),
172
+ unit=(units.pix, units.pix)
173
+ )
174
+ sky_frame = CelestialFrame(
175
+ reference_frame=getattr(coord, w.wcs.radesys).__call__(),
176
+ name=w.wcs.radesys,
177
+ unit=(units.deg, units.deg)
178
+ )
179
+ pipeline = [(detector_frame, det2sky), (sky_frame, None)]
180
+ gw = gwcs.wcs.WCS(pipeline)
181
+ gw.array_shape = w.array_shape
182
+ gw.bounding_box = ((-0.5, nx - 0.5), (-0.5, ny - 0.5))
183
+
184
+ if w.sip is not None:
185
+ # compute inverse SIP and re-create output GWCS
186
+
187
+ # compute inverse SIP:
188
+ hdr = gw.to_fits_sip(
189
+ max_inv_pix_error=1e-5,
190
+ inv_degree=None,
191
+ npoints=64,
192
+ crpix=w.wcs.crpix,
193
+ projection='TAN',
194
+ verbose=False
195
+ )
196
+ winv = fits_wcs.WCS(hdr)
197
+ ap = winv.sip.ap.copy()
198
+ bp = winv.sip.bp.copy()
199
+ ap[1, 0] += 1
200
+ bp[0, 1] += 1
201
+ polx_inv = coeffs_to_poly(ap, winv.sip.ap_order)
202
+ poly_inv = coeffs_to_poly(bp, winv.sip.bp_order)
203
+ af = AffineTransformation2D(
204
+ matrix=np.linalg.inv(winv.wcs.piximg_matrix)
205
+ )
206
+
207
+ # set analytical inverses:
208
+ sip.inverse = af | Mapping((0, 1, 0, 1)) | (polx_inv & poly_inv)
209
+
210
+ # construct GWCS:
211
+ det2sky = (
212
+ (Shift(-x0) & Shift(-y0)) | sip |
213
+ Pix2Sky_TAN() | RotateNative2Celestial(*w.wcs.crval, 180)
214
+ )
215
+
216
+ pipeline = [(detector_frame, det2sky), (sky_frame, None)]
217
+ gw = gwcs.wcs.WCS(pipeline)
218
+ gw.array_shape = w.array_shape
219
+ gw.bounding_box = ((-0.5, nx - 0.5), (-0.5, ny - 0.5))
220
+
221
+ return gw