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 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
|
"""
utilities for the fits library
"""
from contextlib import contextmanager
import sys
import numpy
from . import _fitsio_wrap
if sys.version_info >= (3, 0, 0):
IS_PY3 = True
else:
IS_PY3 = False
_FLOATING_NULL_VALUE = _fitsio_wrap.cfitsio_null_value_for_nan()
class FITSRuntimeWarning(RuntimeWarning):
pass
def cfitsio_version(asfloat=False):
"""
Return the cfitsio version as a string.
"""
# use string version to avoid roundoffs
ver = '%0.3f' % _fitsio_wrap.cfitsio_version()
if asfloat:
return float(ver)
else:
return ver
def cfitsio_is_bundled():
"""Return True if library was built with a
bundled copy of cfitsio.
"""
return _fitsio_wrap.cfitsio_is_bundled()
if sys.version_info > (3, 0, 0):
_itypes = (int,)
_stypes = (str, bytes)
else:
_itypes = (int, long) # noqa - only for py2
_stypes = (
basestring, # noqa - only for py2
unicode, # noqa - only for py2
) # noqa - only for py2
_itypes += (
numpy.uint8,
numpy.int8,
numpy.uint16,
numpy.int16,
numpy.uint32,
numpy.int32,
numpy.uint64,
numpy.int64,
)
# different for py3
if numpy.lib.NumpyVersion(numpy.__version__) < "1.28.0":
_stypes += (
numpy.string_,
numpy.str_,
)
else:
_stypes += (
numpy.bytes_,
numpy.str_,
)
# for header keywords
_ftypes = (float, numpy.float32, numpy.float64)
def isstring(arg):
return isinstance(arg, _stypes)
def isinteger(arg):
return isinstance(arg, _itypes)
def is_object(arr):
if arr.dtype.descr[0][1][1] == 'O':
return True
else:
return False
def fields_are_object(arr):
isobj = numpy.zeros(len(arr.dtype.names), dtype=bool)
for i, name in enumerate(arr.dtype.names):
if is_object(arr[name]):
isobj[i] = True
return isobj
def is_little_endian(array):
"""
Return True if array is little endian, False otherwise.
Parameters
----------
array: numpy array
A numerical python array.
Returns
-------
Truth value:
True for little-endian
Notes
-----
Strings are neither big or little endian. The input must be a simple numpy
array, not an array with fields.
"""
if numpy.little_endian:
machine_little = True
else:
machine_little = False
byteorder = array.dtype.base.byteorder
return (byteorder == '<') or (machine_little and byteorder == '=')
def array_to_native(array, inplace=False):
"""
Convert an array to the native byte order.
NOTE: the inplace keyword argument is not currently used.
"""
if numpy.little_endian:
machine_little = True
else:
machine_little = False
data_little = False
if array.dtype.names is None:
if array.dtype.base.byteorder == '|':
# strings and 1 byte integers
return array
data_little = is_little_endian(array)
else:
# assume all are same byte order: we only need to find one with
# little endian
for fname in array.dtype.names:
if is_little_endian(array[fname]):
data_little = True
break
if (machine_little and not data_little) or (
not machine_little and data_little
):
output = array.byteswap(inplace)
else:
output = array
return numpy.require(output, requirements=['ALIGNED'])
if numpy.lib.NumpyVersion(numpy.__version__) >= "2.0.0":
copy_if_needed = None
elif numpy.lib.NumpyVersion(numpy.__version__) < "1.28.0":
copy_if_needed = False
else:
# 2.0.0 dev versions, handle cases where copy may or may not exist
try:
numpy.array([1]).__array__(copy=None)
copy_if_needed = None
except TypeError:
copy_if_needed = False
def array_to_native_c(array_in, inplace=False):
# copy only made if not C order
arr = numpy.require(
array_in,
requirements=['C_CONTIGUOUS', 'ALIGNED'],
)
return array_to_native(arr, inplace=inplace)
def mks(val):
"""
make sure the value is a string, paying mind to python3 vs 2
"""
if sys.version_info > (3, 0, 0):
if isinstance(val, bytes):
sval = str(val, 'utf-8')
else:
sval = str(val)
else:
sval = str(val)
return sval
@contextmanager
def _nonfinite_as_cfitsio_floating_null_value(data, target_hdu_compressed):
try:
has_nonfinite = False
if (
data is not None
and data.dtype.kind == "f"
and target_hdu_compressed
):
msk_nonfinite = ~numpy.isfinite(data)
if numpy.any(msk_nonfinite):
has_nonfinite = True
old_vals = data[msk_nonfinite]
data[msk_nonfinite] = _FLOATING_NULL_VALUE
yield data, has_nonfinite
finally:
if has_nonfinite:
data[msk_nonfinite] = old_vals
|