def test_misc(): cadc = Cadc() coords = '08h45m07.5s +54d18m00s' coords_ra = parse_coordinates(coords).fk5.ra.degree coords_dec = parse_coordinates(coords).fk5.dec.degree assert "SELECT * from caom2.Observation o join caom2.Plane p ON " \ "o.obsID=p.obsID WHERE INTERSECTS( CIRCLE('ICRS', " \ "{}, {}, 0.3), position_bounds) = 1 " \ "AND (quality_flag IS NULL OR quality_flag != 'junk') " \ "AND collection='CFHT' AND dataProductType='image'".\ format(coords_ra, coords_dec) == \ cadc._args_to_payload(**{'coordinates': coords, 'radius': 0.3 * u.deg, 'collection': 'CFHT', 'data_product_type': 'image'})['query'] # no collection or data_product_type assert "SELECT * from caom2.Observation o join caom2.Plane p ON " \ "o.obsID=p.obsID WHERE INTERSECTS( CIRCLE('ICRS', " \ "{}, {}, 0.3), position_bounds) = 1 AND (quality_flag IS NULL OR " \ "quality_flag != 'junk')".format(coords_ra, coords_dec) == \ cadc._args_to_payload(**{'coordinates': coords, 'radius': '0.3 deg'})['query']
def test_misc(monkeypatch): dummyTapHandler = DummyTapHandler() monkeypatch.setattr(cadc_core, 'get_access_url', get_access_url_mock) cadc = CadcClass(tap_plus_handler=dummyTapHandler) class Result(object): pass result = Result() result._phase = 'RUN' with pytest.raises(RuntimeError): cadc._parse_result(result) result._phase = 'COMPLETED' result.results = 'WELL DONE' assert result.results == cadc._parse_result(result) coords = '08h45m07.5s +54d18m00s' coords_ra = parse_coordinates(coords).fk5.ra.degree coords_dec = parse_coordinates(coords).fk5.dec.degree assert "SELECT * from caom2.Observation o join caom2.Plane p ON " \ "o.obsID=p.obsID WHERE INTERSECTS( CIRCLE('ICRS', " \ "{}, {}, 0.3), position_bounds) = 1 " \ "AND (quality_flag IS NULL OR quality_flag != 'junk') " \ "AND collection='CFHT'".format(coords_ra, coords_dec) == \ cadc._args_to_payload(**{'coordinates': coords, 'radius': 0.3, 'collection': 'CFHT'})['query'] # no collection assert "SELECT * from caom2.Observation o join caom2.Plane p ON " \ "o.obsID=p.obsID WHERE INTERSECTS( CIRCLE('ICRS', " \ "{}, {}, 0.3), position_bounds) = 1 AND (quality_flag IS NULL OR " \ "quality_flag != 'junk')".format(coords_ra, coords_dec) == \ cadc._args_to_payload(**{'coordinates': coords, 'radius': 0.3})['query']
def test_misc(monkeypatch): dummyTapHandler = DummyTapHandler() monkeypatch.setattr(cadc_core, 'get_access_url', get_access_url_mock) cadc = Cadc(tap_plus_handler=dummyTapHandler) class Result(object): pass result = Result() result._phase = 'RUN' with pytest.raises(RuntimeError): cadc._parse_result(result) result._phase = 'COMPLETED' result.results = 'WELL DONE' assert result.results == cadc._parse_result(result) coords = '08h45m07.5s +54d18m00s' coords_ra = parse_coordinates(coords).ra.degree coords_dec = parse_coordinates(coords).dec.degree assert "SELECT * from caom2.Observation o join caom2.Plane p ON " \ "o.obsID=p.obsID WHERE INTERSECTS( CIRCLE('ICRS', " \ "{}, {}, 0.3), position_bounds) = 1 " \ "AND (quality_flag IS NULL OR quality_flag != 'junk') " \ "AND collection='CFHT'".format(coords_ra, coords_dec) == \ cadc._args_to_payload(**{'coordinates': coords, 'radius': 0.3, 'collection': 'CFHT'})['query'] # no collection assert "SELECT * from caom2.Observation o join caom2.Plane p ON " \ "o.obsID=p.obsID WHERE INTERSECTS( CIRCLE('ICRS', " \ "{}, {}, 0.3), position_bounds) = 1 AND (quality_flag IS NULL OR " \ "quality_flag != 'junk')".format(coords_ra, coords_dec) == \ cadc._args_to_payload(**{'coordinates': coords, 'radius': 0.3})['query']
def handle_coordinates(url, key, coordinates): """ Handler function for coordinates """ assert(key == "coordinates") coordinates = commons.parse_coordinates(coordinates) if coordinates is not None: return "%s/ra=%f/dec=%f" % (url, coordinates.ra.deg, coordinates.dec.deg) return url
def addTarget(self, target): """Add target to archiveSearch object. Parameters ---------- target : str or list Target to query the ALMA archive for. Can be either a string indicating a source name (e.g. 'M87') or a list indicating a region to search consisting of (coordinates, radius). The coordinates element can be either a string or an astropy.coordinates object and the radius element can be either a string or an astropy.units.Quantity object. """ targetType = type(target) if targetType == str: # source name self.targets[target] = target self.isObjectQuery[target] = True elif targetType == list: # region if type(target[0]) != SkyCoord: target[0] = commons.parse_coordinates(target[0]) if type(target[1]) != Angle: target[1] = Angle(target[1]) targetStr = 'coord=({:} {:}) radius={:}deg' targetStr = targetStr.format(target[0].ra, target[0].dec, target[1].deg) self.targets[targetStr] = target self.isObjectQuery[targetStr] = False else: msg = 'Cannot work with targets of type {:}'.format(targetType) raise TypeError(msg)
def _parse_coordinates(coordinates): try: coordinates = commons.parse_coordinates(coordinates) # now c has some subclass of astropy.coordinate # get ra, dec and frame return _get_frame_coords(coordinates) except (u.UnitsError, TypeError): raise ValueError("Coordinates not specified correctly")
def __getCoordInput(self, value, msg): if not (isinstance(value, str) or isinstance(value, commons.CoordClasses)): raise ValueError(f"{msg} must be either a string or astropy.coordinates") if isinstance(value, str): c = commons.parse_coordinates(value) return c else: return value
def __getCoordInput(self, value, msg): if not (isinstance(value, str) or isinstance(value, commons.CoordClasses)): raise ValueError( str(msg) + " must be either a string or astropy.coordinates") if isinstance(value, str): c = commons.parse_coordinates(value) return c else: return value
def query_region_async(self, table, coordinates, radius, *, get_query_payload=False, cache=None, **criteria): """ Filter a table using a cone search around specified coordinates Parameters ---------- table : str The name of the table to query. A list of the tables on the Exoplanet Archive can be found on the documentation pages [1]_, [2]_. coordinates : str or `~astropy.coordinates` The coordinates around which to query. radius : str or `~astropy.units.Quantity` The radius of the cone search. Assumed to be have units of degrees if not provided as a ``Quantity``. get_query_payload : bool, optional Just return the dict of HTTP request parameters. Defaults to ``False``. cache : bool, optional Should the request result be cached? This can be useful for large repeated queries, but since the data in the archive is updated regularly, this defaults to ``False``. **criteria Any other filtering criteria to apply. These are described in detail in the archive documentation [1]_,[2]_ but some examples include ``select="*"`` to return all columns of the queried table or ``where=pl_name='K2-18 b'`` to filter a specific column. Returns ------- response : `requests.Response` The HTTP response returned from the service. References ---------- .. [1] `NASA Exoplanet Archive TAP Documentation <https://exoplanetarchive.ipac.caltech.edu/docs/TAP/usingTAP.html>`_ .. [2] `NASA Exoplanet Archive API Documentation <https://exoplanetarchive.ipac.caltech.edu/docs/program_interfaces.html>`_ """ # Checks if coordinate strings is parsable as an astropy.coordinates object coordinates = commons.parse_coordinates(coordinates) # if radius is just a number we assume degrees if isinstance(radius, (int, float)): radius = radius * u.deg radius = coord.Angle(radius) criteria["ra"] = coordinates.ra.deg criteria["dec"] = coordinates.dec.deg criteria["radius"] = "{0} degree".format(radius.deg) # Runs the query method defined above, but with added region filter return self.query_criteria_async( table, get_query_payload=get_query_payload, cache=cache, **criteria, )
def _args_to_payload(self, coordinate, radius=None): """ Accepts the query parameters and returns a dictionary suitable to be sent as data via a HTTP POST request. Parameters ---------- coordinate : str Can be either the name of an object or a coordinate string If a name, must be resolvable by NED, SIMBAD, 2MASS, or SWAS. Examples of acceptable coordinate strings, can be found `here <https://irsa.ipac.caltech.edu/applications/DUST/docs/coordinate.html>`_ radius : str / `~astropy.units.Quantity`, optional The size of the region to include in the dust query, in radian, degree or hour as per format specified by `~astropy.coordinates.Angle` or `~astropy.units.Quantity`. Defaults to 5 degrees. Returns ------- payload : dict A dictionary that specifies the data for an HTTP POST request """ if isinstance(coordinate, str): try: # If the coordinate is a resolvable name, pass that name # directly to irsa_dust because it can handle it (and that # changes the return value associated metadata) C = commons.ICRSCoord.from_name(coordinate) payload = {"locstr": coordinate} except coordinates.name_resolve.NameResolveError: C = commons.parse_coordinates(coordinate).transform_to('fk5') # check if this is resolvable? payload = {"locstr": "{0} {1}".format(C.ra.deg, C.dec.deg)} elif isinstance(coordinate, coordinates.SkyCoord): C = coordinate.transform_to('fk5') payload = {"locstr": "{0} {1}".format(C.ra.deg, C.dec.deg)} # check if radius is given with proper units if radius is not None: reg_size = coordinates.Angle(radius).deg # check if radius falls in the acceptable range if reg_size < 2 or reg_size > 37.5: raise ValueError("Radius (in any unit) must be in the" " range of 2.0 to 37.5 degrees") payload["regSize"] = reg_size return payload
def query_region(self, coordinate, radius, *, n_obs=10, columns='*', **kwargs): """ Get the observation metadata from a given region Parameters ---------- coordinate : string / `astropy.coordinates` the identifier or coordinates around which to query radius : int / `~astropy.units.Quantity` the radius of the region n_obs : int, optional the number of observations columns : str, optional the columns to retrieve from the data table kwargs : dict passed to `query_hsa_tap` Returns ------- A table object with the list of observations in the region """ r = radius if not isinstance(radius, u.Quantity): r = radius * u.deg coord = commons.parse_coordinates(coordinate).icrs query = ( f"select top {n_obs} {columns} from hsa.v_active_observation " f"where contains(" f"point('ICRS', hsa.v_active_observation.ra, hsa.v_active_observation.dec), " f"circle('ICRS', {coord.ra.degree},{coord.dec.degree},{r.to(u.deg).value}))=1" ) return self.query_hsa_tap(query, **kwargs)
def test_get_images_against_AS(self): cadc = Cadc() coords = '08h45m07.5s +54d18m00s' radius = 0.05 * u.deg # Compare results from cadc advanced search to get_images query = cadc._args_to_payload(**{ 'coordinates': coords, 'radius': radius, 'data_product_type': 'image' })['query'] result = cadc.exec_sync(query) uri_list = [uri.decode('ascii') for uri in result['publisherID']] access_url = 'https://www.cadc-ccda.hia-iha.nrc-cnrc.gc.ca/en/download' icrs_coords = parse_coordinates(coords).icrs data = { 'uris': ' '.join(uri_list), 'params': 'cutout=Circle ICRS {} {} {}'.format(icrs_coords.ra.degree, icrs_coords.dec.degree, radius.value), 'method': 'URL List' } resp_urls = requests.post(access_url, data).text.split('\r\n') # Filter out the errors and empty strings filtered_resp_urls = list( filter(lambda url: not url.startswith('ERROR') and url != '', resp_urls)) # This function should return nearly the same urls image_urls = cadc.get_images(coords, radius, get_url_list=True) assert len(filtered_resp_urls) == len(image_urls)
def test_get_image_list(): def get(*args, **kwargs): class CapsResponse(object): def __init__(self): self.status_code = 200 self.content = b'' def raise_for_status(self): pass return CapsResponse() class Params(object): def __init__(self, **param_dict): self.__dict__.update(param_dict) coords = '08h45m07.5s +54d18m00s' coords_ra = parse_coordinates(coords).fk5.ra.degree coords_dec = parse_coordinates(coords).fk5.dec.degree radius = 0.1*u.deg uri = 'im_an_ID' run_id = 'im_a_RUNID' pos = 'CIRCLE {} {} {}'.format(coords_ra, coords_dec, radius.value) service_def1 = Mock() service_def1.access_url = \ b'https://www.cadc-ccda.hia-iha.nrc-cnrc.gc.ca/caom2ops/sync' service_def1.input_params = [Params(name='ID', value=uri), Params(name='RUNID', value=run_id)] service_def2 = Mock() service_def2.access_url = \ b'https://www.cadc-ccda.hia-iha.nrc-cnrc.gc.ca/caom2ops/async' service_def2.input_params = [Params(name='ID', value=uri), Params(name='RUNID', value=run_id)] result = Mock() service_def_list = [service_def1, service_def2] result.bysemantics.return_value = service_def_list with patch('pyvo.dal.adhoc.DatalinkResults.from_result_url') as dl_results_mock: dl_results_mock.return_value = result cadc = Cadc() cadc._request = get # Mock the request url_list = cadc.get_image_list( {'publisherID': ['ivo://cadc.nrc.ca/foo']}, coords, radius) assert len(url_list) == 1 params = parse_qs(urlsplit(url_list[0]).query) assert params['ID'][0] == uri assert params['RUNID'][0] == run_id assert params['POS'][0] == pos with pytest.raises(TypeError): cadc.get_image_list(None) with pytest.raises(AttributeError): cadc.get_image_list(None, coords, radius) with pytest.raises(TypeError): cadc.get_image_list({'publisherID': [ 'ivo://cadc.nrc.ca/foo']}, coords, 0.1)
def query_region_async(self, coordinates, radius=1 * u.arcmin, equinox='J2000.0', get_query_payload=False): """ Serves the same purpose as `~NedClass.query_region` but returns the raw HTTP response rather than the `astropy.table.Table` object. Parameters ---------- coordinates : str or `astropy.coordinates` object The target around which to search. It may be specified as a string in which case it is resolved using online services or as the appropriate `astropy.coordinates` object. ICRS coordinates may also be entered as strings as specified in the `astropy.coordinates` module. radius : str or `~astropy.units.Quantity` object, optional The string must be parsable by `astropy.coordinates.Angle`. The appropriate `~astropy.units.Quantity` object from `astropy.units` may also be used. Defaults to 1 arcmin. equinox : str, optional The equinox may be either J2000.0 or B1950.0. Defaults to J2000.0 get_query_payload : bool, optional if set to `True` then returns the dictionary sent as the HTTP request. Defaults to `False`. Returns ------- response : `requests.Response` The HTTP response returned from the service """ request_payload = self._request_payload_init() self._set_input_options(request_payload) self._set_output_options(request_payload) # if its a name then query near name if not commons._is_coordinate(coordinates): request_payload['objname'] = coordinates request_payload['search_type'] = 'Near Name Search' request_payload['radius'] = coord.Angle(radius).arcmin else: try: c = commons.parse_coordinates(coordinates) if c.frame.name == 'galactic': request_payload['in_csys'] = 'Galactic' request_payload['lon'] = c.l.degree request_payload['lat'] = c.b.degree # for any other, convert to ICRS and send else: request_payload['in_csys'] = 'Equatorial' ra, dec = commons.coord_to_radec(c) request_payload['lon'] = ra request_payload['lat'] = dec request_payload['search_type'] = 'Near Position Search' request_payload['in_equinox'] = equinox request_payload['radius'] = coord.Angle(radius).arcmin except (u.UnitsError, TypeError): raise TypeError("Coordinates not specified correctly") if get_query_payload: return request_payload response = self._request("GET", url=Ned.OBJ_SEARCH_URL, params=request_payload, timeout=Ned.TIMEOUT) return response
def query_region_async(self, coordinate=None, where=None, mission=None, dataset=None, table=None, columns=None, width=None, height=None, action='search', intersect='OVERLAPS', most_centered=False): """ For certain missions, this function can be used to search for image and catalog files based on a point, a box (bounded by great circles) and/or an SQL-like ``where`` clause. If ``coordinates`` is specified, then the optional ``width`` and ``height`` arguments control the width and height of the search box. If neither ``width`` nor ``height`` are provided, then the search area is a point. If only one of ``width`` or ``height`` are specified, then the search area is a square with that side length centered at the coordinate. Parameters ---------- coordinate : str, `astropy.coordinates` object Gives the position of the center of the box if performing a box search. If it is a string, then it must be a valid argument to `~astropy.coordinates.SkyCoord`. Required if ``where`` is absent. where : str SQL-like query string. Required if ``coordinates`` is absent. mission : str The mission to be used (if not the default mission). dataset : str The dataset to be used (if not the default dataset). table : str The table to be queried (if not the default table). columns : str, list A space-separated string or a list of strings of the names of the columns to return. width : str or `~astropy.units.Quantity` object Width of the search box if ``coordinates`` is present. The string must be parsable by `~astropy.coordinates.Angle`. The appropriate `~astropy.units.Quantity` object from `astropy.units` may also be used. height : str, `~astropy.units.Quantity` object Height of the search box if ``coordinates`` is present. The string must be parsable by `~astropy.coordinates.Angle`. The appropriate `~astropy.units.Quantity` object from `astropy.units` may also be used. intersect : ``'COVERS'``, ``'ENCLOSED'``, ``'CENTER'``, ``'OVERLAPS'`` Spatial relationship between search box and image footprint. ``'COVERS'``: X must completely contain S. Equivalent to ``'CENTER'`` and ``'OVERLAPS'`` if S is a point. ``'ENCLOSED'``: S must completely contain X. If S is a point, the query will always return an empty image table. ``'CENTER'``: X must contain the center of S. If S is a point, this is equivalent to ``'COVERS'`` and ``'OVERLAPS'``. ``'OVERLAPS'``: The intersection of S and X is non-empty. If S is a point, this is equivalent to ``'CENTER'`` and ``'COVERS'``. most_centered : bool If True, then only the most centered image is returned. action : ``'search'``, ``'data'``, or ``'sia'`` The action to perform at the server. The default is ``'search'``, which returns a table of the available data. ``'data'`` requires advanced path construction that is not yet supported. ``'sia'`` provides access to the 'simple image access' IVOA protocol Returns ------- response : `~requests.Response` The HTTP response returned from the service """ if coordinate is None and where is None: raise InvalidQueryError( 'At least one of `coordinate` or `where` is required') intersect = intersect.upper() if intersect not in ('COVERS', 'ENCLOSED', 'CENTER', 'OVERLAPS'): raise InvalidQueryError( "Invalid value for `intersects` " + "(must be 'COVERS', 'ENCLOSED', 'CENTER', or 'OVERLAPS')") if action not in ('sia', 'data', 'search'): raise InvalidQueryError("Valid actions are: sia, data, search.") if action == 'data': raise NotImplementedError( "The action='data' option is a placeholder for future " + "functionality.") args = {'INTERSECT': intersect} # Note: in IBE, if 'mcen' argument is present, it is true. # If absent, it is false. if most_centered: args['mcen'] = '1' if coordinate is not None: c = commons.parse_coordinates(coordinate).transform_to(coord.ICRS) args['POS'] = '{0},{1}'.format(c.ra.deg, c.dec.deg) if width and height: args['SIZE'] = '{0},{1}'.format( coord.Angle(width).value, coord.Angle(height).value) elif width or height: args['SIZE'] = str(coord.Angle(width or height).value) if where: args['where'] = where if columns: if isinstance(columns, str): columns = columns.split() args['columns'] = ','.join(columns) url = "{URL}{action}/{mission}/{dataset}/{table}".format( URL=self.URL, action=action, mission=mission or self.MISSION, dataset=dataset or self.DATASET, table=table or self.TABLE) return self._request('GET', url, args, timeout=self.TIMEOUT)