From 7f622170854d1d522a07314acebc947ecaa764fb Mon Sep 17 00:00:00 2001 From: Volodymyr Savchenko Date: Mon, 31 May 2021 15:31:12 +0200 Subject: [PATCH 01/13] Adding DESILegacySurvey module --- astroquery/desi/__init__.py | 41 ++++++ astroquery/desi/core.py | 77 ++++++++++ astroquery/desi/tests/__init__.py | 0 astroquery/desi/tests/data/dummy_table.txt | 8 ++ astroquery/desi/tests/data/dummy_tractor.fits | Bin 0 -> 92160 bytes .../desi/tests/data/hdu_list_images.fits | Bin 0 -> 17280 bytes astroquery/desi/tests/setup_package.py | 10 ++ astroquery/desi/tests/test_module.py | 132 ++++++++++++++++++ astroquery/desi/tests/test_module_remote.py | 53 +++++++ astroquery/utils/commons.py | 2 - docs/desi/desi.rst | 111 +++++++++++++++ 11 files changed, 432 insertions(+), 2 deletions(-) create mode 100644 astroquery/desi/__init__.py create mode 100644 astroquery/desi/core.py create mode 100644 astroquery/desi/tests/__init__.py create mode 100644 astroquery/desi/tests/data/dummy_table.txt create mode 100644 astroquery/desi/tests/data/dummy_tractor.fits create mode 100644 astroquery/desi/tests/data/hdu_list_images.fits create mode 100644 astroquery/desi/tests/setup_package.py create mode 100644 astroquery/desi/tests/test_module.py create mode 100644 astroquery/desi/tests/test_module_remote.py create mode 100644 docs/desi/desi.rst diff --git a/astroquery/desi/__init__.py b/astroquery/desi/__init__.py new file mode 100644 index 0000000000..e8af61e0e7 --- /dev/null +++ b/astroquery/desi/__init__.py @@ -0,0 +1,41 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +""" +DESI LegacySurvery + +https://www.legacysurvey.org/ +------------------------- + +:author: Gabriele Barni (Gabriele.Barni@unige.ch) +""" + +# Make the URL of the server, timeout and other items configurable +# See +# for docs and examples on how to do this +# Below is a common use case +from astropy import config as _config + + +class Conf(_config.ConfigNamespace): + """ + Configuration parameters for `astroquery.desi`. + """ + server = _config.ConfigItem( + ['https://portal.nersc.gov/cfs/cosmo/data/legacysurvey/', + ], + 'base url') + + timeout = _config.ConfigItem( + 30, + 'Time limit for connecting to template_module server.') + + +conf = Conf() + +# Now import your public class +# Should probably have the same name as your module +from .core import DESILegacySurvey, DESILegacySurveyClass + +__all__ = ['DESILegacySurvey', 'DESILegacySurveyClass', + 'Conf', 'conf', + ] diff --git a/astroquery/desi/core.py b/astroquery/desi/core.py new file mode 100644 index 0000000000..37e0593015 --- /dev/null +++ b/astroquery/desi/core.py @@ -0,0 +1,77 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +import urllib.error +import requests + +import pyvo as vo +import numpy as np + +from astroquery.exceptions import NoResultsWarning +from astroquery.query import BaseQuery +from astroquery.utils import commons, async_to_sync + +from . import conf + + +__all__ = ['DESILegacySurvey', 'DESILegacySurveyClass'] + + +@async_to_sync +class DESILegacySurveyClass(BaseQuery): + URL = conf.server + TIMEOUT = conf.timeout + + def query_region(self, coordinates, radius, data_release=9): + """ + Queries a region around the specified coordinates. + + Parameters + ---------- + coordinates : str or `astropy.coordinates`. + coordinates around which to query + radius : str or `astropy.units.Quantity`. + the radius of the cone search + + Returns + ------- + response : `astropy.table.Table` + """ + + url = 'https://datalab.noirlab.edu/tap' + tap_service = vo.dal.TAPService(url) + qstr = "SELECT all * FROM ls_dr" + str(data_release) + ".tractor WHERE dec>" + str(coordinates.dec.deg - radius.deg) + " and dec<" + str( + coordinates.dec.deg + radius.deg) + " and ra>" + str(coordinates.ra.deg - radius.deg / np.cos(coordinates.dec.deg * np.pi / 180.)) + " and ra<" + str( + coordinates.ra.deg + radius.deg / np.cos(coordinates.dec.deg * np.pi / 180)) + + tap_result = tap_service.run_sync(qstr) + tap_result = tap_result.to_table() + # filter out duplicated lines from the table + mask = tap_result['type'] != 'D' + filtered_table = tap_result[mask] + + return filtered_table + + def get_images(self, position, data_release=9, pixels=None, radius=None, show_progress=True, image_band='g'): + """ + Returns + ------- + A list of `astropy.io.fits.HDUList` objects. + """ + + image_size_arcsec = radius.arcsec + pixsize = 2 * image_size_arcsec / pixels + + image_url = 'https://www.legacysurvey.org/viewer/fits-cutout?ra=' + str(position.ra.deg) + '&dec=' + str(position.dec.deg) + '&size=' + str( + pixels) + '&layer=ls-dr' + str(data_release) + '&pixscale=' + str(pixsize) + '&bands=' + image_band + + file_container = commons.FileContainer(image_url, encoding='binary', show_progress=show_progress) + + try: + fits_file = file_container.get_fits() + except (requests.exceptions.HTTPError, urllib.error.HTTPError) as e: + # TODO not sure this is the most suitable exception + raise NoResultsWarning(f"{str(e)} - Problem retrieving the file at the url: {str(image_url)}") + + return [fits_file] + + +DESILegacySurvey = DESILegacySurveyClass() diff --git a/astroquery/desi/tests/__init__.py b/astroquery/desi/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/astroquery/desi/tests/data/dummy_table.txt b/astroquery/desi/tests/data/dummy_table.txt new file mode 100644 index 0000000000..da5be1746a --- /dev/null +++ b/astroquery/desi/tests/data/dummy_table.txt @@ -0,0 +1,8 @@ +ra,dec,objid,type +166.10552527002142,38.20797162140221,877,PSF +166.10328347620825,38.211862682863625,855,PSF +166.1146193911762,38.20826292586543,991,PSF +166.1138080401007,38.20883307659884,3705,DUP +166.11382707824612,38.20885008952696,982,SER +166.11779248975387,38.211159276708706,1030,PSF +166.11865123008005,38.2147201669633,1039,PSF diff --git a/astroquery/desi/tests/data/dummy_tractor.fits b/astroquery/desi/tests/data/dummy_tractor.fits new file mode 100644 index 0000000000000000000000000000000000000000..254b4cb3ba10a528788df3207c185d4db9c1a328 GIT binary patch literal 92160 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 D_p;~Z literal 0 HcmV?d00001 diff --git a/astroquery/desi/tests/data/hdu_list_images.fits b/astroquery/desi/tests/data/hdu_list_images.fits new file mode 100644 index 0000000000000000000000000000000000000000..8b77018ffb06a26073946e64d8b8237c59014122 GIT binary patch literal 17280 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 F{y$fhekK3_ literal 0 HcmV?d00001 diff --git a/astroquery/desi/tests/setup_package.py b/astroquery/desi/tests/setup_package.py new file mode 100644 index 0000000000..df5b7c82e5 --- /dev/null +++ b/astroquery/desi/tests/setup_package.py @@ -0,0 +1,10 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +import os + + +def get_package_data(): + paths = [os.path.join('data', '*.txt'), + os.path.join('data', '*.fits'), + os.path.join('data', '*.fits.gz'), + ] + return {'astroquery.desi.tests': paths} diff --git a/astroquery/desi/tests/test_module.py b/astroquery/desi/tests/test_module.py new file mode 100644 index 0000000000..6e047bea23 --- /dev/null +++ b/astroquery/desi/tests/test_module.py @@ -0,0 +1,132 @@ +import astropy.io.votable +import pytest +import os +import numpy as np +import pyvo as vo + +from astropy.table import Table +from astropy.io import fits +from pyvo.dal import TAPResults +from urllib import parse +from contextlib import contextmanager + +from ... import desi +from ...utils.mocks import MockResponse +from ...utils import commons + +DATA_FILES = { + 'dummy_tap_table': 'dummy_table.txt', + 'dummy_tractor_fits': 'dummy_tractor.fits', + 'dummy_hdu_list_fits': 'hdu_list_images.fits' +} + +coords = commons.ICRSCoord('11h04m27s +38d12m32s') +radius = commons.ArcminRadiusGenerator(0.5) +pixels = 60 +data_release = 9 +emispheres_list = ['north', 'south'] + + +@pytest.fixture +def patch_get(request): + try: + mp = request.getfixturevalue("monkeypatch") + except AttributeError: # pytest < 3 + mp = request.getfuncargvalue("monkeypatch") + + mp.setattr(desi.DESILegacySurvey, '_request', get_mockreturn) + return mp + + +@pytest.fixture +def patch_get_readable_fileobj(request): + @contextmanager + def get_readable_fileobj_mockreturn(filename, **kwargs): + file_obj = data_path(DATA_FILES['dummy_hdu_list_fits']) # TODO: add images option + encoding = kwargs.get('encoding', None) + f = None + try: + if encoding == 'binary': + f = open(file_obj, 'rb') + yield f + else: + f = open(file_obj, 'rb') + yield f + finally: + if f is not None: + f.close() + + try: + mp = request.getfixturevalue("monkeypatch") + except AttributeError: # pytest < 3 + mp = request.getfuncargvalue("monkeypatch") + + mp.setattr(commons, 'get_readable_fileobj', + get_readable_fileobj_mockreturn) + return mp + + +@pytest.fixture +def patch_tap(request): + try: + mp = request.getfixturevalue("monkeypatch") + except AttributeError: # pytest < 3 + mp = request.getfuncargvalue("monkeypatch") + + mp.setattr(vo.dal.TAPService, 'run_sync', tap_mockreturn) + return mp + + +def get_mockreturn(method, url, params=None, timeout=10, **kwargs): + parsed_url = parse.urlparse(url) + splitted_parsed_url = parsed_url.path.split('/') + url_filename = splitted_parsed_url[-1] + filename = None + content = None + if url_filename.startswith('tractor-'): + filename = data_path(DATA_FILES['dummy_tractor_fits']) + + if filename is not None: + content = open(filename, 'rb').read() + return MockResponse(content) + + +def tap_mockreturn(url, params=None, timeout=10, **kwargs): + content_table = Table.read(data_path(DATA_FILES['dummy_tap_table']), + format='ascii.csv', comment='#') + votable_table = astropy.io.votable.from_table(content_table) + return TAPResults(votable_table) + + +def data_path(filename): + data_dir = os.path.join(os.path.dirname(__file__), 'data') + return os.path.join(data_dir, filename) + + +def compare_result_data(result, data): + for col in result.colnames: + if result[col].dtype.type is np.string_ or result[col].dtype.type is np.str_: + assert np.array_equal(result[col], data[col]) + else: + np.testing.assert_allclose(result[col], data[col]) + + +def image_tester(images, filetype): + assert type(images) == list + data = fits.open(data_path(DATA_FILES[filetype])) + assert images[0][0].header == data[0].header + assert np.array_equal(images[0][0].data, data[0].data) + + +def test_coords_query_region(patch_tap, coords=coords, radius=radius): + result = desi.DESILegacySurvey.query_region(coords, radius) + data = Table.read(data_path(DATA_FILES['dummy_tap_table']), + format='ascii.csv', comment='#') + data['objid'] = data['objid'].astype(np.int64) + compare_result_data(result, data) + + +def test_coords_get_images(patch_get_readable_fileobj, dr=data_release): + images_list = desi.DESILegacySurvey.get_images(coords, data_release=dr, radius=radius, pixels=pixels) + + image_tester(images_list, 'dummy_hdu_list_fits') diff --git a/astroquery/desi/tests/test_module_remote.py b/astroquery/desi/tests/test_module_remote.py new file mode 100644 index 0000000000..30edcc4bd6 --- /dev/null +++ b/astroquery/desi/tests/test_module_remote.py @@ -0,0 +1,53 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import pytest +from astropy.io.fits import HDUList +from astroquery.exceptions import NoResultsWarning + + +@pytest.mark.remote_data +class TestLegacySurveyClass: + + def test_query_region(self): + import astroquery.desi + from astropy.coordinates import SkyCoord + from astropy.coordinates import Angle + from astropy.table import Table + + ra = Angle('11h04m27s', unit='hourangle').degree + dec = Angle('+38d12m32s', unit='hourangle').degree + coordinates = SkyCoord(ra, dec, unit='degree') + + radius = Angle(5, unit='arcmin') + + query1 = astroquery.desi.DESILegacySurvey.query_region(coordinates, radius=radius, data_release=9) + + assert isinstance(query1, Table) + + @pytest.mark.parametrize("valid_inputs", [True, False]) + def test_get_images(self, valid_inputs): + import astroquery.desi + from astropy.coordinates import SkyCoord + from astropy.coordinates import Angle + + if valid_inputs: + ra = Angle('11h04m27s', unit='hourangle').degree + dec = Angle('+38d12m32s', unit='hourangle').degree + radius_input = 0.5 # arcmin + pixels = 60 + else: + ra = Angle('86.633212', unit='degree').degree + dec = Angle('22.01446', unit='degree').degree + radius_input = 3 # arcmin + pixels = 1296000 + + pos = SkyCoord(ra, dec, unit='degree') + radius = Angle(radius_input, unit='arcmin') + + if valid_inputs: + query1 = astroquery.desi.DESILegacySurvey.get_images(pos, data_release=9, radius=radius, pixels=pixels) + assert isinstance(query1, list) + assert isinstance(query1[0], HDUList) + else: + with pytest.raises(NoResultsWarning): + astroquery.desi.DESILegacySurvey.get_images(pos, data_release=9, radius=radius, pixels=pixels) diff --git a/astroquery/utils/commons.py b/astroquery/utils/commons.py index 3d149fa43a..0f99b3cf87 100644 --- a/astroquery/utils/commons.py +++ b/astroquery/utils/commons.py @@ -24,10 +24,8 @@ from ..exceptions import TimeoutError, InputWarning from .. import version - CoordClasses = (SkyCoord, BaseCoordinateFrame) - __all__ = ['send_request', 'parse_coordinates', 'TableList', diff --git a/docs/desi/desi.rst b/docs/desi/desi.rst new file mode 100644 index 0000000000..b79269bda1 --- /dev/null +++ b/docs/desi/desi.rst @@ -0,0 +1,111 @@ +.. _astroquery.desi: + +************************************ +DESI LegacySurvey Queries (`astroquery.desi`) +************************************ + +Getting started +=============== + +This module provides a way to query the DesiLegacySurvey service. +Presented below are examples that illustrate the different types of queries +that can be formulated. + +Query a region +=============== + +This example shows how to query a certain region with DesiLegacySurvey. +We'll use a set of coordinates that define the region of interest, +and search within a 5 arcmin radius. + +.. code-block:: python + + >>> from astroquery.desi import DESILegacySurvey + >>> from astropy.coordinates import Angle, SkyCoord + >>> ra = Angle('11h04m27s', unit='hourangle').degree + >>> dec = Angle('+38d12m32s', unit='hourangle').degree + >>> coordinates = SkyCoord(ra, dec, unit='degree') + >>> radius = Angle(5, unit='arcmin') + >>> table_out = DESILegacySurvey.query_region(coordinates, radius, data_release=9) + >>> print(table_out[:5]) + + ls_id dec ra ... type wise_coadd_id + ---------------- ------------------ ------------------ ... ---- ------------- + 9907734382838387 38.12628495570797 166.0838654387131 ... PSF 1667p378 + 9907734382838488 38.12798336424771 166.0922968862182 ... PSF 1667p378 + 9907734382838554 38.12858283671958 166.0980673954384 ... REX 1667p378 + 9907734382838564 38.12840803351445 166.09921863682337 ... EXP 1667p378 + 9907734382838584 38.12836885301038 166.10070750146636 ... REX 1667p378 + +The result is an astropy.Table. + +Get images +=============== + +To download images for a certain region of interest, +we can define our region in the same way used in the example above. + +.. code-block:: python + + >>> from astroquery.desi import DESILegacySurvey + >>> from astropy.coordinates import Angle, SkyCoord + >>> ra = Angle('11h04m27s', unit='hourangle').degree + >>> dec = Angle('+38d12m32s', unit='hourangle').degree + >>> pos = SkyCoord(ra, dec, unit='degree') + >>> radius = Angle(0.5, unit='arcmin') + >>> pixels = 60 + >>> im = DESILegacySurvey.get_images(pos, data_release=9, radius=radius, pixels=pixels) + +All the information we need can be found within the object "im". + +.. code-block:: python + + >>> hdul = im[0] + >>> hdul[0].header + + SIMPLE = T / file does conform to FITS standard + BITPIX = -32 / number of bits per data pixel + NAXIS = 2 / number of data axes + NAXIS1 = 60 / length of data axis 1 + NAXIS2 = 60 / length of data axis 2 + EXTEND = T / FITS dataset may contain extensions + COMMENT FITS (Flexible Image Transport System) format is defined in 'Astronomy + COMMENT and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H + BANDS = 'g ' + BAND0 = 'g ' + CTYPE1 = 'RA---TAN' / TANgent plane + CTYPE2 = 'DEC--TAN' / TANgent plane + CRVAL1 = 166.1125 / Reference RA + CRVAL2 = 38.2088888888889 / Reference Dec + CRPIX1 = 30.5 / Reference x + CRPIX2 = 30.5 / Reference y + CD1_1 = -0.000277777777777778 / CD matrix + CD1_2 = 0. / CD matrix + CD2_1 = 0. / CD matrix + CD2_2 = 0.000277777777777778 / CD matrix + IMAGEW = 60. / Image width + IMAGEH = 60. / Image height + +The variable "im" is a list of `~astropy.io.fits.HDUList` objects, one entry for +each corresponding object. + +In case a set of not valid coordinates is provided, then a `astroquery.exceptions.NoResultsWarning` +exception is raised, as shown in the example below. + +.. code-block:: python + + >>> from astroquery.desi import DESILegacySurvey + >>> from astropy.coordinates import Angle, SkyCoord + >>> ra = Angle('86.633212', unit='degree').degree + >>> dec = Angle('22.01446', unit='degree').degree + >>> radius_input = 3 + >>> pixels = 1296000 + + >>> pos = SkyCoord(ra, dec, unit='degree') + >>> radius = Angle(radius_input, unit='arcmin') + >>> pixels = 60 + >>> im = DESILegacySurvey.get_images(pos, data_release=9, radius=radius, pixels=pixels) + +.. code-block:: python + + astroquery.exceptions.NoResultsWarning: HTTP Error 500: Internal Server Error - Problem retrieving the file at the url: https://www.legacysurvey.org/viewer/fits-cutout?ra=86.633212&dec=22.01446&size=1296000&layer=ls-dr9&pixscale=0.0002777777777777778&bands=g From ffb8018d3401785a577765c9ac1480d2d8215c96 Mon Sep 17 00:00:00 2001 From: burnout87 Date: Mon, 16 May 2022 12:04:28 +0200 Subject: [PATCH 02/13] addressing comments, second iteration --- astroquery/desi/__init__.py | 13 ++-- astroquery/desi/core.py | 64 ++++++++++++------- astroquery/desi/tests/setup_package.py | 1 - .../tests/{test_module.py => test_desi.py} | 22 +++---- ...t_module_remote.py => test_desi_remote.py} | 18 ++---- docs/desi/desi.rst | 55 ++++++---------- docs/index.rst | 1 + 7 files changed, 83 insertions(+), 91 deletions(-) rename astroquery/desi/tests/{test_module.py => test_desi.py} (87%) rename astroquery/desi/tests/{test_module_remote.py => test_desi_remote.py} (76%) diff --git a/astroquery/desi/__init__.py b/astroquery/desi/__init__.py index e8af61e0e7..6c05d0033b 100644 --- a/astroquery/desi/__init__.py +++ b/astroquery/desi/__init__.py @@ -20,14 +20,15 @@ class Conf(_config.ConfigNamespace): """ Configuration parameters for `astroquery.desi`. """ - server = _config.ConfigItem( - ['https://portal.nersc.gov/cfs/cosmo/data/legacysurvey/', + legacysurvey_service_url = _config.ConfigItem( + ['https://www.legacysurvey.org/viewer/fits-cutout', ], - 'base url') + 'url for the LegacySurvey service') - timeout = _config.ConfigItem( - 30, - 'Time limit for connecting to template_module server.') + tap_service_url = _config.ConfigItem( + ['https://datalab.noirlab.edu/tap', + ], + 'url for the TAP service') conf = Conf() diff --git a/astroquery/desi/core.py b/astroquery/desi/core.py index 37e0593015..c6099f1c1b 100644 --- a/astroquery/desi/core.py +++ b/astroquery/desi/core.py @@ -1,46 +1,48 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst import urllib.error import requests +import warnings import pyvo as vo import numpy as np +import astropy.coordinates as coord from astroquery.exceptions import NoResultsWarning from astroquery.query import BaseQuery from astroquery.utils import commons, async_to_sync - -from . import conf - +from astroquery.desi import conf __all__ = ['DESILegacySurvey', 'DESILegacySurveyClass'] -@async_to_sync class DESILegacySurveyClass(BaseQuery): - URL = conf.server - TIMEOUT = conf.timeout - def query_region(self, coordinates, radius, data_release=9): + def query_region(self, coordinates, radius, *, data_release=9): """ Queries a region around the specified coordinates. Parameters ---------- - coordinates : str or `astropy.coordinates`. - coordinates around which to query - radius : str or `astropy.units.Quantity`. - the radius of the cone search + coordinates : `astropy.coordinates` + coordinates around which to query. + radius : `astropy.units.Quantity` + the radius of the cone search. + data_release: int + the data release of the LegacySurvey to use. Returns ------- response : `astropy.table.Table` """ - url = 'https://datalab.noirlab.edu/tap' - tap_service = vo.dal.TAPService(url) - qstr = "SELECT all * FROM ls_dr" + str(data_release) + ".tractor WHERE dec>" + str(coordinates.dec.deg - radius.deg) + " and dec<" + str( - coordinates.dec.deg + radius.deg) + " and ra>" + str(coordinates.ra.deg - radius.deg / np.cos(coordinates.dec.deg * np.pi / 180.)) + " and ra<" + str( - coordinates.ra.deg + radius.deg / np.cos(coordinates.dec.deg * np.pi / 180)) + tap_service = vo.dal.TAPService(conf.tap_service_url) + coordinates_transformed = coordinates.transform_to(coord.ICRS) + + qstr = (f"SELECT all * FROM ls_dr{data_release}.tractor WHERE " + f"dec>{coordinates_transformed.dec.deg - radius.deg} and " + f"dec<{coordinates_transformed.dec.deg + radius.deg} and " + f"ra>{coordinates_transformed.ra.deg - radius.deg / np.cos(coordinates_transformed.dec.deg * np.pi / 180.)} and " + f"ra<{coordinates_transformed.ra.deg + radius.deg / np.cos(coordinates_transformed.dec.deg * np.pi / 180)}") tap_result = tap_service.run_sync(qstr) tap_result = tap_result.to_table() @@ -50,26 +52,44 @@ def query_region(self, coordinates, radius, data_release=9): return filtered_table - def get_images(self, position, data_release=9, pixels=None, radius=None, show_progress=True, image_band='g'): + def get_images(self, position, pixels, radius, *, data_release=9, show_progress=True, image_band='g'): """ + Downloads the images for a certain region of interest. + + Parameters + ------- + position: `astropy.coordinates`. + coordinates around which we define our region of interest. + radius: `astropy.units.Quantity`. + the radius of the cone search. + data_release: int + the data release of the LegacySurvey to use. + Returns ------- - A list of `astropy.io.fits.HDUList` objects. + list: A list of `astropy.io.fits.HDUList` objects. """ + position_transformed = position.transform_to(coord.ICRS) + image_size_arcsec = radius.arcsec pixsize = 2 * image_size_arcsec / pixels - image_url = 'https://www.legacysurvey.org/viewer/fits-cutout?ra=' + str(position.ra.deg) + '&dec=' + str(position.dec.deg) + '&size=' + str( - pixels) + '&layer=ls-dr' + str(data_release) + '&pixscale=' + str(pixsize) + '&bands=' + image_band + image_url = (f"{conf.legacysurvey_service_url}?" + f"ra={position_transformed.ra.deg}&" + f"dec={position_transformed.dec.deg}&" + f"size={pixels}&" + f"layer=ls-dr{data_release}&" + f"pixscale={pixsize}&" + f"bands={image_band}") file_container = commons.FileContainer(image_url, encoding='binary', show_progress=show_progress) try: fits_file = file_container.get_fits() except (requests.exceptions.HTTPError, urllib.error.HTTPError) as e: - # TODO not sure this is the most suitable exception - raise NoResultsWarning(f"{str(e)} - Problem retrieving the file at the url: {str(image_url)}") + fits_file = None + warnings.warn(f"{str(e)} - Problem retrieving the file at the url: {image_url}", NoResultsWarning) return [fits_file] diff --git a/astroquery/desi/tests/setup_package.py b/astroquery/desi/tests/setup_package.py index df5b7c82e5..2e89d237d6 100644 --- a/astroquery/desi/tests/setup_package.py +++ b/astroquery/desi/tests/setup_package.py @@ -5,6 +5,5 @@ def get_package_data(): paths = [os.path.join('data', '*.txt'), os.path.join('data', '*.fits'), - os.path.join('data', '*.fits.gz'), ] return {'astroquery.desi.tests': paths} diff --git a/astroquery/desi/tests/test_module.py b/astroquery/desi/tests/test_desi.py similarity index 87% rename from astroquery/desi/tests/test_module.py rename to astroquery/desi/tests/test_desi.py index 6e047bea23..a5b8b8ba67 100644 --- a/astroquery/desi/tests/test_module.py +++ b/astroquery/desi/tests/test_desi.py @@ -6,13 +6,13 @@ from astropy.table import Table from astropy.io import fits -from pyvo.dal import TAPResults +from astropy import coordinates as coord +from astroquery.utils.mocks import MockResponse +from astroquery.utils import commons +from astroquery import desi from urllib import parse from contextlib import contextmanager -from ... import desi -from ...utils.mocks import MockResponse -from ...utils import commons DATA_FILES = { 'dummy_tap_table': 'dummy_table.txt', @@ -21,7 +21,7 @@ } coords = commons.ICRSCoord('11h04m27s +38d12m32s') -radius = commons.ArcminRadiusGenerator(0.5) +radius = coord.Angle(0.5, unit='arcmin') pixels = 60 data_release = 9 emispheres_list = ['north', 'south'] @@ -29,10 +29,7 @@ @pytest.fixture def patch_get(request): - try: - mp = request.getfixturevalue("monkeypatch") - except AttributeError: # pytest < 3 - mp = request.getfuncargvalue("monkeypatch") + mp = request.getfixturevalue("monkeypatch") mp.setattr(desi.DESILegacySurvey, '_request', get_mockreturn) return mp @@ -68,10 +65,7 @@ def get_readable_fileobj_mockreturn(filename, **kwargs): @pytest.fixture def patch_tap(request): - try: - mp = request.getfixturevalue("monkeypatch") - except AttributeError: # pytest < 3 - mp = request.getfuncargvalue("monkeypatch") + mp = request.getfixturevalue("monkeypatch") mp.setattr(vo.dal.TAPService, 'run_sync', tap_mockreturn) return mp @@ -95,7 +89,7 @@ def tap_mockreturn(url, params=None, timeout=10, **kwargs): content_table = Table.read(data_path(DATA_FILES['dummy_tap_table']), format='ascii.csv', comment='#') votable_table = astropy.io.votable.from_table(content_table) - return TAPResults(votable_table) + return vo.dal.TAPResults(votable_table) def data_path(filename): diff --git a/astroquery/desi/tests/test_module_remote.py b/astroquery/desi/tests/test_desi_remote.py similarity index 76% rename from astroquery/desi/tests/test_module_remote.py rename to astroquery/desi/tests/test_desi_remote.py index 30edcc4bd6..4e6d2c6c6b 100644 --- a/astroquery/desi/tests/test_module_remote.py +++ b/astroquery/desi/tests/test_desi_remote.py @@ -1,7 +1,10 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst - import pytest +import astroquery.desi + from astropy.io.fits import HDUList +from astropy.coordinates import SkyCoord +from astropy.coordinates import Angle +from astropy.table import Table from astroquery.exceptions import NoResultsWarning @@ -9,10 +12,6 @@ class TestLegacySurveyClass: def test_query_region(self): - import astroquery.desi - from astropy.coordinates import SkyCoord - from astropy.coordinates import Angle - from astropy.table import Table ra = Angle('11h04m27s', unit='hourangle').degree dec = Angle('+38d12m32s', unit='hourangle').degree @@ -26,9 +25,6 @@ def test_query_region(self): @pytest.mark.parametrize("valid_inputs", [True, False]) def test_get_images(self, valid_inputs): - import astroquery.desi - from astropy.coordinates import SkyCoord - from astropy.coordinates import Angle if valid_inputs: ra = Angle('11h04m27s', unit='hourangle').degree @@ -45,9 +41,9 @@ def test_get_images(self, valid_inputs): radius = Angle(radius_input, unit='arcmin') if valid_inputs: - query1 = astroquery.desi.DESILegacySurvey.get_images(pos, data_release=9, radius=radius, pixels=pixels) + query1 = astroquery.desi.DESILegacySurvey.get_images(pos, pixels, radius, data_release=9) assert isinstance(query1, list) assert isinstance(query1[0], HDUList) else: with pytest.raises(NoResultsWarning): - astroquery.desi.DESILegacySurvey.get_images(pos, data_release=9, radius=radius, pixels=pixels) + astroquery.desi.DESILegacySurvey.get_images(pos, pixels, radius, data_release=9) diff --git a/docs/desi/desi.rst b/docs/desi/desi.rst index b79269bda1..ec696347c3 100644 --- a/docs/desi/desi.rst +++ b/docs/desi/desi.rst @@ -1,8 +1,8 @@ .. _astroquery.desi: -************************************ +********************************************* DESI LegacySurvey Queries (`astroquery.desi`) -************************************ +********************************************* Getting started =============== @@ -12,13 +12,13 @@ Presented below are examples that illustrate the different types of queries that can be formulated. Query a region -=============== +============== This example shows how to query a certain region with DesiLegacySurvey. We'll use a set of coordinates that define the region of interest, and search within a 5 arcmin radius. -.. code-block:: python +.. doctest-remote-data:: >>> from astroquery.desi import DESILegacySurvey >>> from astropy.coordinates import Angle, SkyCoord @@ -28,41 +28,43 @@ and search within a 5 arcmin radius. >>> radius = Angle(5, unit='arcmin') >>> table_out = DESILegacySurvey.query_region(coordinates, radius, data_release=9) >>> print(table_out[:5]) - - ls_id dec ra ... type wise_coadd_id - ---------------- ------------------ ------------------ ... ---- ------------- + ls_id dec ra ... type wise_coadd_id + ... + ---------------- ----------------- ------------------ ... ---- ------------- 9907734382838387 38.12628495570797 166.0838654387131 ... PSF 1667p378 9907734382838488 38.12798336424771 166.0922968862182 ... PSF 1667p378 9907734382838554 38.12858283671958 166.0980673954384 ... REX 1667p378 - 9907734382838564 38.12840803351445 166.09921863682337 ... EXP 1667p378 - 9907734382838584 38.12836885301038 166.10070750146636 ... REX 1667p378 + 9907734382838564 38.12840803351445 166.09921863682337 ... EXP 1667p378 + 9907734382838584 38.12836885301038 166.10070750146636 ... REX 1667p378 The result is an astropy.Table. Get images -=============== +========== To download images for a certain region of interest, we can define our region in the same way used in the example above. -.. code-block:: python +.. doctest-remote-data:: >>> from astroquery.desi import DESILegacySurvey >>> from astropy.coordinates import Angle, SkyCoord + >>> >>> ra = Angle('11h04m27s', unit='hourangle').degree >>> dec = Angle('+38d12m32s', unit='hourangle').degree - >>> pos = SkyCoord(ra, dec, unit='degree') - >>> radius = Angle(0.5, unit='arcmin') + >>> radius_input = 0.5 >>> pixels = 60 - >>> im = DESILegacySurvey.get_images(pos, data_release=9, radius=radius, pixels=pixels) + >>> + >>> pos = SkyCoord(ra, dec, unit='degree') + >>> radius = Angle(radius_input, unit='arcmin') + >>> im = DESILegacySurvey.get_images(pos, pixels, radius, data_release=9) All the information we need can be found within the object "im". -.. code-block:: python +.. doctest-remote-data:: >>> hdul = im[0] >>> hdul[0].header - SIMPLE = T / file does conform to FITS standard BITPIX = -32 / number of bits per data pixel NAXIS = 2 / number of data axes @@ -88,24 +90,3 @@ All the information we need can be found within the object "im". The variable "im" is a list of `~astropy.io.fits.HDUList` objects, one entry for each corresponding object. - -In case a set of not valid coordinates is provided, then a `astroquery.exceptions.NoResultsWarning` -exception is raised, as shown in the example below. - -.. code-block:: python - - >>> from astroquery.desi import DESILegacySurvey - >>> from astropy.coordinates import Angle, SkyCoord - >>> ra = Angle('86.633212', unit='degree').degree - >>> dec = Angle('22.01446', unit='degree').degree - >>> radius_input = 3 - >>> pixels = 1296000 - - >>> pos = SkyCoord(ra, dec, unit='degree') - >>> radius = Angle(radius_input, unit='arcmin') - >>> pixels = 60 - >>> im = DESILegacySurvey.get_images(pos, data_release=9, radius=radius, pixels=pixels) - -.. code-block:: python - - astroquery.exceptions.NoResultsWarning: HTTP Error 500: Internal Server Error - Problem retrieving the file at the url: https://www.legacysurvey.org/viewer/fits-cutout?ra=86.633212&dec=22.01446&size=1296000&layer=ls-dr9&pixscale=0.0002777777777777778&bands=g diff --git a/docs/index.rst b/docs/index.rst index 8e5ab7e85a..5c7e75bc21 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -185,6 +185,7 @@ The following modules have been completed using a common API: cds/cds.rst linelists/cdms/cdms.rst dace/dace.rst + desi/desi.rst esa/hsa/hsa.rst esa/hubble/hubble.rst esa/iso/iso.rst From 9b679bcf2141647d3bdc131bfc237e78229f40bf Mon Sep 17 00:00:00 2001 From: burnout87 Date: Mon, 13 Jun 2022 17:06:38 +0200 Subject: [PATCH 03/13] query_region radius default value and doc --- astroquery/desi/core.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/astroquery/desi/core.py b/astroquery/desi/core.py index c6099f1c1b..1206b60b38 100644 --- a/astroquery/desi/core.py +++ b/astroquery/desi/core.py @@ -17,16 +17,17 @@ class DESILegacySurveyClass(BaseQuery): - def query_region(self, coordinates, radius, *, data_release=9): + def query_region(self, coordinates, radius=None, *, data_release=9): """ Queries a region around the specified coordinates. Parameters ---------- - coordinates : `astropy.coordinates` + coordinates : `~astropy.coordinates.SkyCoord` coordinates around which to query. - radius : `astropy.units.Quantity` - the radius of the cone search. + radius : `~astropy.coordinates.Angle`, optional + the radius of the region. If missing, set to default + value of 0.5 arcmin. data_release: int the data release of the LegacySurvey to use. @@ -35,6 +36,9 @@ def query_region(self, coordinates, radius, *, data_release=9): response : `astropy.table.Table` """ + if radius is None: + radius = coord.Angle(0.5, unit='arcmin') + tap_service = vo.dal.TAPService(conf.tap_service_url) coordinates_transformed = coordinates.transform_to(coord.ICRS) From b38d853e1157f7a29ef28c5be7d7013b49263984 Mon Sep 17 00:00:00 2001 From: burnout87 Date: Tue, 14 Jun 2022 08:17:06 +0200 Subject: [PATCH 04/13] using float values and adjusting imports --- astroquery/desi/tests/test_desi_remote.py | 24 +++++++++++------------ 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/astroquery/desi/tests/test_desi_remote.py b/astroquery/desi/tests/test_desi_remote.py index 4e6d2c6c6b..8a789f96d8 100644 --- a/astroquery/desi/tests/test_desi_remote.py +++ b/astroquery/desi/tests/test_desi_remote.py @@ -1,6 +1,6 @@ import pytest -import astroquery.desi +from astroquery.desi import DESILegacySurvey from astropy.io.fits import HDUList from astropy.coordinates import SkyCoord from astropy.coordinates import Angle @@ -13,13 +13,11 @@ class TestLegacySurveyClass: def test_query_region(self): - ra = Angle('11h04m27s', unit='hourangle').degree - dec = Angle('+38d12m32s', unit='hourangle').degree - coordinates = SkyCoord(ra, dec, unit='degree') + coordinates = SkyCoord('11h04m27s +38d12m32s') radius = Angle(5, unit='arcmin') - query1 = astroquery.desi.DESILegacySurvey.query_region(coordinates, radius=radius, data_release=9) + query1 = DESILegacySurvey.query_region(coordinates, radius=radius, data_release=9) assert isinstance(query1, Table) @@ -27,23 +25,23 @@ def test_query_region(self): def test_get_images(self, valid_inputs): if valid_inputs: - ra = Angle('11h04m27s', unit='hourangle').degree - dec = Angle('+38d12m32s', unit='hourangle').degree - radius_input = 0.5 # arcmin + ra = 166.1125 + dec = 38.209 + radius_input = 0.5 pixels = 60 else: - ra = Angle('86.633212', unit='degree').degree - dec = Angle('22.01446', unit='degree').degree - radius_input = 3 # arcmin + ra = 86.633212 + dec = 22.01446 + radius_input = 3 pixels = 1296000 pos = SkyCoord(ra, dec, unit='degree') radius = Angle(radius_input, unit='arcmin') if valid_inputs: - query1 = astroquery.desi.DESILegacySurvey.get_images(pos, pixels, radius, data_release=9) + query1 = DESILegacySurvey.get_images(pos, pixels, radius, data_release=9) assert isinstance(query1, list) assert isinstance(query1[0], HDUList) else: with pytest.raises(NoResultsWarning): - astroquery.desi.DESILegacySurvey.get_images(pos, pixels, radius, data_release=9) + DESILegacySurvey.get_images(pos, pixels, radius, data_release=9) From e41373d5d23905fd6d061240c9aafc75c71a69de Mon Sep 17 00:00:00 2001 From: burnout87 Date: Tue, 14 Jun 2022 08:44:53 +0200 Subject: [PATCH 05/13] using Quantity class instead of Angle, fixed docstr --- astroquery/desi/core.py | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/astroquery/desi/core.py b/astroquery/desi/core.py index 1206b60b38..09bbbce683 100644 --- a/astroquery/desi/core.py +++ b/astroquery/desi/core.py @@ -7,9 +7,11 @@ import numpy as np import astropy.coordinates as coord +from astropy import units as u +from astropy.units import Quantity from astroquery.exceptions import NoResultsWarning from astroquery.query import BaseQuery -from astroquery.utils import commons, async_to_sync +from astroquery.utils import commons from astroquery.desi import conf __all__ = ['DESILegacySurvey', 'DESILegacySurveyClass'] @@ -25,7 +27,7 @@ def query_region(self, coordinates, radius=None, *, data_release=9): ---------- coordinates : `~astropy.coordinates.SkyCoord` coordinates around which to query. - radius : `~astropy.coordinates.Angle`, optional + radius : `~astropy.units.Quantity`, optional the radius of the region. If missing, set to default value of 0.5 arcmin. data_release: int @@ -33,20 +35,20 @@ def query_region(self, coordinates, radius=None, *, data_release=9): Returns ------- - response : `astropy.table.Table` + response : `~astropy.table.Table` """ if radius is None: - radius = coord.Angle(0.5, unit='arcmin') + radius = Quantity(0.5, unit='arcmin') tap_service = vo.dal.TAPService(conf.tap_service_url) coordinates_transformed = coordinates.transform_to(coord.ICRS) qstr = (f"SELECT all * FROM ls_dr{data_release}.tractor WHERE " - f"dec>{coordinates_transformed.dec.deg - radius.deg} and " - f"dec<{coordinates_transformed.dec.deg + radius.deg} and " - f"ra>{coordinates_transformed.ra.deg - radius.deg / np.cos(coordinates_transformed.dec.deg * np.pi / 180.)} and " - f"ra<{coordinates_transformed.ra.deg + radius.deg / np.cos(coordinates_transformed.dec.deg * np.pi / 180)}") + f"dec>{(coordinates_transformed.dec - radius).to(u.deg).value} and " + f"dec<{(coordinates_transformed.dec + radius).to(u.deg).value} and " + f"ra>{coordinates_transformed.ra.to(u.deg).value - radius.to(u.deg).value / np.cos(coordinates_transformed.dec.to(u.deg).value * np.pi / 180.)} and " + f"ra<{coordinates_transformed.ra.to(u.deg).value + radius.to(u.deg).value / np.cos(coordinates_transformed.dec.to(u.deg).value * np.pi / 180)}") tap_result = tap_service.run_sync(qstr) tap_result = tap_result.to_table() @@ -56,24 +58,31 @@ def query_region(self, coordinates, radius=None, *, data_release=9): return filtered_table - def get_images(self, position, pixels, radius, *, data_release=9, show_progress=True, image_band='g'): + def get_images(self, position, pixels, radius=None, *, data_release=9, show_progress=True, image_band='g'): """ Downloads the images for a certain region of interest. Parameters ------- - position: `astropy.coordinates`. + position: `~astropy.coordinates`. coordinates around which we define our region of interest. - radius: `astropy.units.Quantity`. - the radius of the cone search. - data_release: int + radius: `~astropy.units.Quantity`, optional + the radius of our region of interest. + data_release: int, optional the data release of the LegacySurvey to use. + show_progress: bool, optional + Whether to display a progress bar if the file is downloaded + from a remote server. Default is True. + image_band: str, optional Returns ------- - list: A list of `astropy.io.fits.HDUList` objects. + list: A list of `~astropy.io.fits.HDUList` objects. """ + if radius is None: + radius = Quantity(0.5, u.arcmin) + position_transformed = position.transform_to(coord.ICRS) image_size_arcsec = radius.arcsec @@ -91,9 +100,9 @@ def get_images(self, position, pixels, radius, *, data_release=9, show_progress= try: fits_file = file_container.get_fits() - except (requests.exceptions.HTTPError, urllib.error.HTTPError) as e: + except (requests.exceptions.HTTPError, urllib.error.HTTPError) as exp: fits_file = None - warnings.warn(f"{str(e)} - Problem retrieving the file at the url: {image_url}", NoResultsWarning) + warnings.warn(f"{str(exp)} - Problem retrieving the file at the url: {image_url}", NoResultsWarning) return [fits_file] From 73b52c139f047c45d81612316c8737eafc55d3f4 Mon Sep 17 00:00:00 2001 From: burnout87 Date: Tue, 14 Jun 2022 14:42:12 +0200 Subject: [PATCH 06/13] get_images doc --- astroquery/desi/core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/astroquery/desi/core.py b/astroquery/desi/core.py index 09bbbce683..2c95e14ef5 100644 --- a/astroquery/desi/core.py +++ b/astroquery/desi/core.py @@ -74,6 +74,7 @@ def get_images(self, position, pixels, radius=None, *, data_release=9, show_prog Whether to display a progress bar if the file is downloaded from a remote server. Default is True. image_band: str, optional + Default to 'g' Returns ------- From a2bab0420134da97e7194b01a4955c8082e51018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Brigitta=20Sip=C5=91cz?= Date: Sun, 12 Jun 2022 15:39:14 -0700 Subject: [PATCH 07/13] Fixing sphinx build --- astroquery/desi/__init__.py | 2 +- astroquery/desi/core.py | 2 +- docs/desi/desi.rst | 7 +++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/astroquery/desi/__init__.py b/astroquery/desi/__init__.py index 6c05d0033b..40466fbc12 100644 --- a/astroquery/desi/__init__.py +++ b/astroquery/desi/__init__.py @@ -4,7 +4,7 @@ DESI LegacySurvery https://www.legacysurvey.org/ -------------------------- +----------------------------- :author: Gabriele Barni (Gabriele.Barni@unige.ch) """ diff --git a/astroquery/desi/core.py b/astroquery/desi/core.py index 2c95e14ef5..4ecab36c1f 100644 --- a/astroquery/desi/core.py +++ b/astroquery/desi/core.py @@ -64,7 +64,7 @@ def get_images(self, position, pixels, radius=None, *, data_release=9, show_prog Parameters ------- - position: `~astropy.coordinates`. + position: `astropy.coordinates`. coordinates around which we define our region of interest. radius: `~astropy.units.Quantity`, optional the radius of our region of interest. diff --git a/docs/desi/desi.rst b/docs/desi/desi.rst index ec696347c3..4dca0d0149 100644 --- a/docs/desi/desi.rst +++ b/docs/desi/desi.rst @@ -90,3 +90,10 @@ All the information we need can be found within the object "im". The variable "im" is a list of `~astropy.io.fits.HDUList` objects, one entry for each corresponding object. + + +Reference/API +============= + +.. automodapi:: astroquery.desi + :no-inheritance-diagram: From 0c7a4b6d927044fb359ee2be2962754b1922dc6d Mon Sep 17 00:00:00 2001 From: burnout87 Date: Wed, 15 Jun 2022 10:48:59 +0200 Subject: [PATCH 08/13] ra and dec parameters test_query_region --- astroquery/desi/tests/test_desi_remote.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/astroquery/desi/tests/test_desi_remote.py b/astroquery/desi/tests/test_desi_remote.py index 8a789f96d8..a33bfa7328 100644 --- a/astroquery/desi/tests/test_desi_remote.py +++ b/astroquery/desi/tests/test_desi_remote.py @@ -12,8 +12,9 @@ class TestLegacySurveyClass: def test_query_region(self): - - coordinates = SkyCoord('11h04m27s +38d12m32s') + ra = 166.1125 + dec = 38.209 + coordinates = SkyCoord(ra, dec, unit='degree') radius = Angle(5, unit='arcmin') From 85fbfce55d72666f544e1cb542cb8a06ebb2012f Mon Sep 17 00:00:00 2001 From: burnout87 Date: Wed, 15 Jun 2022 14:43:18 +0200 Subject: [PATCH 09/13] more concise default value for radius --- astroquery/desi/core.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/astroquery/desi/core.py b/astroquery/desi/core.py index 4ecab36c1f..43cf7beeba 100644 --- a/astroquery/desi/core.py +++ b/astroquery/desi/core.py @@ -19,7 +19,7 @@ class DESILegacySurveyClass(BaseQuery): - def query_region(self, coordinates, radius=None, *, data_release=9): + def query_region(self, coordinates, radius=0.5*u.arcmin, *, data_release=9): """ Queries a region around the specified coordinates. @@ -38,17 +38,13 @@ def query_region(self, coordinates, radius=None, *, data_release=9): response : `~astropy.table.Table` """ - if radius is None: - radius = Quantity(0.5, unit='arcmin') - tap_service = vo.dal.TAPService(conf.tap_service_url) coordinates_transformed = coordinates.transform_to(coord.ICRS) qstr = (f"SELECT all * FROM ls_dr{data_release}.tractor WHERE " - f"dec>{(coordinates_transformed.dec - radius).to(u.deg).value} and " - f"dec<{(coordinates_transformed.dec + radius).to(u.deg).value} and " - f"ra>{coordinates_transformed.ra.to(u.deg).value - radius.to(u.deg).value / np.cos(coordinates_transformed.dec.to(u.deg).value * np.pi / 180.)} and " - f"ra<{coordinates_transformed.ra.to(u.deg).value + radius.to(u.deg).value / np.cos(coordinates_transformed.dec.to(u.deg).value * np.pi / 180)}") + f"dec<{(coordinates_transformed.dec + radius).to_value(u.deg)} and " + f"ra>{coordinates_transformed.ra.to_value(u.deg) - radius.to_value(u.deg) / np.cos(coordinates_transformed.dec)} and " + f"ra<{coordinates_transformed.ra.to_value(u.deg) + radius.to_value(u.deg) / np.cos(coordinates_transformed.dec)}") tap_result = tap_service.run_sync(qstr) tap_result = tap_result.to_table() @@ -58,7 +54,7 @@ def query_region(self, coordinates, radius=None, *, data_release=9): return filtered_table - def get_images(self, position, pixels, radius=None, *, data_release=9, show_progress=True, image_band='g'): + def get_images(self, position, pixels=None, radius=0.5*u.arcmin, *, data_release=9, show_progress=True, image_band='g'): """ Downloads the images for a certain region of interest. @@ -81,9 +77,6 @@ def get_images(self, position, pixels, radius=None, *, data_release=9, show_prog list: A list of `~astropy.io.fits.HDUList` objects. """ - if radius is None: - radius = Quantity(0.5, u.arcmin) - position_transformed = position.transform_to(coord.ICRS) image_size_arcsec = radius.arcsec From d533beaad72df0ba11b432eed845a4a4d7e81efc Mon Sep 17 00:00:00 2001 From: burnout87 Date: Wed, 15 Jun 2022 14:46:31 +0200 Subject: [PATCH 10/13] only using getfixturevalue for test --- astroquery/desi/tests/test_desi.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/astroquery/desi/tests/test_desi.py b/astroquery/desi/tests/test_desi.py index a5b8b8ba67..b9da12a836 100644 --- a/astroquery/desi/tests/test_desi.py +++ b/astroquery/desi/tests/test_desi.py @@ -53,10 +53,7 @@ def get_readable_fileobj_mockreturn(filename, **kwargs): if f is not None: f.close() - try: - mp = request.getfixturevalue("monkeypatch") - except AttributeError: # pytest < 3 - mp = request.getfuncargvalue("monkeypatch") + mp = request.getfixturevalue("monkeypatch") mp.setattr(commons, 'get_readable_fileobj', get_readable_fileobj_mockreturn) From 7d79d2750880ca8e9ed473859b130e5b56029073 Mon Sep 17 00:00:00 2001 From: burnout87 Date: Wed, 15 Jun 2022 14:49:15 +0200 Subject: [PATCH 11/13] fixing fixture for test_desi --- astroquery/desi/tests/test_desi.py | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/astroquery/desi/tests/test_desi.py b/astroquery/desi/tests/test_desi.py index b9da12a836..0469521529 100644 --- a/astroquery/desi/tests/test_desi.py +++ b/astroquery/desi/tests/test_desi.py @@ -40,18 +40,9 @@ def patch_get_readable_fileobj(request): @contextmanager def get_readable_fileobj_mockreturn(filename, **kwargs): file_obj = data_path(DATA_FILES['dummy_hdu_list_fits']) # TODO: add images option - encoding = kwargs.get('encoding', None) - f = None - try: - if encoding == 'binary': - f = open(file_obj, 'rb') - yield f - else: - f = open(file_obj, 'rb') - yield f - finally: - if f is not None: - f.close() + # f = None + with open(file_obj, 'rb') as f: + yield f mp = request.getfixturevalue("monkeypatch") @@ -78,7 +69,8 @@ def get_mockreturn(method, url, params=None, timeout=10, **kwargs): filename = data_path(DATA_FILES['dummy_tractor_fits']) if filename is not None: - content = open(filename, 'rb').read() + with open(filename, 'rb') as f: + content = f.read() return MockResponse(content) From 4a13dd6ef124f948fc404d03ebbdae48fc0667bf Mon Sep 17 00:00:00 2001 From: Adam Ginsburg Date: Fri, 28 Oct 2022 08:52:55 -0400 Subject: [PATCH 12/13] remove an unused import and fix tests --- astroquery/desi/core.py | 1 - astroquery/desi/tests/test_desi.py | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/astroquery/desi/core.py b/astroquery/desi/core.py index 43cf7beeba..65c0a8c608 100644 --- a/astroquery/desi/core.py +++ b/astroquery/desi/core.py @@ -8,7 +8,6 @@ import astropy.coordinates as coord from astropy import units as u -from astropy.units import Quantity from astroquery.exceptions import NoResultsWarning from astroquery.query import BaseQuery from astroquery.utils import commons diff --git a/astroquery/desi/tests/test_desi.py b/astroquery/desi/tests/test_desi.py index 0469521529..75f58e025e 100644 --- a/astroquery/desi/tests/test_desi.py +++ b/astroquery/desi/tests/test_desi.py @@ -20,7 +20,7 @@ 'dummy_hdu_list_fits': 'hdu_list_images.fits' } -coords = commons.ICRSCoord('11h04m27s +38d12m32s') +coords = coord.SkyCoord('11h04m27s +38d12m32s', frame='icrs') radius = coord.Angle(0.5, unit='arcmin') pixels = 60 data_release = 9 @@ -96,9 +96,9 @@ def compare_result_data(result, data): def image_tester(images, filetype): assert type(images) == list - data = fits.open(data_path(DATA_FILES[filetype])) - assert images[0][0].header == data[0].header - assert np.array_equal(images[0][0].data, data[0].data) + with fits.open(data_path(DATA_FILES[filetype])) as data: + assert images[0][0].header == data[0].header + assert np.array_equal(images[0][0].data, data[0].data) def test_coords_query_region(patch_tap, coords=coords, radius=radius): From 7cc8d8b12829a7d3696266f333da1cfca3fe3d01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Brigitta=20Sip=C5=91cz?= Date: Tue, 8 Nov 2022 16:27:16 -0800 Subject: [PATCH 13/13] TST: addressing more review comments --- astroquery/desi/core.py | 34 +++++++++---------- astroquery/desi/tests/test_desi.py | 8 ++--- astroquery/desi/tests/test_desi_remote.py | 41 ++++++++++------------- docs/desi/desi.rst | 12 +++---- 4 files changed, 44 insertions(+), 51 deletions(-) diff --git a/astroquery/desi/core.py b/astroquery/desi/core.py index 65c0a8c608..5c45e90962 100644 --- a/astroquery/desi/core.py +++ b/astroquery/desi/core.py @@ -18,7 +18,7 @@ class DESILegacySurveyClass(BaseQuery): - def query_region(self, coordinates, radius=0.5*u.arcmin, *, data_release=9): + def query_region(self, coordinates, *, width=0.5*u.arcmin, data_release=9): """ Queries a region around the specified coordinates. @@ -26,10 +26,10 @@ def query_region(self, coordinates, radius=0.5*u.arcmin, *, data_release=9): ---------- coordinates : `~astropy.coordinates.SkyCoord` coordinates around which to query. - radius : `~astropy.units.Quantity`, optional - the radius of the region. If missing, set to default + width : `~astropy.units.Quantity`, optional + the width of the region. If missing, set to default value of 0.5 arcmin. - data_release: int + data_release : int the data release of the LegacySurvey to use. Returns @@ -41,9 +41,9 @@ def query_region(self, coordinates, radius=0.5*u.arcmin, *, data_release=9): coordinates_transformed = coordinates.transform_to(coord.ICRS) qstr = (f"SELECT all * FROM ls_dr{data_release}.tractor WHERE " - f"dec<{(coordinates_transformed.dec + radius).to_value(u.deg)} and " - f"ra>{coordinates_transformed.ra.to_value(u.deg) - radius.to_value(u.deg) / np.cos(coordinates_transformed.dec)} and " - f"ra<{coordinates_transformed.ra.to_value(u.deg) + radius.to_value(u.deg) / np.cos(coordinates_transformed.dec)}") + f"dec<{(coordinates_transformed.dec + width).to_value(u.deg)} and " + f"ra>{coordinates_transformed.ra.to_value(u.deg) - width.to_value(u.deg) / np.cos(coordinates_transformed.dec)} and " + f"ra<{coordinates_transformed.ra.to_value(u.deg) + width.to_value(u.deg) / np.cos(coordinates_transformed.dec)}") tap_result = tap_service.run_sync(qstr) tap_result = tap_result.to_table() @@ -53,32 +53,32 @@ def query_region(self, coordinates, radius=0.5*u.arcmin, *, data_release=9): return filtered_table - def get_images(self, position, pixels=None, radius=0.5*u.arcmin, *, data_release=9, show_progress=True, image_band='g'): + def get_images(self, position, *, pixels=None, width=0.5*u.arcmin, data_release=9, show_progress=True, image_band='g'): """ Downloads the images for a certain region of interest. Parameters - ------- - position: `astropy.coordinates`. + ---------- + position : `astropy.coordinates`. coordinates around which we define our region of interest. - radius: `~astropy.units.Quantity`, optional - the radius of our region of interest. - data_release: int, optional + width : `~astropy.units.Quantity`, optional + the width of our region of interest. + data_release : int, optional the data release of the LegacySurvey to use. - show_progress: bool, optional + show_progress : bool, optional Whether to display a progress bar if the file is downloaded from a remote server. Default is True. - image_band: str, optional + image_band : str, optional Default to 'g' Returns ------- - list: A list of `~astropy.io.fits.HDUList` objects. + list : A list of `~astropy.io.fits.HDUList` objects. """ position_transformed = position.transform_to(coord.ICRS) - image_size_arcsec = radius.arcsec + image_size_arcsec = width.arcsec pixsize = 2 * image_size_arcsec / pixels image_url = (f"{conf.legacysurvey_service_url}?" diff --git a/astroquery/desi/tests/test_desi.py b/astroquery/desi/tests/test_desi.py index 75f58e025e..0d9ae737ff 100644 --- a/astroquery/desi/tests/test_desi.py +++ b/astroquery/desi/tests/test_desi.py @@ -21,7 +21,7 @@ } coords = coord.SkyCoord('11h04m27s +38d12m32s', frame='icrs') -radius = coord.Angle(0.5, unit='arcmin') +width = coord.Angle(0.5, unit='arcmin') pixels = 60 data_release = 9 emispheres_list = ['north', 'south'] @@ -101,8 +101,8 @@ def image_tester(images, filetype): assert np.array_equal(images[0][0].data, data[0].data) -def test_coords_query_region(patch_tap, coords=coords, radius=radius): - result = desi.DESILegacySurvey.query_region(coords, radius) +def test_coords_query_region(patch_tap): + result = desi.DESILegacySurvey.query_region(coords, width=width) data = Table.read(data_path(DATA_FILES['dummy_tap_table']), format='ascii.csv', comment='#') data['objid'] = data['objid'].astype(np.int64) @@ -110,6 +110,6 @@ def test_coords_query_region(patch_tap, coords=coords, radius=radius): def test_coords_get_images(patch_get_readable_fileobj, dr=data_release): - images_list = desi.DESILegacySurvey.get_images(coords, data_release=dr, radius=radius, pixels=pixels) + images_list = desi.DESILegacySurvey.get_images(coords, data_release=dr, width=width, pixels=pixels) image_tester(images_list, 'dummy_hdu_list_fits') diff --git a/astroquery/desi/tests/test_desi_remote.py b/astroquery/desi/tests/test_desi_remote.py index a33bfa7328..be607955cd 100644 --- a/astroquery/desi/tests/test_desi_remote.py +++ b/astroquery/desi/tests/test_desi_remote.py @@ -16,33 +16,26 @@ def test_query_region(self): dec = 38.209 coordinates = SkyCoord(ra, dec, unit='degree') - radius = Angle(5, unit='arcmin') + width = Angle(15, unit='arcsec') - query1 = DESILegacySurvey.query_region(coordinates, radius=radius, data_release=9) + query1 = DESILegacySurvey.query_region(coordinates, width=width, data_release=9) assert isinstance(query1, Table) - @pytest.mark.parametrize("valid_inputs", [True, False]) - def test_get_images(self, valid_inputs): + @pytest.mark.parametrize(('ra', 'dec', 'width', 'pixels'), + ((166.1125, 38.209, 0.5, 60),)) + def test_get_images(self, ra, dec, width, pixels): + pos = SkyCoord(ra, dec, unit='degree') + width = Angle(width, unit='arcmin') - if valid_inputs: - ra = 166.1125 - dec = 38.209 - radius_input = 0.5 - pixels = 60 - else: - ra = 86.633212 - dec = 22.01446 - radius_input = 3 - pixels = 1296000 + query1 = DESILegacySurvey.get_images(pos, pixels=pixels, width=width, data_release=9) + assert isinstance(query1, list) + assert isinstance(query1[0], HDUList) - pos = SkyCoord(ra, dec, unit='degree') - radius = Angle(radius_input, unit='arcmin') - - if valid_inputs: - query1 = DESILegacySurvey.get_images(pos, pixels, radius, data_release=9) - assert isinstance(query1, list) - assert isinstance(query1[0], HDUList) - else: - with pytest.raises(NoResultsWarning): - DESILegacySurvey.get_images(pos, pixels, radius, data_release=9) + def test_noresults_warning(self): + # Using position with no coverage + pos = SkyCoord(86.633212, 22.01446, unit='degree') + width = Angle(3, unit='arcmin') + + with pytest.warns(NoResultsWarning): + DESILegacySurvey.get_images(pos, width=width, pixels=100) diff --git a/docs/desi/desi.rst b/docs/desi/desi.rst index 4dca0d0149..eb66967644 100644 --- a/docs/desi/desi.rst +++ b/docs/desi/desi.rst @@ -16,7 +16,7 @@ Query a region This example shows how to query a certain region with DesiLegacySurvey. We'll use a set of coordinates that define the region of interest, -and search within a 5 arcmin radius. +and search within a 15 arcsec box. .. doctest-remote-data:: @@ -25,9 +25,9 @@ and search within a 5 arcmin radius. >>> ra = Angle('11h04m27s', unit='hourangle').degree >>> dec = Angle('+38d12m32s', unit='hourangle').degree >>> coordinates = SkyCoord(ra, dec, unit='degree') - >>> radius = Angle(5, unit='arcmin') - >>> table_out = DESILegacySurvey.query_region(coordinates, radius, data_release=9) - >>> print(table_out[:5]) + >>> width = Angle(15, unit='arcsec') + >>> table_out = DESILegacySurvey.query_region(coordinates, width=width, data_release=9) + >>> print(table_out[:5]) # doctest: +IGNORE_OUTPUT ls_id dec ra ... type wise_coadd_id ... ---------------- ----------------- ------------------ ... ---- ------------- @@ -56,8 +56,8 @@ we can define our region in the same way used in the example above. >>> pixels = 60 >>> >>> pos = SkyCoord(ra, dec, unit='degree') - >>> radius = Angle(radius_input, unit='arcmin') - >>> im = DESILegacySurvey.get_images(pos, pixels, radius, data_release=9) + >>> width = Angle(radius_input, unit='arcmin') + >>> im = DESILegacySurvey.get_images(pos, pixels=pixels, width=width, data_release=9) All the information we need can be found within the object "im".