# Licensed under a 3-clause BSD style license - see LICENSE.rstimportloggingimportsysimportastropy.unitsasufromastropy.coordinatesimportAngle,EarthLocationfromastropy.ioimportfitsfromastropy.unitsimportQuantityfrom.scriptsimportmake_pathlog=logging.getLogger(__name__)__all__=["earth_location_from_dict","LazyFitsData","HDULocation"]
[docs]classHDULocation:"""HDU localisation, loading and Gammapy object mapper. This represents one row in `HDUIndexTable`. It's more a helper class, that is wrapped by `~gammapy.data.Observation`, usually those objects will be used to access data. See also :ref:`gadf:hdu-index`. """def__init__(self,hdu_class,base_dir=".",file_dir=None,file_name=None,hdu_name=None,cache=True,format=None,):self.hdu_class=hdu_classself.base_dir=base_dirself.file_dir=file_dirself.file_name=file_nameself.hdu_name=hdu_nameself.cache=cacheself.format=format
[docs]definfo(self,file=None):"""Print some summary info to stdout."""ifnotfile:file=sys.stdoutprint(f"HDU_CLASS = {self.hdu_class}",file=file)print(f"BASE_DIR = {self.base_dir}",file=file)print(f"FILE_DIR = {self.file_dir}",file=file)print(f"FILE_NAME = {self.file_name}",file=file)print(f"HDU_NAME = {self.hdu_name}",file=file)
[docs]defpath(self,abs_path=True):"""Full filename path. Include ``base_dir`` if ``abs_path`` is True. """path=make_path(self.base_dir)/self.file_dir/self.file_nameifabs_pathandpath.exists():returnpathelse:returnmake_path(self.file_dir)/self.file_name
[docs]defget_hdu(self):"""Get HDU."""filename=self.path(abs_path=True)# Here we're intentionally not calling `with fits.open`# because we don't want the file to remain open.hdu_list=fits.open(str(filename),memmap=False)returnhdu_list[self.hdu_name]
[docs]defload(self):"""Load HDU as appropriate class. TODO: this should probably go via an extensible registry. """fromgammapy.irfimportIRF_REGISTRYhdu_class=self.hdu_classfilename=self.path()hdu=self.hdu_nameifhdu_class=="events":fromgammapy.dataimportEventListreturnEventList.read(filename,hdu=hdu)elifhdu_class=="gti":fromgammapy.dataimportGTIreturnGTI.read(filename,hdu=hdu)elifhdu_class=="map":fromgammapy.mapsimportMapreturnMap.read(filename,hdu=hdu,format=self.format)else:cls=IRF_REGISTRY.get_cls(hdu_class)returncls.read(filename,hdu=hdu)
[docs]classLazyFitsData(object):"""A lazy FITS data descriptor. Parameters ---------- cache : bool Whether to cache the data. """def__init__(self,cache=True):self.cache=cachedef__set_name__(self,owner,name):self.name=namedef__get__(self,instance,objtype):ifinstanceisNone:# Accessed on a class, not an instancereturnselftry:returninstance.__dict__[self.name]exceptKeyError:hdu_loc=instance.__dict__[f"_{self.name}_hdu"]try:value=hdu_loc.load()exceptKeyError:value=Nonelog.warning(f"HDU '{hdu_loc.hdu_name}' not found")ifself.cacheandhdu_loc.cache:instance.__dict__[self.name]=valuereturnvaluedef__set__(self,instance,value):ifisinstance(value,HDULocation):instance.__dict__[f"_{self.name}_hdu"]=valueelse:instance.__dict__[self.name]=value
# TODO: add unit test
[docs]defearth_location_from_dict(meta):"""Create `~astropy.coordinates.EarthLocation` from FITS header dict."""lon=Angle(meta["GEOLON"],"deg")lat=Angle(meta["GEOLAT"],"deg")# TODO: should we support both here?# Check latest spec if ALTITUDE is used somewhere.if"GEOALT"inmeta:height=Quantity(meta["GEOALT"],"meter")elif"ALTITUDE"inmeta:height=Quantity(meta["ALTITUDE"],"meter")else:raiseKeyError("The GEOALT or ALTITUDE header keyword must be set")returnEarthLocation(lon=lon,lat=lat,height=height)
defearth_location_to_dict(location):"""Create `~astropy.coordinates.EarthLocation` from FITS header dict."""return{"GEOLON":location.lon.deg,"GEOLAT":location.lat.deg,"ALTITUDE":location.height.to_value(u.m),}