1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
|
# The read and write methods for SpectralCube, StokesSpectralCube, and
# LowerDimensionalObject are defined in this file and then added to the classes
# using UnifiedReadWriteMethod. This makes it possible to dynamically add the
# available formats to the read/write docstrings. For more information about
# the unified I/O framework from Astropy which is used to implement this, see
# http://docs.astropy.org/en/stable/io/unified.html
from pathlib import PosixPath
import warnings
from astropy.io import registry
from ..utils import StokesWarning
__doctest_skip__ = ['SpectralCubeRead',
'SpectralCubeWrite',
'StokesSpectralCubeRead',
'StokesSpectralCubeWrite',
'LowerDimensionalObjectWrite']
DOCSTRING_READ_TEMPLATE = """
Read and parse a dataset and return as a {clsname}
This allows easily reading a dataset in several supported data
formats using syntax such as::
>>> from spectral_cube import {clsname}
>>> cube1 = {clsname}.read('cube.fits', format='fits')
>>> cube2 = {clsname}.read('cube.image', format='casa')
{notes}
Get help on the available readers for {clsname} using the``help()`` method::
>>> {clsname}.read.help() # Get help reading {clsname} and list supported formats
>>> {clsname}.read.help('fits') # Get detailed help on {clsname} FITS reader
>>> {clsname}.read.list_formats() # Print list of available formats
See also: http://docs.astropy.org/en/stable/io/unified.html
Parameters
----------
*args : tuple, optional
Positional arguments passed through to data reader. If supplied the
first argument is typically the input filename.
format : str
File format specifier.
**kwargs : dict, optional
Keyword arguments passed through to data reader.
Returns
-------
cube : `{clsname}`
{clsname} corresponding to dataset
Notes
-----
"""
DOCSTRING_WRITE_TEMPLATE = """
Write this {clsname} object out in the specified format.
This allows easily writing a dataset in many supported data formats
using syntax such as::
>>> data.write('data.fits', format='fits')
Get help on the available writers for {clsname} using the``help()`` method::
>>> {clsname}.write.help() # Get help writing {clsname} and list supported formats
>>> {clsname}.write.help('fits') # Get detailed help on {clsname} FITS writer
>>> {clsname}.write.list_formats() # Print list of available formats
See also: http://docs.astropy.org/en/stable/io/unified.html
Parameters
----------
*args : tuple, optional
Positional arguments passed through to data writer. If supplied the
first argument is the output filename.
format : str
File format specifier.
**kwargs : dict, optional
Keyword arguments passed through to data writer.
Notes
-----
"""
# Due to a bug in the astropy I/O infrastructure which causes an exception
# for directories (which we need for .image), we need to wrap the filenames in
# a custom string so that astropy doesn't try and call get_readable_fileobj on
# them.
class StringWrapper:
def __init__(self, value):
self.value = value
class SpectralCubeRead(registry.UnifiedReadWrite):
__doc__ = DOCSTRING_READ_TEMPLATE.format(clsname='SpectralCube',
notes="If the file contains Stokes axes, they will automatically be dropped. If "
"you want to read in all Stokes informtion, use "
":meth:`~spectral_cube.StokesSpectralCube.read` instead.")
def __init__(self, instance, cls):
super().__init__(instance, cls, 'read')
def __call__(self, filename, *args, **kwargs):
from ..spectral_cube import BaseSpectralCube
if isinstance(filename, PosixPath):
filename = str(filename)
kwargs['target_cls'] = BaseSpectralCube
try:
return registry.read(BaseSpectralCube, filename, *args, **kwargs)
except IsADirectoryError: # See note above StringWrapper
return registry.read(BaseSpectralCube, StringWrapper(filename), *args, **kwargs)
class SpectralCubeWrite(registry.UnifiedReadWrite):
__doc__ = DOCSTRING_WRITE_TEMPLATE.format(clsname='SpectralCube')
def __init__(self, instance, cls):
super().__init__(instance, cls, 'write')
def __call__(self, *args, serialize_method=None, **kwargs):
registry.write(self._instance, *args, **kwargs)
class StokesSpectralCubeRead(registry.UnifiedReadWrite):
__doc__ = DOCSTRING_READ_TEMPLATE.format(clsname='StokesSpectralCube',
notes="If the file contains Stokes axes, they will be read in. If you are only "
"interested in the unpolarized emission (I), you can use "
":meth:`~spectral_cube.SpectralCube.read` instead.")
def __init__(self, instance, cls):
super().__init__(instance, cls, 'read')
def __call__(self, filename, *args, **kwargs):
from ..stokes_spectral_cube import StokesSpectralCube
if isinstance(filename, PosixPath):
filename = str(filename)
kwargs['target_cls'] = StokesSpectralCube
try:
return registry.read(StokesSpectralCube, filename, *args, **kwargs)
except IsADirectoryError: # See note above StringWrapper
return registry.read(StokesSpectralCube, StringWrapper(filename), *args, **kwargs)
class StokesSpectralCubeWrite(registry.UnifiedReadWrite):
__doc__ = DOCSTRING_WRITE_TEMPLATE.format(clsname='StokesSpectralCube')
def __init__(self, instance, cls):
super().__init__(instance, cls, 'write')
def __call__(self, *args, serialize_method=None, **kwargs):
registry.write(self._instance, *args, **kwargs)
class LowerDimensionalObjectWrite(registry.UnifiedReadWrite):
__doc__ = DOCSTRING_WRITE_TEMPLATE.format(clsname='LowerDimensionalObject')
def __init__(self, instance, cls):
super().__init__(instance, cls, 'write')
def __call__(self, *args, serialize_method=None, **kwargs):
registry.write(self._instance, *args, **kwargs)
def normalize_cube_stokes(cube, target_cls=None):
from ..spectral_cube import BaseSpectralCube
from ..stokes_spectral_cube import StokesSpectralCube
if target_cls is BaseSpectralCube and isinstance(cube, StokesSpectralCube):
if hasattr(cube, 'I'):
warnings.warn("Cube is a Stokes cube, "
"returning spectral cube for I component",
StokesWarning)
return cube.I
else:
raise ValueError("Spectral cube is a Stokes cube that "
"does not have an I component")
elif target_cls is StokesSpectralCube and isinstance(cube, BaseSpectralCube):
return StokesSpectralCube({'I': cube})
else:
return cube
|