# Licensed under a 3-clause BSD style license - see LICENSE.rstimporthtmlimportloggingimportsysimportastropy.unitsasufromastropy.coordinatesimportAltAz,Angle,EarthLocation,SkyCoordfromastropy.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=formatdef_repr_html_(self):try:returnself.to_html()exceptAttributeError:returnf"<pre>{html.escape(str(self))}</pre>"
[docs]definfo(self,file=None):"""Print some summary information 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."""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.data.gtiimportGTIreturnGTI.read(filename,hdu=hdu)elifhdu_class=="map":fromgammapy.mapsimportMapreturnMap.read(filename,hdu=hdu,format=self.format)elifhdu_class=="pointing":# FIXME: support loading the pointing tablefromgammapy.dataimportFixedPointingInforeturnFixedPointingInfo.read(filename,hdu=hdu)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
[docs]defearth_location_from_dict(meta):"""Create `~astropy.coordinates.EarthLocation` from FITS header dictionary."""lon=Angle(meta["GEOLON"],"deg")lat=Angle(meta["GEOLAT"],"deg")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):"""Convert `~astropy.coordinates.EarthLocation` to FITS header dictionary."""return{"GEOLON":location.lon.deg,"GEOLAT":location.lat.deg,"ALTITUDE":location.height.to_value(u.m),}defskycoord_from_dict(header,frame="icrs",ext="PNT"):"""Create `~astropy.coordinates.SkyCoord` from a dictionary of FITS keywords. Parameters ---------- header : dict The input dictionary. frame : {"icrs", "galactic", "altaz"} The frame to use. Default is 'icrs'. ext: str, optional The keyword extension to apply to the keywords names. Default is 'PNT'. Returns ------- skycoord : `~astropy.coordinates.skycoord` The input SkyCoord. """ext="_"+extifext!=""else""ifframe=="altaz":alt=header.get("ALT"+ext,None)az=header.get("AZ"+ext,None)return(AltAz(alt=alt*u.deg,az=az*u.deg)if(altisnotNoneandazisnotNone)elseNone)elifframe=="icrs":coords=header.get("RA"+ext,None),header.get("DEC"+ext,None)elifframe=="galactic":coords=header.get("GLON"+ext,None),header.get("GLAT"+ext,None)else:raiseValueError(f"Unsupported frame {frame}. Select in 'icrs', 'galactic', 'altaz'.")ifcoords[0]isnotNoneandcoords[1]isnotNone:returnSkyCoord(coords[0],coords[1],unit="deg",frame=frame)else:returnNone