fitscube 2.3.2__tar.gz → 2.4.1__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.
- {fitscube-2.3.2 → fitscube-2.4.1}/.github/workflows/cd.yml +4 -4
- {fitscube-2.3.2 → fitscube-2.4.1}/.github/workflows/ci.yml +5 -5
- {fitscube-2.3.2 → fitscube-2.4.1}/PKG-INFO +1 -1
- {fitscube-2.3.2 → fitscube-2.4.1}/fitscube/__init__.py +2 -1
- {fitscube-2.3.2 → fitscube-2.4.1}/fitscube/_version.py +2 -2
- {fitscube-2.3.2 → fitscube-2.4.1}/fitscube/bounding_box.py +60 -8
- {fitscube-2.3.2 → fitscube-2.4.1}/fitscube/combine_fits.py +284 -90
- fitscube-2.4.1/fitscube/exceptions.py +25 -0
- {fitscube-2.3.2 → fitscube-2.4.1}/tests/conftest.py +70 -0
- fitscube-2.4.1/tests/test_combine.py +44 -0
- fitscube-2.4.1/tests/test_combine_guards.py +381 -0
- {fitscube-2.3.2 → fitscube-2.4.1}/tests/test_times.py +0 -41
- fitscube-2.3.2/fitscube/exceptions.py +0 -13
- {fitscube-2.3.2 → fitscube-2.4.1}/.github/CONTRIBUTING.md +0 -0
- {fitscube-2.3.2 → fitscube-2.4.1}/.github/dependabot.yml +0 -0
- {fitscube-2.3.2 → fitscube-2.4.1}/.github/release.yml +0 -0
- {fitscube-2.3.2 → fitscube-2.4.1}/.gitignore +0 -0
- {fitscube-2.3.2 → fitscube-2.4.1}/.pre-commit-config.yaml +0 -0
- {fitscube-2.3.2 → fitscube-2.4.1}/CHANGELOG.md +0 -0
- {fitscube-2.3.2 → fitscube-2.4.1}/LICENSE +0 -0
- {fitscube-2.3.2 → fitscube-2.4.1}/README.md +0 -0
- {fitscube-2.3.2 → fitscube-2.4.1}/fitscube/asyncio.py +0 -0
- {fitscube-2.3.2 → fitscube-2.4.1}/fitscube/cli.py +0 -0
- {fitscube-2.3.2 → fitscube-2.4.1}/fitscube/extract.py +0 -0
- {fitscube-2.3.2 → fitscube-2.4.1}/fitscube/logging.py +0 -0
- {fitscube-2.3.2 → fitscube-2.4.1}/fitscube/version.pyi +0 -0
- {fitscube-2.3.2 → fitscube-2.4.1}/noxfile.py +0 -0
- {fitscube-2.3.2 → fitscube-2.4.1}/pyproject.toml +0 -0
- {fitscube-2.3.2 → fitscube-2.4.1}/tests/__init__.py +0 -0
- {fitscube-2.3.2 → fitscube-2.4.1}/tests/data/cube.zip +0 -0
- {fitscube-2.3.2 → fitscube-2.4.1}/tests/data/images.zip +0 -0
- {fitscube-2.3.2 → fitscube-2.4.1}/tests/data/time_images.zip +0 -0
- {fitscube-2.3.2 → fitscube-2.4.1}/tests/data/timecube.zip +0 -0
- {fitscube-2.3.2 → fitscube-2.4.1}/tests/test_bb.py +0 -0
- {fitscube-2.3.2 → fitscube-2.4.1}/tests/test_extract.py +0 -0
- {fitscube-2.3.2 → fitscube-2.4.1}/tests/test_frequencies.py +0 -0
- {fitscube-2.3.2 → fitscube-2.4.1}/tests/test_package.py +0 -0
|
@@ -25,11 +25,11 @@ jobs:
|
|
|
25
25
|
runs-on: ubuntu-latest
|
|
26
26
|
|
|
27
27
|
steps:
|
|
28
|
-
- uses: actions/checkout@
|
|
28
|
+
- uses: actions/checkout@v7
|
|
29
29
|
with:
|
|
30
30
|
fetch-depth: 0
|
|
31
31
|
|
|
32
|
-
- uses: hynek/build-and-inspect-python-package@v2
|
|
32
|
+
- uses: hynek/build-and-inspect-python-package@v2.18.0
|
|
33
33
|
|
|
34
34
|
publish:
|
|
35
35
|
needs: [dist]
|
|
@@ -43,13 +43,13 @@ jobs:
|
|
|
43
43
|
if: github.event_name == 'release' && github.event.action == 'published'
|
|
44
44
|
|
|
45
45
|
steps:
|
|
46
|
-
- uses: actions/download-artifact@
|
|
46
|
+
- uses: actions/download-artifact@v8
|
|
47
47
|
with:
|
|
48
48
|
name: Packages
|
|
49
49
|
path: dist
|
|
50
50
|
|
|
51
51
|
- name: Generate artifact attestation for sdist and wheel
|
|
52
|
-
uses: actions/attest-build-provenance@
|
|
52
|
+
uses: actions/attest-build-provenance@v4.1.1
|
|
53
53
|
with:
|
|
54
54
|
subject-path: "dist/*"
|
|
55
55
|
|
|
@@ -21,10 +21,10 @@ jobs:
|
|
|
21
21
|
name: Format
|
|
22
22
|
runs-on: ubuntu-latest
|
|
23
23
|
steps:
|
|
24
|
-
- uses: actions/checkout@
|
|
24
|
+
- uses: actions/checkout@v7
|
|
25
25
|
with:
|
|
26
26
|
fetch-depth: 0
|
|
27
|
-
- uses: actions/setup-python@
|
|
27
|
+
- uses: actions/setup-python@v7
|
|
28
28
|
with:
|
|
29
29
|
python-version: "3.x"
|
|
30
30
|
- uses: pre-commit/action@v3.0.1
|
|
@@ -42,11 +42,11 @@ jobs:
|
|
|
42
42
|
runs-on: [ubuntu-latest]
|
|
43
43
|
|
|
44
44
|
steps:
|
|
45
|
-
- uses: actions/checkout@
|
|
45
|
+
- uses: actions/checkout@v7
|
|
46
46
|
with:
|
|
47
47
|
fetch-depth: 0
|
|
48
48
|
|
|
49
|
-
- uses: actions/setup-python@
|
|
49
|
+
- uses: actions/setup-python@v7
|
|
50
50
|
with:
|
|
51
51
|
python-version: ${{ matrix.python-version }}
|
|
52
52
|
allow-prereleases: true
|
|
@@ -62,6 +62,6 @@ jobs:
|
|
|
62
62
|
--durations=20
|
|
63
63
|
|
|
64
64
|
- name: Upload coverage report
|
|
65
|
-
uses: codecov/codecov-action@
|
|
65
|
+
uses: codecov/codecov-action@v7.0.0
|
|
66
66
|
with:
|
|
67
67
|
token: ${{ secrets.CODECOV_TOKEN }}
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from fitscube.bounding_box import BoundingBox, get_common_bounding_box
|
|
5
6
|
from fitscube.combine_fits import combine_fits
|
|
6
7
|
|
|
7
8
|
from ._version import version as __version__
|
|
8
9
|
|
|
9
|
-
__all__ = ["__version__", "combine_fits"]
|
|
10
|
+
__all__ = ["BoundingBox", "__version__", "combine_fits", "get_common_bounding_box"]
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '2.
|
|
22
|
-
__version_tuple__ = version_tuple = (2,
|
|
21
|
+
__version__ = version = '2.4.1'
|
|
22
|
+
__version_tuple__ = version_tuple = (2, 4, 1)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -10,28 +10,43 @@ from pathlib import Path
|
|
|
10
10
|
import numpy as np
|
|
11
11
|
from astropy.io import fits
|
|
12
12
|
|
|
13
|
+
from fitscube.asyncio import gather_with_limit, sync_wrapper
|
|
13
14
|
from fitscube.logging import logger
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
@dataclass(frozen=True)
|
|
17
18
|
class BoundingBox:
|
|
18
|
-
"""Simple container to represent a bounding box.
|
|
19
|
-
|
|
19
|
+
"""Simple container to represent a bounding box.
|
|
20
|
+
|
|
21
|
+
.. warning::
|
|
22
|
+
``x`` and ``y`` here are the *numpy* axes, which are the reverse of the
|
|
23
|
+
FITS ``NAXIS`` convention:
|
|
24
|
+
|
|
25
|
+
- ``x`` is axis ``-2`` -- image rows -- which is ``NAXIS2`` (declination)
|
|
26
|
+
- ``y`` is axis ``-1`` -- image columns -- which is ``NAXIS1`` (right ascension)
|
|
27
|
+
|
|
28
|
+
So a plane is sliced as ``data[..., xmin:xmax, ymin:ymax]`` and the
|
|
29
|
+
trimmed header takes ``NAXIS1 = y_span``, ``NAXIS2 = x_span``.
|
|
30
|
+
|
|
31
|
+
Minimum values are inclusive and maximum values are exclusive, so both can
|
|
32
|
+
be used as is when slicing. Note that ``xmax``/``ymax`` were *inclusive*
|
|
33
|
+
prior to ``v2.3.2``.
|
|
34
|
+
"""
|
|
20
35
|
|
|
21
36
|
xmin: int
|
|
22
|
-
"""Minimum
|
|
37
|
+
"""Minimum row pixel (numpy axis -2, FITS NAXIS2). Inclusive."""
|
|
23
38
|
xmax: int
|
|
24
|
-
"""Maximum
|
|
39
|
+
"""Maximum row pixel (numpy axis -2, FITS NAXIS2). Can be used as is in a slice (e.g. is exclusive)."""
|
|
25
40
|
ymin: int
|
|
26
|
-
"""Minimum
|
|
41
|
+
"""Minimum column pixel (numpy axis -1, FITS NAXIS1). Inclusive."""
|
|
27
42
|
ymax: int
|
|
28
|
-
"""Maximum
|
|
43
|
+
"""Maximum column pixel (numpy axis -1, FITS NAXIS1). Can be used as is in a slice (e.g. is exclusive)."""
|
|
29
44
|
original_shape: tuple[int, int]
|
|
30
45
|
"""The original shape of the image. If constructed against a cube this is the shape of a single plane."""
|
|
31
46
|
y_span: int
|
|
32
|
-
"""The span between ymax and ymin"""
|
|
47
|
+
"""The span between ymax and ymin (i.e. the trimmed NAXIS1)"""
|
|
33
48
|
x_span: int
|
|
34
|
-
"""The span between xmax and xmin"""
|
|
49
|
+
"""The span between xmax and xmin (i.e. the trimmed NAXIS2)"""
|
|
35
50
|
|
|
36
51
|
|
|
37
52
|
def create_bound_box_plane(image_data: np.ndarray) -> BoundingBox | None:
|
|
@@ -151,3 +166,40 @@ async def get_bounding_box_for_fits_coro(
|
|
|
151
166
|
data[data == 0.0] = np.nan
|
|
152
167
|
|
|
153
168
|
return await asyncio.to_thread(create_bound_box_plane, image_data=data)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
async def get_common_bounding_box_coro(
|
|
172
|
+
file_list: list[Path],
|
|
173
|
+
invalidate_zeros: bool = False,
|
|
174
|
+
max_workers: int | None = None,
|
|
175
|
+
) -> BoundingBox:
|
|
176
|
+
"""Compute the single bounding box that encompasses the valid data of every
|
|
177
|
+
image in ``file_list``.
|
|
178
|
+
|
|
179
|
+
This is the box that ``combine_fits`` computes internally when
|
|
180
|
+
``bounding_box=True``. Compute it once with this function and pass the
|
|
181
|
+
result to ``combine_fits(bounding_box=...)`` when several cubes (e.g. an
|
|
182
|
+
image cube and its weights cube) must land on an identical pixel grid.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
file_list (list[Path]): The FITS images to consider
|
|
186
|
+
invalidate_zeros (bool, optional): Mark pixels that are exactly 0.0 as invalid (NaN them). Defaults to False.
|
|
187
|
+
max_workers (int | None, optional): Maximum number of concurrent reads. Defaults to None.
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
BoundingBox: The smallest bounding box that contains all valid data
|
|
191
|
+
"""
|
|
192
|
+
boxes = await gather_with_limit(
|
|
193
|
+
max_workers,
|
|
194
|
+
*(
|
|
195
|
+
get_bounding_box_for_fits_coro(
|
|
196
|
+
fits_path=fits_path, invalidate_zeros=invalidate_zeros
|
|
197
|
+
)
|
|
198
|
+
for fits_path in file_list
|
|
199
|
+
),
|
|
200
|
+
desc="Bounding boxes",
|
|
201
|
+
)
|
|
202
|
+
return extract_common_bounding_box(bounding_boxes=boxes)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
get_common_bounding_box = sync_wrapper(get_common_bounding_box_coro)
|