Source code for gavo.grammars.fitsprodgrammar
"""
A grammar to parse from primary FITS headers.
This grammar will return exactly one row per source.
"""
#c Copyright 2008-2023, the GAVO project <gavo@ari.uni-heidelberg.de>
#c
#c This program is free software, covered by the GNU GPL. See the
#c COPYING file in the source distribution.
import gzip
import re
from gavo import base
from gavo.grammars.common import Grammar, RowIterator, MapKeys
from gavo.utils import fitstools
from gavo.utils import pyfits
[docs]class FITSProdIterator(RowIterator):
def _iterRows(self):
if self.grammar.qnd:
return self._parseFast()
else:
return self._parseSlow()
def _hackBotchedCard(self, card, res):
"""tries to make *anything* from a card pyfits doesn't want to parse.
In reality, I'm just trying to cope with oversized keywords.
"""
mat = re.match(r"([^\s=]*)\s*=\s*([^/]+)", card.image)
if mat:
res[mat.group(1)] = mat.group(2).strip()
else: # Card beyond recognition, ignore
pass
def _buildDictFromHeader(self, header):
res = {}
for card in header.cards:
try:
res[card.keyword.replace("-", "_")] = card.value
except (ValueError, pyfits.VerifyError):
self._hackBotchedCard(card, res)
res["header_"] = header
if self.grammar.hdusField:
res[self.grammar.hdusField] = fitstools.openFits(self.sourceToken)
return self.grammar.mapKeys.doMap(res)
def _parseFast(self):
fName = self.sourceToken
if fName.endswith(".gz"):
f = gzip.open(fName, "rb")
else:
f = open(fName, "rb")
header = fitstools.readPrimaryHeaderQuick(f,
maxHeaderBlocks=self.grammar.maxHeaderBlocks)
f.close()
yield self._buildDictFromHeader(header)
def _parseSlow(self):
fName = self.sourceToken
hdus = fitstools.openFits(fName)
hduIndex = int(self.grammar.hdu)
# fix extind to handle compressed FITSes transparently
header = hdus[fitstools.fixImageExtind(hdus, hduIndex)].header
hdus.close()
yield self._buildDictFromHeader(header)
[docs] def getLocator(self):
return self.sourceToken
[docs]class FITSProdGrammar(Grammar):
r"""A grammar that returns FITS-headers as dictionaries.
This is the grammar you want when one FITS file corresponds to one
row in the destination table.
The keywords of the grammar record are the cards in the primary
header (or some other hdu using the same-named attribute). "-" in
keywords is replaced with an underscore for easier @-referencing.
You can use a mapKeys element to effect further name cosmetics.
This grammar should handle compressed FITS images transparently if
set qnd="False". This means that you will essentially get the headers
from the second extension for those even if you left hdu="0".
The original header is preserved as the value of the header\_ key. This
is mainly intended for use WCS use, as in ``wcs.WCS(@header_)``.
If you have more complex structures in your FITS files, you can get access
to the pyfits HDU using the hdusField attribute. With
``hdusField="_H"``, you could say things like ``@_H[1].data[10][0]``
to get the first data item in the tenth row in the second HDU.
"""
name_ = "fitsProdGrammar"
_qnd = base.BooleanAttribute("qnd", default=True, description=
"Use a hack to read the FITS header more quickly. This only"
" works for the primary HDU", copyable=True)
_hduIndex = base.IntAttribute("hdu", default=0,
description="Take the header from this HDU. You must say qnd='False'"
" for this to take effect.", copyable=True)
_mapKeys = base.StructAttribute("mapKeys", childFactory=MapKeys,
default=None, copyable=True, description="Prescription for how to"
" map header keys to grammar dictionary keys")
_hdusAttr = base.UnicodeAttribute("hdusField", default=None,
description="If set, the complete pyfits HDU list for the FITS"
" file is returned in this grammar field.", copyable=True)
_maxHeaderBlocks = base.IntAttribute("maxHeaderBlocks",
default=40, copyable=True, description="Stop looking for"
" FITS END cards and raise an error after this many blocks."
" You may need to raise this for people dumping obscene amounts"
" of data or history into headers.")
rowIterator = FITSProdIterator
[docs] def onElementComplete(self):
if self.mapKeys is None:
self.mapKeys = base.makeStruct(MapKeys)
super().onElementComplete()