def conesearch(self, ra, dec, radius, **kwargs): ## ToDo: rewrite to use Gaia Archive, not tap service. Need to ditch distance param and just use parallax # input parameters in degrees: ra_ = ra.to(u.degree); dec_= dec.to(u.degree); rad_=radius.to(u.degree) maxrec = kwargs.get('maxrec', 20000) columnlist = self._get_col_list() + ', gd.r_est' dbsource = '\n\t'.join(['\nFROM gaiadr2_complements.geometric_distance gd', 'INNER JOIN gaiadr2.gaia_source gs using (source_id) ']) constraints = '\n\t'.join(['\nWHERE ', 'CONTAINS(POINT(\'\', gs.ra, gs.dec), ', '\tCIRCLE(\'\', {ra}, {dec}, {rad})) = 1 '.format(ra=ra_.value, dec=dec_.value, rad=rad_.value)]) if self.source_constraints is not None: constraints = constraints + ' AND '+ self.source_constraints self.tap_query_string = 'SELECT \n\t\t'+ columnlist + dbsource + constraints tap_service = TAPService(self.tap_service_url) tap_results = tap_service.search(self.tap_query_string, maxrec=maxrec) self.objs = tap_results.to_table().to_pandas() self.objs.set_index('source_id', inplace=True)
def from_source_idlist(self, source_idlist, source_idcol=None, filters=False): #xml-ify the source_idlist to a file if isinstance(source_idlist, Table): #guess which column contains the source ids if source_idcol is None: if 'source_id' in source_idlist.colnames: sidcol = 'source_id' elif 'source' in source_idlist.colnames: sidcol = 'source' else: raise ValueError('no column to use as source_id') else: if source_idcol not in source_idlist.colnames: raise ValueError(f'invalid column specified as source id column: {source_idcol}') sidcol = source_idcol tbl = source_idlist elif isinstance(source_idlist, np.ndarray) or isinstance(source_idlist, list): sidcol = 'source_id' tbl = Table({sidcol:source_idlist}) else: raise ValueError(f'invalid source_idlist type: {type(source_idlist)}') xml_path = 'source_idlist.xml' tbl.write(xml_path, table_id='source_idlist', format='votable', overwrite=True) #build the query: col_list = self._get_col_list() + ', gd.r_est' dbsource = ''.join([' FROM tap_upload.source_idlist sidl', f' LEFT JOIN gaiadr2.gaia_source gs ON gs.source_id = sidl.{sidcol}', f' LEFT JOIN gaiadr2_complements.geometric_distance gd on gs.source_id = gd.source_id' ]) query_str = f'SELECT sidl.{sidcol} as "source", '+col_list+dbsource if filters: query_str = query_str + ' WHERE '+ self.source_constraints self.tap_query_string = query_str #fetch the data #job = Gaia.launch_job_async(query=query_str, upload_resource=xml_path,upload_table_name='source_idlist') #self.objs = job.get_results().to_pandas() #fetch data via tap query tap_service = TAPService(self.tap_service_url) tap_results = tap_service.search(self.tap_query_string, maxrec=len(tbl),uploads={'source_idlist':tbl}) self.objs = tap_results.to_table().to_pandas() self.objs.set_index('source', inplace=True)
from pyvo.dal import TAPService import astropy from astropy.table import Table, vstack import sys tap_service = TAPService("http://dc.g-vo.org/tap") tap_results = tap_service.search("SELECT TOP 10 * FROM ivoa.obscore") print(tap_results)
class ObsDatabase(object): """ Class to access NenuFAR BST TAP service. """ def __init__(self): self.service = TAPService(nancay_tap) log.info(f'TAP service {nancay_tap} accessed.') self.meta_names = [ 'target_name', 'obs_creator_did', 's_ra', 's_dec', 't_min', 't_max', 'em_min', 'em_max' ] self._conditions = { 'time': '', 'freq': '', 'pos': '', } self.time_range = [None, None] self.freq_range = [None, None] self.fov_radius = 180 * u.deg self.fov_center = None # --------------------------------------------------------- # # --------------------- Getter/Setter --------------------- # @property def meta_names(self): """ Column names (observation properties) to return using :meth:`~nenupy.observation.tapdatabase.ObsDatabase.search`. :setter: `list` of column names. :getter: Properties to query. :type: `str` :seealso: `Database description <http://vogate.obs-nancay.fr/__system__/dc_tables/show/tableinfo/nenufar.bst>`_ """ return ', '.join(self._meta_names) @meta_names.setter def meta_names(self, m): if not isinstance(m, list): raise TypeError('meta_names must be a list') unknown_mask = ~np.isin(m, colnames) if unknown_mask.any(): unknown = np.array(m)[unknown_mask] raise ValueError('Unknown meta_names: {}, available: {}'.format( unknown, colnames)) self._meta_names = m return @property def time_range(self): """ Time range selection for the ADQL query using :meth:`~nenupy.observation.tapdatabase.ObsDatabase.search`. Default is ``[None, None]`` which means that no condition based on observation time will be applied. :setter: Length-2 list of ``[start, stop]``. ``start`` and ``stop`` may be passed as :class:`~astropy.time.Time` instances or as ISO/ISOT `str`. :getter: ``[start, stop]`` list. :type: `list` """ return self._time_range @time_range.setter def time_range(self, t): if t == [None, None]: self._conditions['time'] = '' self._time_range = t return if not isinstance(t, list): raise TypeError('time_range must be a list') if not len(t) == 2: raise ValueError('time_range must be of length 2') if not all([isinstance(ti, Time) for ti in t]): t = [Time(ti) for ti in t] self._conditions['time'] = f'(t_min >= {t[0].mjd} '\ f'AND t_max <= {t[1].mjd})' log.info(f'time_range set to {t}.') self._time_range = t return @property def freq_range(self): """ Frequency range selection for the ADQL query using :meth:`~nenupy.observation.tapdatabase.ObsDatabase.search`. Default is ``[None, None]`` which means that no condition based on observation frequencies will be applied. :setter: Length-2 list of ``[fmin, fmax]``. ``fmin`` and ``fmax`` may be passed as :class:`~astropy.units.Quantity` instances or as `float` (assumed to be expressed in MHz). :getter: ``[fmin, fmax]`` list. :type: `list` """ return self._freq_range @freq_range.setter def freq_range(self, f): if f == [None, None]: self._conditions['freq'] = '' self._freq_range = f return if not isinstance(f, list): raise TypeError('freq_range must be a list') if not len(f) == 2: raise ValueError('freq_range must be of length 2') if not all([isinstance(fi, u.Quantity) for fi in f]): f = [fi * u.MHz for fi in f] lmax = wavelength(f[0]).to(u.m).value lmin = wavelength(f[1]).to(u.m).value self._conditions['freq'] = f'(em_min >= {lmin} AND '\ f'em_max <= {lmax})' log.info(f'freq_range set to {f}.') self._freq_range = f return @property def fov_radius(self): """ Radius of the query, in combination with the query center :attr:`~nenupy.observation.tapdatabase.ObsDatabase.fov_center`. :setter: Radius (in degrees if no unit is provided). Default is ``180 deg``. :getter: Radius in degrees. :type: `float` or :class:`~astropy.units.Quantity` .. warning:: Must be set **before** :attr:`~nenupy.observation.tapdatabase.ObsDatabase.fov_center`. """ return self._fov_radius @fov_radius.setter def fov_radius(self, r): if r is None: r = 180 * u.deg if not isinstance(r, u.Quantity): r *= u.deg if not r.isscalar: raise ValueError('FOV radius must be a scalar.') self._fov_radius = r return @property def fov_center(self): """ Center of the field of view queried, in comination with the radius :attr:`~nenupy.observation.tapdatabase.ObsDatabase.fov_radius`. :setter: Center of the field of view. :getter: Center of the field of view. :type: :class:`~astropy.coordinates.SkyCoord` .. warning:: Must be set **after** :attr:`~nenupy.observation.tapdatabase.ObsDatabase.fov_radius`. """ return self._fov_center @fov_center.setter def fov_center(self, f): if f is None: self._conditions['pos'] = '' self._fov_center = f return if not isinstance(f, SkyCoord): raise TypeError('fov_center must be a SkyCoord instance.') if not f.isscalar: raise ValueError('fov_center must be scalar.') radius = self.fov_radius.to(u.deg).value self._conditions['pos'] = f'1 = CONTAINS'\ f"(POINT('ICRS', s_ra, s_dec), CIRCLE('ICRS', "\ f'{f.ra.deg}, {f.dec.deg}, {radius}))' log.info(f'fov_center set to {f}, radius={radius} deg.') self._fov_center = f return @property def conditions(self): """ Conditions summary of the query. :getter: Query conditions. :type: `str` """ conds = [] for key in self._conditions.keys(): if self._conditions[key] != '': conds.append(self._conditions[key]) conditions = ' AND '.join(conds) return conditions @property def query(self): """ Full query, combining returned parameteres :attr:`~nenupy.observation.tapdatabase.ObsDatabase.meta_names` and the conditions :attr:`~nenupy.observation.tapdatabase.ObsDatabase.conditions`. :getter: Query. :type: `str` """ q = f'SELECT {self.meta_names} from nenufar.bst '\ f'WHERE ({self.conditions})' return q # --------------------------------------------------------- # # ------------------------ Methods ------------------------ # def search(self): """ Run the TAP :attr:`~nenupy.observation.tapdatabase.ObsDatabase.query` on the `NenuFAR BST service <http://vogate.obs-nancay.fr/tap>`_. :returns: NenuFAR observation properties resulting from the ADQL query. :rtype: :class:`~astropy.table.Table` """ if self.conditions == '': raise ValueError('Empty query, fill out some attributes first.') log.info(f'TAP service columns to return: {self._meta_names}.') log.debug(f"Querying: '{self.query}'") result = self.service.search(self.query) return result.to_table() def reset(self): """ Reset query parameters to default values. """ self.meta_names = [ 'target_name', 'obs_creator_did', 's_ra', 's_dec', 't_min', 't_max', 'em_min', 'em_max' ] self.time_range = [None, None] self.freq_range = [None, None] self.fov_radius = 180 * u.deg self.fov_center = None log.info('Query parameters reset to default values.') return