#!/usr/bin/env python
# encoding: utf-8
# Created by Brian Cherinka on 2016-05-17 10:17:35
# Licensed under a 3-clause BSD license.
# Revision History:
# Initial Version: 2016-05-17 10:17:35 by Brian Cherinka
# Last Modified On: 2016-05-17 10:17:35 by Brian
from __future__ import division, print_function
import inspect
from astropy.io import fits
from marvin import config
from marvin.core.exceptions import MarvinError
from marvin.tools.cube import Cube
from marvin.utils.general.structs import FuzzyList
from .core import MarvinToolsClass
try:
from sdss_access.path import Path
except ImportError:
Path = None
[docs]class Plate(MarvinToolsClass, FuzzyList):
'''A class to interface with MaNGA Plate.
This class represents a Plate, initialised either
from a file, a database, or remotely via the Marvin API. The class
inherits from Python's list class, and is defined as a list of
Cube objects. As it inherits from list, it can do all the standard Python
list operations.
When instanstantiated, Marvin Plate will attempt to discover and load all the Cubes
associated with this plate.
Parameters:
plate (str):
The plate id of the Plate to load.
plateifu (str):
The plate-ifu of the Plate to load
filename (str):
The path of the file containing the data cube to load.
mode ({'local', 'remote', 'auto'}):
The load mode to use. See
:doc:`Mode secision tree</mode_decision>`..
release (str):
The MPL/DR version of the data to use.
nocubes (bool):
Set this to turn off the Cube loading
Attributes:
cubeXXXX (object):
The Marvin Cube object for the given ifu, e.g. cube1901 refers to the Cube for plateifu 8485-1901
plate/plateid (int):
The plate id for this plate
cartid (str):
The cart id for this plate
designid (int):
The design id for this plate
ra (float):
The RA of the plate center
dec (float):
The declination of the plate center
dateobs (str):
The date of observation for this plate
surveymode (str):
The survey mode for this plate
isbright (bool):
True if this is a bright time plate
Return:
plate:
An object representing the Plate entity. The object is a list of
Cube objects, one for each IFU cube in the Plate entity.
Example:
>>> from marvin.tools.plate import Plate
>>> plate = Plate(plate=8485)
>>> print(plate)
>>> <Marvin Plate (plate=8485, n_cubes=17, mode='local', data_origin='db')>
>>>
>>> print('Cubes found in this plate: {0}'.format(len(plate)))
>>> Cubes found in this plate: 4
>>>
>>> # access the plate via index to access the individual cubes
>>> plate[0]
>>> <Marvin Cube (plateifu='8485-12701', mode='local', data_origin='db')>
>>>
>>> # or by name
>>> plate['12702']
>>> <Marvin Cube (plateifu='8485-12702', mode='local', data_origin='db')>
>>>
'''
def __init__(self, input=None, filename=None, mangaid=None, plateifu=None,
mode=None, data=None, release=None, plate=None,
download=None, nocubes=None):
self._cubes = None
self._plate = None
self._pdict = None
self.platedir = None
self.nocubes = nocubes
# If plateid specified, force a temp plateifu
if plate:
self.plateid = plate
plateifu = '{0}-XXXX'.format(self.plateid)
self.plateifu = plateifu
args = [plate, plateifu]
assert any(args), 'Enter plate or plateifu!'
MarvinToolsClass.__init__(self, input=input, filename=filename, mangaid=mangaid,
plateifu=plateifu, mode=mode, data=data, release=release,
download=download)
# sort out any plateid, plate-ifu, mangaid name snafus
self._sortOutNames()
# grab the plate info
if self.data_origin == 'file':
self._getPlateFromFile()
elif self.data_origin == 'db':
self._getPlateFromDB()
elif self.data_origin == 'api':
self._getPlateFromAPI()
# load the plate params and init the Marvin Cubes
self._setParams()
if not self.nocubes:
self._initCubes()
def __repr__(self):
'''Representation for Plate.'''
return ('<Marvin Plate (plate={self.plateid!r}, n_cubes={0}, mode={self.mode!r}, '
'data_origin={self.data_origin!r})>'.format(len(self), self=self))
def __dir__(self):
''' Overriding dir for Plate '''
# get the attributes from the class itself
class_members = list(list(zip(*inspect.getmembers(self.__class__)))[0])
instance_attr = list(self.__dict__.keys())
# get the dir from FuzzyList
listattr = ['cube{0}'.format(i.plateifu.split('-')[1]) for i in self]
listattr.sort()
return listattr + sorted(class_members + instance_attr)
def __getattr__(self, value):
if 'cube' in value:
ifu = value.split('cube')[-1]
plateifu = '{0}-{1}'.format(self.plate, ifu)
return self[plateifu]
return super(Plate, self).__getattribute__(value)
def _getFullPath(self, **kwargs):
"""Returns the full path of the file in the tree."""
self.filename = super(Plate, self)._getFullPath('mangaplate', drpver=self._drpver,
plate=self.plateid, **kwargs)
self.platedir = self.filename
self._checkFilename()
return self.filename
def _getPlateFromFile(self):
''' Initialize a Plate from a Cube/RSS File'''
# Load file
try:
self._hdr = fits.getheader(self.filename, 1)
self.plateid = int(self._hdr['PLATEID'])
except Exception as e:
raise MarvinError('Could not initialize via filename: {0}'
.format(e))
else:
self.data_origin = 'file'
self._makePdict()
def _getPlateFromDB(self):
''' Initialize a Plate from the DB '''
import sqlalchemy
from marvin import marvindb as mdb
if not mdb.isdbconnected:
raise MarvinError('No db connected')
# Grab any cube for this plate
cube = None
try:
cube = mdb.session.query(mdb.datadb.Cube).join(
mdb.datadb.PipelineInfo, mdb.datadb.PipelineVersion).\
filter(mdb.datadb.Cube.plate == self.plateid,
mdb.datadb.PipelineVersion.version == self._drpver).first()
except sqlalchemy.orm.exc.NoResultFound as ee:
raise MarvinError('Could not retrieve Cube for plate {0}: '
'No Results Found: {1}'
.format(self.plateid, ee))
except Exception as ee:
raise MarvinError('Could not retrieve Cube for plate {0}: '
'Unknown exception: {1}'
.format(self.plateid, ee))
else:
# no cube
if not cube:
raise MarvinError('No cube found in db for plate {0}, drpver {1}'
.format(self.plateid, self._drpver))
# cube but no plateclass
try:
self._plate = cube.plateclass
except AttributeError as ee:
raise MarvinError('AttributeError: cube has no plateclass for plate {0}: {1}'
.format(self.plateid, ee))
else:
self._hdr = self._plate._hdr
self._pdict = self._plate.__dict__
self.data_origin = 'db'
if not self._plate:
raise MarvinError('Could not retrieve Plate for id {0}'.format(self.plateid))
def _getPlateFromAPI(self):
''' Initialize a Plate using the API '''
# Checks that the Plate exists.
routeparams = {'plateid': self.plateid}
url = config.urlmap['api']['getPlate']['url'].format(**routeparams)
# Make the API call
response = self._toolInteraction(url)
data = response.getData()
self._hdr = data['header']
self.data_origin = 'api'
self._makePdict()
def _initCubes(self):
''' Initialize a list of Marvin Cube objects '''
_cubes = [None]
if self.data_origin == 'file':
sdss_path = Path(release=self.release)
if self.dir3d == 'stack':
cubes = sdss_path.expand('mangacube', drpver=self._drpver,
plate=self.plateid, ifu='*', wave='LOG')
else:
cubes = sdss_path.expand('mangamastar', drpver=self._drpver,
plate=self.plateid, ifu='*', wave='LOG')
_cubes = [Cube(filename=cube, mode=self.mode, release=self.release) for cube in cubes]
elif self.data_origin == 'db':
_cubes = [Cube(plateifu=cube.plateifu, mode=self.mode, release=self.release)
for cube in self._plate.cubes]
elif self.data_origin == 'api':
routeparams = {'plateid': self.plateid}
url = config.urlmap['api']['getPlateCubes']['url'].format(**routeparams)
# Make the API call
response = self._toolInteraction(url)
data = response.getData()
plateifus = data['plateifus']
_cubes = [Cube(plateifu=pifu, mode=self.mode, release=self.release) for pifu in plateifus]
FuzzyList.__init__(self, _cubes)
self.mapper = (lambda e: e.plateifu)
def _setParams(self):
''' Set the plate parameters '''
self.ra = self._pdict.get('ra', None)
self.dec = self._pdict.get('dec', None)
self.designid = self._pdict.get('designid', None)
self.cartid = self._pdict.get('cartid', None)
self.dateobs = self._pdict.get('dateobs', None)
self.platetype = self._pdict.get('platetype', None)
self.surveymode = self._pdict.get('surveymode', None)
self.isbright = self._pdict.get('isbright', None)
self.dir3d = self._pdict.get('dir3d', None)
self.plateid = int(self.plateid)
def _makePdict(self):
''' Make the necessary plate dictionary '''
self._pdict = {}
self._pdict['ra'] = self._hdr.get('CENRA', None)
self._pdict['dec'] = self._hdr.get('CENDEC', None)
self._pdict['designid'] = self._hdr.get('DESIGNID', None)
self._pdict['cartid'] = self._hdr.get('CARTID', None)
self._pdict['dateobs'] = self._hdr.get('DATE-OBS', None)
self._pdict['platetype'] = self._hdr.get('PLATETYP', None)
self._pdict['surveymode'] = self._hdr.get('SRVYMODE', None)
self._pdict['isbright'] = 'APOGEE' in self._pdict['surveymode']
self._pdict['dir3d'] = 'mastar' if self._pdict['isbright'] else 'stack'
self._pdict['ra'] = float(self._pdict['ra'])
self._pdict['dec'] = float(self._pdict['dec'])
self._pdict['designid'] = float(self._pdict['designid'])
def _sortOutNames(self):
''' Sort out any name issues with plateid, plateifu, mangaid inputs '''
if self.plateifu and 'XXX' not in self.plateifu:
plate, ifu = self.plateifu.split('-')
self.plateid = int(plate)
def _checkFilename(self):
''' Checks the filename for a proper FITS file '''
# if filename is not FITS, then try to load one
if 'fits' not in self.filename.lower():
if not Path:
raise MarvinError('sdss_access is not installed')
else:
# is_public = 'DR' in self._release
# path_release = self._release.lower() if is_public else None
sdss_path = Path(release=self._release)
# try a cube
full = sdss_path.full('mangacube', drpver=self._drpver, plate=self.plateid, ifu='*', wave='LOG')
cubeexists = sdss_path.any('', full=full)
if cubeexists:
file = sdss_path.one('', full=full)
else:
# try an rss
full = sdss_path.full('mangarss', drpver=self._drpver, plate=self.plateid, ifu='*', wave='LOG')
rssexists = sdss_path.any('', full=full)
if rssexists:
file = sdss_path.one('', full=full)
else:
file = None
# load the file
if file:
self.filename = file
else:
self.filename = None