roman-snpit-snappl 0.12.0__tar.gz → 0.14.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 roman-snpit-snappl might be problematic. Click here for more details.
- {roman_snpit_snappl-0.12.0/roman_snpit_snappl.egg-info → roman_snpit_snappl-0.14.0}/PKG-INFO +1 -1
- roman_snpit_snappl-0.14.0/changes/63.snappl.rst +1 -0
- roman_snpit_snappl-0.14.0/changes/72.snappl.rst +1 -0
- roman_snpit_snappl-0.14.0/changes/73.feature.rst +1 -0
- roman_snpit_snappl-0.14.0/changes/79.snappl.rst +1 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0/roman_snpit_snappl.egg-info}/PKG-INFO +1 -1
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/roman_snpit_snappl.egg-info/SOURCES.txt +5 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/snappl/_version.py +3 -3
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/snappl/diaobject.py +3 -2
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/snappl/image.py +359 -92
- roman_snpit_snappl-0.14.0/snappl/image_simulator.py +479 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/snappl/imagecollection.py +48 -8
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/snappl/psf.py +52 -37
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/.cruft.json +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/.github/CODEOWNERS +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/.github/ISSUE_TEMPLATE/ISSUE_TEMPLATE.md +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/.github/ISSUE_TEMPLATE/PR_TEMPLATE.md +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/.github/dependabot.yml +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/.github/labeler.yml +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/.github/workflows/changelog.yml +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/.github/workflows/run_labeler.yml +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/.github/workflows/run_snappl_tests.yml +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/.github/workflows/sphinx-deploy.yml +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/.github/workflows/sub_package_update.yml +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/.gitignore +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/.pre-commit-config.yaml +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/CHANGES.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/CITATION.cff +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/CODE_OF_CONDUCT.md +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/CONTRIBUTING.md +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/LICENSE +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/MANIFEST.in +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/README.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/.gitkeep +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/10.snappl.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/13.bugfix.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/14.snappl.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/15.feature.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/16.feature.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/18.feature.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/20.bugfix.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/23.snappl.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/26.feature.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/29.feature.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/3.snappl.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/31.feature.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/35.snappl.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/36.snappl.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/37.snappl.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/40.snappl.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/41.snappl.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/43.snappl.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/47.feature.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/49.docs.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/5.snappl.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/54.snappl.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/57.snappl.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/58.snappl.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/61.bugfix.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/62.snappl.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/65.bugfix.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/68.feature.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/8.snappl.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/changes/9.snappl.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/codespell-ignore.txt +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/docs/Makefile +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/docs/_static/logo_black_filled.png +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/docs/api.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/docs/changes.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/docs/conf.py +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/docs/index.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/docs/installation.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/docs/make.bat +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/docs/usage.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/experimentation/README.md +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/experimentation/play_with_photutils.py +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/licenses/.DS_Store +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/licenses/LICENSE.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/licenses/README.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/licenses/TEMPLATE_LICENSE.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/pyproject.toml +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/roman_snpit_snappl.egg-info/dependency_links.txt +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/roman_snpit_snappl.egg-info/not-zip-safe +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/roman_snpit_snappl.egg-info/requires.txt +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/roman_snpit_snappl.egg-info/top_level.txt +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/setup.cfg +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/setup.py +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/snappl/__init__.py +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/snappl/_dev/__init__.py +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/snappl/_dev/scm_version.py +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/snappl/data/README.rst +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/snappl/sed.py +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/snappl/wcs.py +0 -0
- {roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/tox.ini +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Added the pointing, sca, mjd, and header properties to ManualFITSImages.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Replaced input_wcs with image
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Add image_simulator.py ; redo image.py FITS classes ; fitsio reading/writing
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Make the URL of the roman-desc-simdex configurable
|
{roman_snpit_snappl-0.12.0 → roman_snpit_snappl-0.14.0}/roman_snpit_snappl.egg-info/SOURCES.txt
RENAMED
|
@@ -50,8 +50,12 @@ changes/57.snappl.rst
|
|
|
50
50
|
changes/58.snappl.rst
|
|
51
51
|
changes/61.bugfix.rst
|
|
52
52
|
changes/62.snappl.rst
|
|
53
|
+
changes/63.snappl.rst
|
|
53
54
|
changes/65.bugfix.rst
|
|
54
55
|
changes/68.feature.rst
|
|
56
|
+
changes/72.snappl.rst
|
|
57
|
+
changes/73.feature.rst
|
|
58
|
+
changes/79.snappl.rst
|
|
55
59
|
changes/8.snappl.rst
|
|
56
60
|
changes/9.snappl.rst
|
|
57
61
|
docs/Makefile
|
|
@@ -79,6 +83,7 @@ snappl/__init__.py
|
|
|
79
83
|
snappl/_version.py
|
|
80
84
|
snappl/diaobject.py
|
|
81
85
|
snappl/image.py
|
|
86
|
+
snappl/image_simulator.py
|
|
82
87
|
snappl/imagecollection.py
|
|
83
88
|
snappl/psf.py
|
|
84
89
|
snappl/sed.py
|
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.
|
|
32
|
-
__version_tuple__ = version_tuple = (0,
|
|
31
|
+
__version__ = version = '0.14.0'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 14, 0)
|
|
33
33
|
|
|
34
|
-
__commit_id__ = commit_id = '
|
|
34
|
+
__commit_id__ = commit_id = 'g6a12ff3d0'
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
__all__ = [ 'DiaObject', 'DiaObjectOU2024', 'DiaObjectManual' ]
|
|
2
2
|
|
|
3
|
+
from snpit_utils.config import Config
|
|
3
4
|
from snpit_utils.http import retry_post
|
|
4
5
|
|
|
5
6
|
|
|
@@ -189,8 +190,8 @@ class DiaObjectOU2024( DiaObject ):
|
|
|
189
190
|
if mjd_end_min is not None:
|
|
190
191
|
params['mjd_end_max'] = float( mjd_end_max )
|
|
191
192
|
|
|
192
|
-
|
|
193
|
-
res = retry_post( '
|
|
193
|
+
simdex = Config.get().value( 'photometry.snappl.simdex_server' )
|
|
194
|
+
res = retry_post( f'{simdex}/findtransients', json=params )
|
|
194
195
|
objinfo = res.json()
|
|
195
196
|
|
|
196
197
|
diaobjects = []
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
__all__ = [ 'Image', 'Numpy2DImage', 'FITSImage', '
|
|
1
|
+
__all__ = [ 'Image', 'Numpy2DImage', 'FITSImage', 'FITSImageStdHeaders', 'FITSImageOnDisk',
|
|
2
2
|
'OpenUniverse2024FITSImage', 'RomanDatamodelImage' ]
|
|
3
3
|
|
|
4
4
|
import re
|
|
@@ -7,6 +7,7 @@ import random
|
|
|
7
7
|
|
|
8
8
|
import numpy as np
|
|
9
9
|
import pandas
|
|
10
|
+
import fitsio
|
|
10
11
|
from astropy.io import fits
|
|
11
12
|
from astropy.nddata.utils import Cutout2D
|
|
12
13
|
from astropy.coordinates import SkyCoord
|
|
@@ -185,6 +186,11 @@ class Image:
|
|
|
185
186
|
"""Band (str)"""
|
|
186
187
|
raise NotImplementedError( f"{self.__class__.__name__} needs to implement band" )
|
|
187
188
|
|
|
189
|
+
@band.setter
|
|
190
|
+
def band(self, val):
|
|
191
|
+
raise NotImplementedError("{self.__class__.__name__} needs to implement band setter")
|
|
192
|
+
|
|
193
|
+
|
|
188
194
|
@property
|
|
189
195
|
def zeropoint( self ):
|
|
190
196
|
"""Image zeropoint for AB magnitudes.
|
|
@@ -482,6 +488,47 @@ class Image:
|
|
|
482
488
|
'Photometry cancelled.' )
|
|
483
489
|
raise
|
|
484
490
|
|
|
491
|
+
def save_data( self, which='all', path=None, noisepath=None, flagspath=None, overwrite=False ):
|
|
492
|
+
"""Same as save; here for backwards compatibility. Use save."""
|
|
493
|
+
self.save( which=which, path=path, noisepath=noisepath, flagspath=flagspath, overwrite=overwrite )
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
def save( self, which='all', path=None, noisepath=None, flagspath=None, overwrite=False ):
|
|
497
|
+
"""Save the image to its path(s).
|
|
498
|
+
|
|
499
|
+
May have side-effects on the internal data structure (e.g. FITS
|
|
500
|
+
subclasses modify the internally stored header).
|
|
501
|
+
|
|
502
|
+
Paramters
|
|
503
|
+
---------
|
|
504
|
+
which : str, default "all"
|
|
505
|
+
One of 'data', 'noise', 'flags', or 'all'
|
|
506
|
+
|
|
507
|
+
path : str, default None
|
|
508
|
+
Path to write the image to. If not specified, will use use
|
|
509
|
+
self.path. Does NOT update self.path.
|
|
510
|
+
|
|
511
|
+
noisepath : str, default None
|
|
512
|
+
Path to write the noise image to, if the noise image is
|
|
513
|
+
stored as a separate image. (It isn't always; some
|
|
514
|
+
subclasses have it as a separate part of the data structure
|
|
515
|
+
that also has the image.) If None, use an internally stored
|
|
516
|
+
noisepath. If that is not set, and noisepath is None, and
|
|
517
|
+
this isn't a subclass that combines all the data planes into
|
|
518
|
+
one file, then any noise data array will not be written.
|
|
519
|
+
|
|
520
|
+
flagspath : str, defanot None
|
|
521
|
+
Path to write the flags image to, similar to noisepath.
|
|
522
|
+
|
|
523
|
+
overwrite : bool, default False
|
|
524
|
+
Clobber existing images?
|
|
525
|
+
|
|
526
|
+
Not implemented for all subclasses.
|
|
527
|
+
|
|
528
|
+
"""
|
|
529
|
+
raise NotImplementedError( f"{self.__class__.__name} doesn't implement save" )
|
|
530
|
+
|
|
531
|
+
|
|
485
532
|
|
|
486
533
|
# ======================================================================
|
|
487
534
|
# Lots of classes will probably internally store all of data, noise, and
|
|
@@ -490,12 +537,12 @@ class Image:
|
|
|
490
537
|
class Numpy2DImage( Image ):
|
|
491
538
|
"""Abstract class for classes that store their array internall as a numpy 2d array."""
|
|
492
539
|
|
|
493
|
-
def __init__( self, *args, **kwargs ):
|
|
540
|
+
def __init__( self, *args, data=None, noise=None, flags=None, **kwargs ):
|
|
494
541
|
super().__init__( *args, **kwargs )
|
|
495
542
|
|
|
496
|
-
self._data =
|
|
497
|
-
self._noise =
|
|
498
|
-
self._flags =
|
|
543
|
+
self._data = data
|
|
544
|
+
self._noise = noise
|
|
545
|
+
self._flags = flags
|
|
499
546
|
self._image_shape = None
|
|
500
547
|
|
|
501
548
|
@property
|
|
@@ -571,22 +618,74 @@ class Numpy2DImage( Image ):
|
|
|
571
618
|
|
|
572
619
|
|
|
573
620
|
# ======================================================================
|
|
574
|
-
# A base class for FITSImages which use an AstropyWCS wcs.
|
|
575
|
-
# by itself
|
|
576
|
-
#
|
|
577
|
-
# information will be different etc. However, there will be some
|
|
578
|
-
# shared code between all FITS implementations, so that's here.
|
|
621
|
+
# A base class for FITSImages which use an AstropyWCS wcs. Of limited
|
|
622
|
+
# use by itself. Although you pass it paths, it doesn't actually
|
|
623
|
+
# read from paths; see FITSImageOnDisk for somethign that can.
|
|
579
624
|
|
|
580
625
|
class FITSImage( Numpy2DImage ):
|
|
581
626
|
"""Base class for classes that read FITS images and use an AstropyWCS wcs."""
|
|
582
627
|
|
|
583
|
-
def __init__( self, *args,
|
|
628
|
+
def __init__( self, *args, noisepath=None, flagspath=None,
|
|
629
|
+
imagehdu=0, noisehdu=0, flagshdu=0, header=None, wcs=None,
|
|
630
|
+
std_imagenames=False, **kwargs ):
|
|
584
631
|
super().__init__( *args, **kwargs )
|
|
585
632
|
|
|
586
|
-
self.
|
|
587
|
-
self.
|
|
588
|
-
|
|
589
|
-
|
|
633
|
+
self._header = header
|
|
634
|
+
self._wcs = wcs
|
|
635
|
+
|
|
636
|
+
if std_imagenames:
|
|
637
|
+
if any( i != 0 for i in ( imagehdu, noisehdu, flagshdu ) ):
|
|
638
|
+
raise ValueError( "std_imagenames requireds (image|noise|flags)hdu = 0" )
|
|
639
|
+
if ( noisepath is not None ) or ( flagspath is not None ):
|
|
640
|
+
raise ValueError( "std_imagenames can't be passed with noisepath or flagspath" )
|
|
641
|
+
|
|
642
|
+
self.imagehdu = 0
|
|
643
|
+
self.noisehdu = 0
|
|
644
|
+
self.flagshdu = 0
|
|
645
|
+
self.noisepath = self.path.parent / f"{self.path.name}_noise.fits"
|
|
646
|
+
self.flagspath = self.path.parent / f"{self.path.name}_flags.fits"
|
|
647
|
+
self.path = self.path.parent / f"{self.path.name}_image.fits"
|
|
648
|
+
|
|
649
|
+
else:
|
|
650
|
+
self.noisepath = pathlib.Path( noisepath ) if noisepath is not None else None
|
|
651
|
+
self.flagspath = pathlib.Path( flagspath ) if flagspath is not None else None
|
|
652
|
+
self.imagehdu = imagehdu
|
|
653
|
+
self.noisehdu = noisehdu
|
|
654
|
+
self.flagshdu = flagshdu
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
@classmethod
|
|
658
|
+
def _fitsio_header_to_astropy_header( cls, hdr ):
|
|
659
|
+
# I'm agog that astropy.io.fits.Header can't just take a fitsio HEADER
|
|
660
|
+
# as a constructor argument, but there you have it.
|
|
661
|
+
|
|
662
|
+
if not isinstance( hdr, fitsio.header.FITSHDR ):
|
|
663
|
+
raise TypeError( "_fitsio_header_to_astropy_header expects a fitsio.header.FITSHDR" )
|
|
664
|
+
|
|
665
|
+
ahdr = fits.Header()
|
|
666
|
+
for rec in hdr.records():
|
|
667
|
+
if 'comment' in rec:
|
|
668
|
+
ahdr[ rec['name'] ] = ( rec['value'], rec['comment'] )
|
|
669
|
+
else:
|
|
670
|
+
ahdr[ rec['name'] ] = rec['value']
|
|
671
|
+
|
|
672
|
+
return ahdr
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
@classmethod
|
|
676
|
+
def _astropy_header_to_fitsio_header( cls, ahdr ):
|
|
677
|
+
if not isinstance( ahdr, astropy.io.fits.header.Header ):
|
|
678
|
+
raise TypeError( "_astropy_header_to_fitsio_header expects a astrop.io.fits.header.Header" )
|
|
679
|
+
|
|
680
|
+
hdr = fitsio.header.FITSHDR()
|
|
681
|
+
for i, kw in enumerate( ahdr ):
|
|
682
|
+
rec = { 'name': kw, 'value': ahdr[i] }
|
|
683
|
+
if len( ahdr.comments[i] ) > 0:
|
|
684
|
+
rec['comment'] = ahdr.comments[i]
|
|
685
|
+
hdr.add_record( rec )
|
|
686
|
+
|
|
687
|
+
return hdr
|
|
688
|
+
|
|
590
689
|
|
|
591
690
|
@property
|
|
592
691
|
def image_shape(self):
|
|
@@ -602,8 +701,51 @@ class FITSImage( Numpy2DImage ):
|
|
|
602
701
|
|
|
603
702
|
return self._image_shape
|
|
604
703
|
|
|
704
|
+
def set_fits_header( self, hdr ):
|
|
705
|
+
if not isinstance( hdr, fits.Header ):
|
|
706
|
+
raise TypeError( "FITS header must be an astropy.fits.io.header.Header" )
|
|
707
|
+
self._header = hdr
|
|
708
|
+
|
|
709
|
+
# Subclasses may want to replace this with something different based on how they work
|
|
605
710
|
def get_fits_header( self ):
|
|
606
|
-
|
|
711
|
+
"""Get the header of the image."""
|
|
712
|
+
|
|
713
|
+
if self._header is None:
|
|
714
|
+
with fitsio.FITS( self.path ) as f:
|
|
715
|
+
hdr = f[ self.imagehdu ].read_header()
|
|
716
|
+
self._header = FITSImage._fitsio_header_to_astropy_header( hdr )
|
|
717
|
+
return self._header
|
|
718
|
+
|
|
719
|
+
|
|
720
|
+
def _strip_wcs_header_keywords( self ):
|
|
721
|
+
"""Try to strip all wcs keywords from self._header.
|
|
722
|
+
|
|
723
|
+
Useful as a pre-step for saving the image if you want to write
|
|
724
|
+
the WCS to the image. Using this makes sure (as best possible)
|
|
725
|
+
that you don't end up with conflicting WCS keywords in the
|
|
726
|
+
header.
|
|
727
|
+
|
|
728
|
+
This may not be complete, as it pattern matches expected keywords.
|
|
729
|
+
If it's missing some patterns, those won't get stripped.
|
|
730
|
+
|
|
731
|
+
"""
|
|
732
|
+
|
|
733
|
+
if self._header is None:
|
|
734
|
+
self._header = self.get_fits_header()
|
|
735
|
+
|
|
736
|
+
basematch = re.compile( r"^C(RVAL|RPIX|UNIT|DELT|TYPE)[12]$" )
|
|
737
|
+
cdmatch = re.compile( r"^CD[12]_[12]$" )
|
|
738
|
+
sipmatch = re.compile( r"^[AB]P?_(ORDER|(\d+)_(\d+))$" )
|
|
739
|
+
tpvmatch = re.compile( r"^P[CV]\d+_\d+$" )
|
|
740
|
+
|
|
741
|
+
tonuke = set()
|
|
742
|
+
for kw in self._header.keys():
|
|
743
|
+
if ( basematch.search(kw) or cdmatch.search(kw) or sipmatch.search(kw) or tpvmatch.search(kw) ):
|
|
744
|
+
tonuke.add( kw )
|
|
745
|
+
|
|
746
|
+
for kw in tonuke:
|
|
747
|
+
del self._header[kw]
|
|
748
|
+
|
|
607
749
|
|
|
608
750
|
def get_wcs( self, wcsclass=None ):
|
|
609
751
|
wcsclass = "AstropyWCS" if wcsclass is None else wcsclass
|
|
@@ -616,41 +758,36 @@ class FITSImage( Numpy2DImage ):
|
|
|
616
758
|
self._wcs = GalsimWCS.from_header( hdr )
|
|
617
759
|
return self._wcs
|
|
618
760
|
|
|
619
|
-
def get_data(self, which="all"):
|
|
761
|
+
def get_data( self, which="all", always_reload=False, cache=False ):
|
|
620
762
|
if self._is_cutout:
|
|
621
763
|
raise RuntimeError(
|
|
622
764
|
"get_data called on a cutout image, this will return the ORIGINAL UNCUT image. Currently not supported."
|
|
623
765
|
)
|
|
766
|
+
|
|
624
767
|
if which not in Image.data_array_list:
|
|
625
768
|
raise ValueError(f"Unknown which {which}, must be all, data, noise, or flags")
|
|
769
|
+
which = [ 'data', 'noise', 'flags' ] if which == 'all' else [ which ]
|
|
626
770
|
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
f"Data: {self._data is not None}, Noise: {self._noise is not None},"
|
|
634
|
-
f" Flags: {self._flags is not None}"
|
|
635
|
-
)
|
|
636
|
-
|
|
637
|
-
if (which == "data"):
|
|
638
|
-
if (self._data is not None):
|
|
639
|
-
return [self._data]
|
|
640
|
-
else:
|
|
641
|
-
raise RuntimeError("get_data called with which='data', but data is not set.")
|
|
771
|
+
pathmap = { 'data': self.path,
|
|
772
|
+
'noise': self.noisepath,
|
|
773
|
+
'flags': self.flagspath }
|
|
774
|
+
hdumap = { 'data': self.imagehdu,
|
|
775
|
+
'noise': self.noisehdu,
|
|
776
|
+
'flags': self.flagshdu }
|
|
642
777
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
778
|
+
rval = []
|
|
779
|
+
for plane in which:
|
|
780
|
+
prop = f'_{plane}'
|
|
781
|
+
data = getattr( self, prop )
|
|
782
|
+
if always_reload or ( data is None ):
|
|
783
|
+
with fitsio.FITS( pathmap[plane] ) as f:
|
|
784
|
+
data = f[ hdumap[plane] ].read()
|
|
785
|
+
if cache:
|
|
786
|
+
setattr( self, prop, data )
|
|
787
|
+
rval.append( data )
|
|
788
|
+
|
|
789
|
+
return rval
|
|
648
790
|
|
|
649
|
-
if (which == "flags"):
|
|
650
|
-
if (self._flags is not None):
|
|
651
|
-
return [self._flags]
|
|
652
|
-
else:
|
|
653
|
-
raise RuntimeError("get_data called with which='flags', but flags are not set.")
|
|
654
791
|
|
|
655
792
|
def get_cutout(self, x, y, xsize, ysize=None, mode='strict', fill_value=np.nan):
|
|
656
793
|
"""See Image.get_cutout
|
|
@@ -708,36 +845,169 @@ class FITSImage( Numpy2DImage ):
|
|
|
708
845
|
y = int( np.floor( y + 0.5 ) )
|
|
709
846
|
return self.get_cutout( x, y, xsize, ysize, mode=mode, fill_value=fill_value )
|
|
710
847
|
|
|
848
|
+
def save( self, which='all', path=None, noisepath=None, flagspath=None,
|
|
849
|
+
imagehdu=None, noisehdu=None, flagshdu=None, overwrite=False ):
|
|
850
|
+
"""Write image to its path. See Image.save
|
|
711
851
|
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
# A FITS image that doesn't know where it got its data from, you just
|
|
716
|
-
# feed it the data.
|
|
852
|
+
Has the side-effect of loading self._header if it is None, and
|
|
853
|
+
if replacing WCS keywords in self._header with keywords from the
|
|
854
|
+
current image WCS.
|
|
717
855
|
|
|
718
|
-
|
|
719
|
-
def __init__(self, header, data=None, noise=None, flags=None,
|
|
720
|
-
path=None, exposure=None, sca=None, *args, **kwargs):
|
|
856
|
+
"""
|
|
721
857
|
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
858
|
+
saveim = ( which == 'data' ) or ( which == 'all' )
|
|
859
|
+
saveno = ( which == 'noise' ) or ( which == 'all' )
|
|
860
|
+
savefl = ( which == 'flags' ) or ( which == 'all' )
|
|
861
|
+
|
|
862
|
+
imagehdu = imagehdu if imagehdu is not None else self.imagehdu
|
|
863
|
+
noisehdu = noisehdu if noisehdu is not None else self.noisehdu
|
|
864
|
+
flagshdu = flagshdu if flagshdu is not None else self.flagshdu
|
|
865
|
+
|
|
866
|
+
if ( imagehdu != 0 ) or ( noisehdu != 0 ) or ( flagshdu != 0 ):
|
|
867
|
+
raise NotImplementedError( "We need to implement saving to HDUs other than 0." )
|
|
868
|
+
|
|
869
|
+
path = path if path is not None else self.path
|
|
870
|
+
if saveim and ( path is None ):
|
|
871
|
+
raise RuntimeError( "Can't save data, no path." )
|
|
872
|
+
noisepath = noisepath if noisepath is not None else self.noisepath
|
|
873
|
+
if saveno and ( noisepath is None ):
|
|
874
|
+
raise RuntimeError( "Can't save noise, no path." )
|
|
875
|
+
flagspath = flagspath if flagspath is not None else self.flagspath
|
|
876
|
+
if savefl and ( flagspath is None ):
|
|
877
|
+
raise RuntimeError( "Can't save flags, no path." )
|
|
878
|
+
|
|
879
|
+
if not overwrite:
|
|
880
|
+
if ( path.exists() or
|
|
881
|
+
( noisepath is not None and noisepath.exists() ) or
|
|
882
|
+
( flagspath is not None and flagspath.exists() ) ):
|
|
883
|
+
raise RuntimeError( "FITSImage.save: overwrite is False, but image file(s) already exist" )
|
|
884
|
+
else:
|
|
885
|
+
if path.is_file():
|
|
886
|
+
path.unlink()
|
|
887
|
+
if ( noisepath is not None ) and ( noisepath.is_file() ):
|
|
888
|
+
noisepath.unlink()
|
|
889
|
+
if ( flagspath is not None ) and ( flagspath.is_file() ):
|
|
890
|
+
flagspath.unlink()
|
|
891
|
+
|
|
892
|
+
# Make sure header is loaded
|
|
893
|
+
self.get_fits_header()
|
|
894
|
+
try:
|
|
895
|
+
apwcs = self.get_wcs().get_astropy_wcs( readonly=True )
|
|
896
|
+
wcshdr = apwcs.to_header()
|
|
897
|
+
self._strip_wcs_header_keywords()
|
|
898
|
+
self._header.extend( wcshdr )
|
|
899
|
+
except Exception:
|
|
900
|
+
wcshdr = None
|
|
901
|
+
|
|
902
|
+
with fitsio.FITS( path, 'rw' ) as f:
|
|
903
|
+
f.write( self.data, header=FITSImage._astropy_header_to_fitsio_header( self._header ) )
|
|
904
|
+
if ( noisepath is not None ) and ( self.noise is not None ):
|
|
905
|
+
with fitsio.FITS( noisepath, 'rw' ) as f:
|
|
906
|
+
f.write( self.noise, header=FITSImage._astropy_header_to_fitsio_header( wcshdr ) )
|
|
907
|
+
if ( self.flagspath is not None ) and ( self.flags is not None ):
|
|
908
|
+
with fitsio.FITS( flagspath, 'rw' ) as f:
|
|
909
|
+
f.write( self.flags, header=FITSImage._astropy_header_to_fitsio_header( wcshdr ) )
|
|
729
910
|
|
|
730
|
-
self.path = None
|
|
731
|
-
self.exposure = None
|
|
732
|
-
self.sca = None
|
|
733
911
|
|
|
734
|
-
|
|
912
|
+
# ======================================================================
|
|
913
|
+
# FITSImageStdHeaders
|
|
914
|
+
#
|
|
915
|
+
# A FITSImage that knows it has information in header keywords
|
|
916
|
+
# that can be configurated at instantiation time.
|
|
917
|
+
|
|
918
|
+
class FITSImageStdHeaders( FITSImage ):
|
|
919
|
+
def __init__( self, *args,
|
|
920
|
+
header_kws = {
|
|
921
|
+
'band': "BAND",
|
|
922
|
+
'exptime': "EXPTIME",
|
|
923
|
+
'mjd': "MJD",
|
|
924
|
+
'pointing': "POINTING",
|
|
925
|
+
'sca': "SCA",
|
|
926
|
+
'zeropoint': "ZPT" },
|
|
927
|
+
**kwargs ):
|
|
928
|
+
super().__init__( *args, **kwargs )
|
|
929
|
+
self._header_kws = header_kws
|
|
735
930
|
|
|
736
|
-
|
|
931
|
+
|
|
932
|
+
def get_fits_header( self ):
|
|
737
933
|
if self._header is None:
|
|
738
|
-
|
|
934
|
+
try:
|
|
935
|
+
self._header = FITSImage.get_fits_header( self )
|
|
936
|
+
except Exception:
|
|
937
|
+
self._header = fits.header.Header()
|
|
739
938
|
return self._header
|
|
740
939
|
|
|
940
|
+
@property
|
|
941
|
+
def mjd( self ):
|
|
942
|
+
hdr = self.get_fits_header()
|
|
943
|
+
return hdr[ self._header_kws['band'] ]
|
|
944
|
+
|
|
945
|
+
@mjd.setter
|
|
946
|
+
def mjd( self, val ):
|
|
947
|
+
hdr = self.get_fits_header()
|
|
948
|
+
hdr[ self._header_kws['band'] ] = val
|
|
949
|
+
|
|
950
|
+
@property
|
|
951
|
+
def pointing( self ):
|
|
952
|
+
hdr = self.get_fits_header()
|
|
953
|
+
return hdr[ self._header_kws['pointing'] ]
|
|
954
|
+
|
|
955
|
+
@pointing.setter
|
|
956
|
+
def pointing( self, val ):
|
|
957
|
+
hdr = self.get_fits_header()
|
|
958
|
+
hdr[ self._header_kws['pointing'] ] = val
|
|
959
|
+
|
|
960
|
+
@property
|
|
961
|
+
def sca( self ):
|
|
962
|
+
hdr = self.get_fits_header()
|
|
963
|
+
return hdr[ self._header_kws['sca'] ]
|
|
964
|
+
|
|
965
|
+
@sca.setter
|
|
966
|
+
def sca( self, val ):
|
|
967
|
+
hdr = self.get_fits_header()
|
|
968
|
+
hdr[ self._header_kws['sca'] ] = val
|
|
969
|
+
|
|
970
|
+
@property
|
|
971
|
+
def band( self ):
|
|
972
|
+
hdr = self.get_fits_header()
|
|
973
|
+
return hdr[ self._header_kws['band'] ]
|
|
974
|
+
|
|
975
|
+
@band.setter
|
|
976
|
+
def band( self, val ):
|
|
977
|
+
hdr = self.get_fits_header()
|
|
978
|
+
hdr[ self._header_kws['band'] ] = val
|
|
979
|
+
|
|
980
|
+
@property
|
|
981
|
+
def zeropoint( self ):
|
|
982
|
+
hdr = self.get_fits_header()
|
|
983
|
+
return hdr[ self._header_kws['zeropoint'] ]
|
|
984
|
+
|
|
985
|
+
@zeropoint.setter
|
|
986
|
+
def zeropoint( self, val ):
|
|
987
|
+
hdr = self.get_fits_header()
|
|
988
|
+
hdr[ self._header_kws['zeropoint'] ] = val
|
|
989
|
+
|
|
990
|
+
@property
|
|
991
|
+
def mjd( self ):
|
|
992
|
+
hdr = self.get_fits_header()
|
|
993
|
+
return hdr[ self._header_kws['mjd'] ]
|
|
994
|
+
|
|
995
|
+
@mjd.setter
|
|
996
|
+
def mjd( self, val ):
|
|
997
|
+
hdr = self.get_fits_header()
|
|
998
|
+
hdr[ self._header_kws['mjd'] ] = val
|
|
999
|
+
|
|
1000
|
+
@property
|
|
1001
|
+
def exptime( self ):
|
|
1002
|
+
hdr = self.get_fits_header()
|
|
1003
|
+
return hdr[ self._header_kws['exptime'] ]
|
|
1004
|
+
|
|
1005
|
+
@exptime.setter
|
|
1006
|
+
def exptime( self, val ):
|
|
1007
|
+
hdr = self.get_fits_header()
|
|
1008
|
+
hdr[ self._header_kws['exptime'] ] = val
|
|
1009
|
+
|
|
1010
|
+
|
|
741
1011
|
|
|
742
1012
|
# ======================================================================
|
|
743
1013
|
# A class that's a FITS Image with a corresponding file or
|
|
@@ -758,15 +1028,9 @@ class FITSImageOnDisk( FITSImage ):
|
|
|
758
1028
|
|
|
759
1029
|
"""
|
|
760
1030
|
|
|
761
|
-
def __init__( self, *args,
|
|
762
|
-
imhdu=0, noisehdu=0, flagshdu=0, **kwargs ):
|
|
1031
|
+
def __init__( self, *args, **kwargs ):
|
|
763
1032
|
super().__init__( *args, **kwargs )
|
|
764
1033
|
|
|
765
|
-
self.noisepath = pathlib.Path( noisepath ) if noisepath is not None else None
|
|
766
|
-
self.flagspath = pathlib.Path( flagspath ) if flagspath is not None else None
|
|
767
|
-
self.imhdu = imhdu
|
|
768
|
-
self.noisehdu = noisehdu
|
|
769
|
-
self.flagshdu = flagshdu
|
|
770
1034
|
|
|
771
1035
|
def uncompressed_version( self, include=[ 'data', 'noise', 'flags' ], base_dir=None ):
|
|
772
1036
|
"""Make sure to get a FITSImageOnDisk that's not compressed.
|
|
@@ -830,12 +1094,6 @@ class FITSImageOnDisk( FITSImage ):
|
|
|
830
1094
|
raise TypeError( f"hdr must be a fits.header.Header, not a {type(hdr)}" )
|
|
831
1095
|
self._header = hdr
|
|
832
1096
|
|
|
833
|
-
def get_fits_header( self ):
|
|
834
|
-
if self._header is None:
|
|
835
|
-
with fits.open( self.path ) as hdul:
|
|
836
|
-
self._header = hdul[ self.imhdu ].header
|
|
837
|
-
return self._header
|
|
838
|
-
|
|
839
1097
|
def get_data( self, which="all", always_reload=False, cache=False ):
|
|
840
1098
|
if self._is_cutout:
|
|
841
1099
|
raise RuntimeError( "get_data called on a cutout image, this will return the ORIGINAL UNCUT image. "
|
|
@@ -861,29 +1119,29 @@ class FITSImageOnDisk( FITSImage ):
|
|
|
861
1119
|
|
|
862
1120
|
# Open the data, and do everything else inside that with just in case
|
|
863
1121
|
# noise and flags are part of the same FITS image.
|
|
864
|
-
with
|
|
1122
|
+
with fitsio.FITS( self.path ) as imagefits:
|
|
865
1123
|
if "data" in toload:
|
|
866
|
-
header =
|
|
867
|
-
data =
|
|
1124
|
+
header = FITSImage._fitsio_header_to_astropy_header( imagefits[ self.imagehdu ].read_header() )
|
|
1125
|
+
data = imagefits[ self.imagehdu ].read()
|
|
868
1126
|
if cache:
|
|
869
1127
|
self._data = data
|
|
870
1128
|
self._header = header
|
|
871
1129
|
|
|
872
1130
|
if "noise" in toload:
|
|
873
|
-
if self.noisepath is not None:
|
|
874
|
-
with
|
|
875
|
-
noise =
|
|
1131
|
+
if ( self.noisepath is not None ) and ( self.noisepath != self.path ):
|
|
1132
|
+
with fitsio.FITS( self.noisepath ) as noisefits:
|
|
1133
|
+
noise = noisefits[ self.noisehdu ].read()
|
|
876
1134
|
else:
|
|
877
|
-
noise =
|
|
1135
|
+
noise = imagefits[ self.noisehdu ].read()
|
|
878
1136
|
if cache:
|
|
879
1137
|
self._noise = noise
|
|
880
1138
|
|
|
881
1139
|
if "flags" in toload:
|
|
882
|
-
if self.flagspath is not None:
|
|
883
|
-
with
|
|
884
|
-
flags =
|
|
1140
|
+
if ( self.flagspath is not None ) and ( self.flagspath != self.path ):
|
|
1141
|
+
with fitsio.FITS( self.flagspath ) as flagsfits:
|
|
1142
|
+
flags = flagsfits[ self.flagshdu ].read()
|
|
885
1143
|
else:
|
|
886
|
-
flags =
|
|
1144
|
+
flags = imagefits[ self.flagshdu ].read()
|
|
887
1145
|
if cache:
|
|
888
1146
|
self._flags = flags
|
|
889
1147
|
|
|
@@ -893,14 +1151,23 @@ class FITSImageOnDisk( FITSImage ):
|
|
|
893
1151
|
else [ data, noise, flags ] )
|
|
894
1152
|
|
|
895
1153
|
|
|
896
|
-
def
|
|
897
|
-
path=None,
|
|
1154
|
+
def save( self, which="all", overwrite=False,
|
|
1155
|
+
path=None, imagehdu=0,
|
|
898
1156
|
noisepath=None, noisehdu=None,
|
|
899
1157
|
flagspath=None, flagshdu=None ):
|
|
900
1158
|
"""Write the data to the file."""
|
|
901
|
-
raise NotImplementedError( "OMG needs to be done" )
|
|
902
1159
|
|
|
1160
|
+
path = path if path is not None else self.path
|
|
1161
|
+
noisepath = noisepath if noisepath is not None else self.noisepath
|
|
1162
|
+
flagspath = flagspath if flagspath is not None else self.flagspath
|
|
1163
|
+
|
|
1164
|
+
if not all( ( p is None ) or ( p.name[-5:] == '.fits' ) for p in [ path, noisepath, flagspath ] ):
|
|
1165
|
+
raise NotImplementedError( "I don't know how to save compressed files, only files "
|
|
1166
|
+
"whose names end in .fits" )
|
|
903
1167
|
|
|
1168
|
+
FITSImage.save( self, which=which, overwrite=overwrite,
|
|
1169
|
+
path=path, noisepath=noisepath, flagspath=flagspath,
|
|
1170
|
+
imagehdu=imagehdu, noisehdu=noisehdu, flagshdu=flagshdu )
|
|
904
1171
|
|
|
905
1172
|
|
|
906
1173
|
# ======================================================================
|
|
@@ -911,10 +1178,10 @@ class FITSImageOnDisk( FITSImage ):
|
|
|
911
1178
|
# HDU 3 : DQ (32-bit integer)
|
|
912
1179
|
|
|
913
1180
|
class OpenUniverse2024FITSImage( FITSImageOnDisk ):
|
|
914
|
-
def __init__( self, *args, noisepath=None, flagspath=None,
|
|
1181
|
+
def __init__( self, *args, noisepath=None, flagspath=None, imagehdu=1, noisehdu=2, flagshdu=3, **kwargs ):
|
|
915
1182
|
super().__init__( *args,
|
|
916
1183
|
noisepath=noisepath, flagspath=flagspath,
|
|
917
|
-
|
|
1184
|
+
imagehdu=imagehdu, noisehdu=noisehdu, flagshdu=flagshdu,
|
|
918
1185
|
**kwargs )
|
|
919
1186
|
|
|
920
1187
|
_filenamere = re.compile( r'^Roman_TDS_simple_model_(?P<band>[^_]+)_(?P<pointing>\d+)_(?P<sca>\d+).fits' )
|