diff --git a/CHANGES.rst b/CHANGES.rst index b44a3cce8c..9a24c7a18d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -107,6 +107,11 @@ gaia New Tools and Services ---------------------- +cds.hips2fits +^^^^^^^^^^^^^ + +- HIPS2fits is a service providing nice fits/jpg/png image cutouts from a HiPS + a WCS. It can now be queried from python [#1734] + esa.xmm_newton ^^^^^^^^^^^^^^ diff --git a/astroquery/hips2fits/__init__.py b/astroquery/hips2fits/__init__.py new file mode 100644 index 0000000000..92aefd7b36 --- /dev/null +++ b/astroquery/hips2fits/__init__.py @@ -0,0 +1,44 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +CDS hips2fits Query Tool +------------------------ + +:Author: Matthieu Baumann (matthieu.baumann@astro.unistra.fr) + +This package is for querying the CDS hips2fits service, primarily hosted at: + +* http://alasky.u-strasbg.fr/hips-image-services/hips2fits +* http://alaskybis.u-strasbg.fr/hips-image-services/hips2fits (mirror) + +Note: If the access to hips2fits was helpful for your research +work, the following acknowledgment would be appreciated:: + + This research has made use of the hips2fits, a tool developed at CDS, Strasbourg, France aiming at extracting + FITS images from HiPS sky maps with respect to a WCS. +""" + +from astropy import config as _config + + +class Conf(_config.ConfigNamespace): + """ + Configuration parameters for ``astroquery.template_module``. + """ + server = _config.ConfigItem( + ["http://alasky.u-strasbg.fr/hips-image-services/hips2fits", + "http://alaskybis.u-strasbg.fr/hips-image-services/hips2fits"], + 'Name of the template_module server to use.') + + timeout = _config.ConfigItem( + 30, + 'Time limit for connecting to template_module server.') + + +conf = Conf() + +from .core import hips2fits, hips2fitsClass + +__all__ = [ + 'hips2fits', 'hips2fitsClass', + 'Conf', 'conf', +] diff --git a/astroquery/hips2fits/core.py b/astroquery/hips2fits/core.py new file mode 100644 index 0000000000..6a23b5708e --- /dev/null +++ b/astroquery/hips2fits/core.py @@ -0,0 +1,406 @@ +# -*- coding: utf-8 -* + +# Licensed under a 3-clause BSD style license - see LICENSE.rst +from ..query import BaseQuery + +from ..utils.class_or_instance import class_or_instance +from ..utils import async_to_sync +from . import conf + +from astropy import wcs + +__all__ = ['hips2fits', 'hips2fitsClass'] +__doctest_skip__ = ['hips2fitsClass.*'] + +import json + +# Numpy is used for deserializing the string of bytes +# returned by hips2fits into a valid float32 numpy array +import numpy as np +# astropy.io.fits is used for creating a new HDUList +# from the image data received +from astropy.io import fits +from astropy.coordinates import Angle +import astropy.units as u + +# Used for deserializing string of bytes to an image (i.e. a numpy array) +import io +from PIL import Image + +from astropy.utils.exceptions import AstropyUserWarning +import warnings + + +@async_to_sync +class hips2fitsClass(BaseQuery): + """ + Query the `CDS hips2fits service `_ + + The `CDS hips2fits service `_ offers a way + to extract FITS images from HiPS sky maps. HiPS is an IVOA standard that combines individual images in + order to produce a progressive hierarchical sky map describing the whole survey. Please refer to the + `IVOA paper `_ for more info. + + Given an astropy user-defined WCS with an HiPS name + (see the list of valid HiPS names hosted in CDS `here `_), + hips2fits will return you the corresponding FITS image (JPG/PNG output formats are also implemented). + + This package implements two methods: + + * :meth:`~astroquery.hips2fits.hips2fitsClass.query_with_wcs` extracting a FITS image from a HiPS and an astropy ``wcs.WCS``. + See `here `_ all the valid HiPS names hosted in CDS. + * :meth:`~astroquery.hips2fits.hips2fitsClass.query` extracting a FITS image from a HiPS given the output image pixel size, the center of projection, the type of projection and the field of view. + See `here `_ all the valid HiPS names hosted in CDS. + + """ + server = conf.server + timeout = conf.timeout + + def __init__(self, *args): + super(hips2fitsClass, self).__init__() + + def query_with_wcs(self, hips, wcs, format="fits", min_cut=0.5, max_cut=99.5, stretch="linear", cmap="Greys_r", get_query_payload=False, verbose=False): + """ + Query the `CDS hips2fits service `_ with a astropy WCS. + + Parameters + ---------- + hips : str + ID or keyword identifying the HiPS to use. + If multiple HiPS surveys match, one is chosen randomly. + See the list of valid HiPS ids hosted by the CDS `here `_. + wcs : `~astropy.wcs.WCS` + An astropy WCS defining the astrometry you wish. + Alternatively, you can pass lon, lat, fov, coordsys keywords (if so, please use the + :meth:`~astroquery.hips2fits.hips2fitsClass.query_with_user_defined_wcs` method). + format : str, optional + Format of the output image. + Allowed values are fits (default), jpg and png + In case of jpg or png format, scaling of the pixels value can be controlled with parameters ``min_cut``, ``max_cut`` and ``stretch`` + min_cut : float, optional + Minimal value considered for contrast adjustment normalization. + Only applicable to jpg/png output formats. + Can be given as a percentile value, for example min_cut=1.5%. Default value is 0.5%. + max_cut : float, optional + Maximal value considered for contrast adjustment normalization. + Only applicable to jpg/png output formats. + Can be given as a percentile value, for example max_cut=97%. Default value is 99.5%. + stretch : str, optional + Stretch function used for contrast adjustment. + Only applicable to jpg/png output formats. + Possible values are: power, linear, sqrt, log, asinh. Default value is linear. + cmap : `~matplotlib.colors.Colormap` or str, optional + Name of the color map. + Only applicable to jpg/png output formats. + Any `colormap supported by Matplotlib `_ can be specified. + Default value is Greys_r (grayscale) + get_query_payload : bool, optional + If True, returns a dictionary of the query payload instead of the parsed response. + verbose : bool, optional + + Returns + ------- + response : `~astropy.io.fits.HDUList` or `~numpy.ndarray` + Returns an astropy HDUList for fits output format or a 3-dimensional numpy array + for jpg/png output formats. + + Examples + -------- + >>> from astroquery.hips2fits import hips2fits + >>> import matplotlib.pyplot as plt + >>> from matplotlib.colors import Colormap + >>> from astropy import wcs as astropy_wcs + >>> # Create a new WCS astropy object + >>> w = astropy_wcs.WCS(header={ + ... 'NAXIS1': 2000, # Width of the output fits/image + ... 'NAXIS2': 1000, # Height of the output fits/image + ... 'WCSAXES': 2, # Number of coordinate axes + ... 'CRPIX1': 1000.0, # Pixel coordinate of reference point + ... 'CRPIX2': 500.0, # Pixel coordinate of reference point + ... 'CDELT1': -0.18, # [deg] Coordinate increment at reference point + ... 'CDELT2': 0.18, # [deg] Coordinate increment at reference point + ... 'CUNIT1': 'deg', # Units of coordinate increment and value + ... 'CUNIT2': 'deg', # Units of coordinate increment and value + ... 'CTYPE1': 'GLON-MOL', # galactic longitude, Mollweide's projection + ... 'CTYPE2': 'GLAT-MOL', # galactic latitude, Mollweide's projection + ... 'CRVAL1': 0.0, # [deg] Coordinate value at reference point + ... 'CRVAL2': 0.0, # [deg] Coordinate value at reference point + ... }) + >>> hips = 'CDS/P/DSS2/red' + >>> result = hips2fits.query_with_wcs( + ... hips=hips, + ... wcs=w, + ... get_query_payload=False, + ... format='jpg', + ... min_cut=0.5, + ... max_cut=99.5, + ... cmap=Colormap('viridis'), + ... ) + >>> im = plt.imshow(result) + >>> plt.show(im) + """ + response = self.query_with_wcs_async(get_query_payload, hips=hips, wcs=wcs, format=format, min_cut=min_cut, max_cut=max_cut, stretch=stretch, cmap=cmap) + + if get_query_payload: + return response + + result = self._parse_result(response, verbose=verbose, format=format) + return result + + @class_or_instance + def query_with_wcs_async(self, get_query_payload=False, **kwargs): + request_payload = self._args_to_payload(**kwargs) + + # primarily for debug purposes, but also useful if you want to send + # someone a URL linking directly to the data + if get_query_payload: + return request_payload + + response = self._request( + method="GET", + url=self.server, + data=kwargs.get('data', None), + cache=False, + timeout=self.timeout, + params=request_payload + ) + + return response + + def query(self, hips, width, height, projection, ra, dec, fov, coordsys="icrs", rotation_angle=Angle(0 * u.deg), format="fits", min_cut=0.5, max_cut=99.5, stretch="linear", cmap="Greys_r", get_query_payload=False, verbose=False): + """ + Query the `CDS hips2fits service `_. + + If you have not any WCS, you can call this method by passing: + * The width/height size of the output pixel image + * The center of projection in world coordinates (ra, dec) + * The fov angle in world coordinates + * The rotation angle of the projection + * The name of the projection. All `astropy projections `_ are supported: + + Parameters + ---------- + hips : str + ID or keyword identifying the HiPS to use. + If multiple HiPS surveys match, one is chosen randomly. + See the list of valid HiPS ids hosted by the CDS `here `_. + width : int + Width in pixels of the output image. + height : int + Height in pixels of the output image. + projection : str + Name of the requested projection, eg: SIN, TAN, MOL, AIT, CAR, CEA, STG Compulsory if wcs is not provided. + See `this page `_ for an exhaustive list. + fov : `~astropy.coordinates.Angle` + Size (FoV) of the cutout on the sky. + This is the size of the largest dimension of the image. + ra : `~astropy.coordinates.Longitude` + Right ascension of the center of the output image. + dec : `~astropy.coordinates.Latitude` + Declination of the center of the output image. + coordsys : str, optional + Coordinate frame system to be used for the projection + Possible values are icrs or galactic. + Default value is icrs. + rotation_angle : `~astropy.coordinates.Angle`, optional + Angle value to be applied to the projection + Default value is ``Angle(0 * u.deg)`` + format : str, optional + Format of the output image. + Allowed values are fits (default), jpg and png + In case of jpg or png format, scaling of the pixels value can be controlled with parameters ``min_cut``, ``max_cut`` and ``stretch`` + min_cut : float, optional + Minimal value considered for contrast adjustment normalization. + Only applicable to jpg/png output formats. + Can be given as a percentile value, for example min_cut=1.5%. Default value is 0.5%. + max_cut : float, optional + Maximal value considered for contrast adjustment normalization. + Only applicable to jpg/png output formats. + Can be given as a percentile value, for example max_cut=97%. Default value is 99.5%. + stretch : str, optional + Stretch function used for contrast adjustment. + Only applicable to jpg/png output formats. + Possible values are: power, linear, sqrt, log, asinh. Default value is linear. + cmap : `~matplotlib.colors.Colormap` or str, optional + Name of the color map. + Only applicable to jpg/png output formats. + Any `colormap supported by Matplotlib `_ can be specified. + Default value is Greys_r (grayscale) + get_query_payload : bool, optional + If True, returns a dictionary of the query payload instead of the parsed response. + verbose : bool, optional + + Returns + ------- + response : `~astropy.io.fits.HDUList` or `~numpy.ndarray` + Returns an astropy HDUList for fits output format or a 3-dimensional numpy array + for jpg/png output formats. + + Examples + -------- + >>> from astroquery.hips2fits import hips2fits + >>> import matplotlib.pyplot as plt + >>> from matplotlib.colors import Colormap + >>> import astropy.units as u + >>> from astropy.coordinates import Longitude, Latitude, Angle + >>> hips = 'CDS/P/DSS2/red' + >>> result = hips2fits.query( + ... hips=hips, + ... width=1000, + ... height=500, + ... ra=Longitude(0 * u.deg), + ... dec=Latitude(20 * u.deg), + ... fov=Angle(80 * u.deg), + ... projection="TAN", + ... get_query_payload=False, + ... format='jpg', + ... min_cut=0.5, + ... max_cut=99.5, + ... cmap=Colormap('viridis'), + ... ) + >>> im = plt.imshow(result) + >>> plt.show(im) + """ + + response = self.query_async(get_query_payload, hips=hips, width=width, height=height, projection=projection, ra=ra, dec=dec, fov=fov, coordsys=coordsys, rotation_angle=rotation_angle, format=format, min_cut=min_cut, max_cut=max_cut, stretch=stretch, cmap=cmap) + + if get_query_payload: + return response + + result = self._parse_result(response, verbose=verbose, format=format) + return result + + @class_or_instance + def query_async(self, get_query_payload=False, **kwargs): + request_payload = self._args_to_payload(**kwargs) + + # primarily for debug purposes, but also useful if you want to send + # someone a URL linking directly to the data + if get_query_payload: + return request_payload + + response = self._request( + method="GET", + url=self.server, + data=kwargs.get('data', None), + cache=False, + timeout=self.timeout, + params=request_payload + ) + + return response + + def _parse_result(self, response, verbose, format): + # Parse the json string result + if response.status_code != 200: + # Error code status + content = response.json() + # Print a good user message based on what the + # server returned + # The server returns a dict with two items: + # - The title of the error + # - A little description of the error + msg = content['title'] + if 'description' in content.keys(): + msg += ': ' + content['description'] + '\n' + + raise AttributeError(msg) + else: + # The request succeeded + bytes_str = response.content + # Check the format. At this point, we are sure + # that the format is correct i.e. either, fits, jpg or png + if format == 'fits': + hdul = fits.HDUList.fromstring(bytes_str) + return hdul + else: + # jpg/png formats + bytes = io.BytesIO(bytes_str) + im = Image.open(bytes) + data = np.asarray(im) + return data + + def _args_to_payload(self, **kwargs): + # Convert arguments to a valid requests payload + # Build the payload + payload = {} + + # If a wcs is still present in the kwargs it means + # the user called query_with_wcs + if kwargs.get('wcs'): + wcs = kwargs.pop('wcs') + header = wcs.to_header() + + # hips2fits needs the size of the output image + if wcs.pixel_shape is not None: + nx, ny = wcs.pixel_shape + header['NAXIS1'] = nx + header['NAXIS2'] = ny + else: + # The wcs does not contain the size of the image + raise AttributeError("""The WCS passed does not contain the size of the pixel image. + Please add it to the WCS or refer to the query method.""") + + # Add the WCS to the payload + header_json = dict(header.items()) + header_str = json.dumps(header_json) + payload.update({'wcs': header_str}) + else: + # The user called query + payload.update({ + 'width': kwargs.pop("width"), + 'height': kwargs.pop("height"), + 'projection': kwargs.pop("projection"), + 'coordsys': kwargs.pop("coordsys"), + 'rotation_angle': kwargs.pop("rotation_angle").to_value(u.deg), + 'ra': kwargs.pop("ra").to_value(u.deg), + 'dec': kwargs.pop("dec").to_value(u.deg), + 'fov': kwargs.pop("fov").to_value(u.deg), + }) + + # Min-cut + min_cut_value = kwargs.pop('min_cut') + min_cut_kw = str(min_cut_value) + '%' + payload.update({'min_cut': min_cut_kw}) + + # Max-cut + max_cut_value = kwargs.pop('max_cut') + max_cut_kw = str(max_cut_value) + '%' + payload.update({'max_cut': max_cut_kw}) + + # Colormap + cmap = kwargs.pop('cmap') + from matplotlib.colors import Colormap + if isinstance(cmap, Colormap): + cmap = cmap.name + payload.update({'cmap': cmap}) + + # Stretch + stretch = kwargs.pop('stretch') + # TODO remove that: it must be handled properly by the server side + # that should return a json error + if stretch not in ('power', 'linear', 'sqrt', 'log', 'asinh'): + msg = "stretch: must either 'power', 'linear', 'sqrt', 'log' or 'asinh'.\n" + msg += str(stretch) + ' is ignored.' + + warnings.warn(msg, AstropyUserWarning) + else: + payload.update({'stretch': stretch}) + payload.update({'stretch': stretch}) + + # HiPS + hips = kwargs.pop("hips") + # Output format + format = kwargs.pop("format") + payload.update({ + "hips": hips, + "format": format, + }) + + # Check that all the arguments have been handled + assert kwargs == {} + + return payload + + +hips2fits = hips2fitsClass() diff --git a/astroquery/hips2fits/tests/__init__.py b/astroquery/hips2fits/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/astroquery/hips2fits/tests/test_hips2fits.py b/astroquery/hips2fits/tests/test_hips2fits.py new file mode 100644 index 0000000000..356bea4e12 --- /dev/null +++ b/astroquery/hips2fits/tests/test_hips2fits.py @@ -0,0 +1,68 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +import sys +import pytest + +from ..core import hips2fits + +from astropy import wcs as astropy_wcs +from astropy.io import fits +import numpy as np + +from astropy.utils.exceptions import AstropyUserWarning +from matplotlib.colors import Colormap + +from astropy.coordinates import Angle, Longitude, Latitude +import astropy.units as u + + +class TestHips2fitsRemote(object): + + # Create a new WCS astropy object + w = astropy_wcs.WCS(header={ + 'NAXIS1': 2000, # Width of the output fits/image + 'NAXIS2': 1000, # Height of the output fits/image + 'WCSAXES': 2, # Number of coordinate axes + 'CRPIX1': 1000.0, # Pixel coordinate of reference point + 'CRPIX2': 500.0, # Pixel coordinate of reference point + 'CDELT1': -0.18, # [deg] Coordinate increment at reference point + 'CDELT2': 0.18, # [deg] Coordinate increment at reference point + 'CUNIT1': 'deg', # Units of coordinate increment and value + 'CUNIT2': 'deg', # Units of coordinate increment and value + 'CTYPE1': 'GLON-MOL', # galactic longitude, Mollweide's projection + 'CTYPE2': 'GLAT-MOL', # galactic latitude, Mollweide's projection + 'CRVAL1': 0.0, # [deg] Coordinate value at reference point + 'CRVAL2': 0.0, # [deg] Coordinate value at reference point + }) + hips = 'CDS/P/DSS2/red' + + def test_query_jpg(self): + result = hips2fits.query_with_wcs( + hips=self.hips, + wcs=self.w, + get_query_payload=True, + format='jpg', + min_cut=0.5, + max_cut=99.5, + cmap=Colormap('viridis'), + ) + + # We must get a numpy array with 3 dimensions, and the last one should be of size 3 (RGB) + assert result["format"] == 'jpg' and result["hips"] == "CDS/P/DSS2/red" + + def test_query_no_wcs_fits(self): + result = hips2fits.query( + hips=self.hips, + get_query_payload=True, + width=1000, + height=500, + fov=Angle(20 * u.deg), + ra=Longitude(0 * u.deg), + dec=Latitude(20 * u.deg), + projection="TAN", + min_cut=0.5, + max_cut=99.5, + cmap=Colormap('viridis'), + ) + + # We must get a numpy array with 3 dimensions, and the last one should be of size 3 (RGB) + assert result["format"] == 'fits' and result["hips"] == "CDS/P/DSS2/red" diff --git a/astroquery/hips2fits/tests/test_hips2fits_remote.py b/astroquery/hips2fits/tests/test_hips2fits_remote.py new file mode 100644 index 0000000000..5f1bf8c6e9 --- /dev/null +++ b/astroquery/hips2fits/tests/test_hips2fits_remote.py @@ -0,0 +1,165 @@ +# -*- coding: utf-8 -* + +# Licensed under a 3-clause BSD style license - see LICENSE.rst +import sys +import pytest + +from ..core import hips2fits + +from astropy import wcs as astropy_wcs +from astropy.io import fits +import numpy as np + +from astropy.utils.exceptions import AstropyUserWarning +from matplotlib.colors import Colormap + + +@pytest.mark.remote_data +class TestHips2fitsRemote(object): + + # Create a new WCS astropy object + w = astropy_wcs.WCS(header={ + 'NAXIS1': 2000, # Width of the output fits/image + 'NAXIS2': 1000, # Height of the output fits/image + 'WCSAXES': 2, # Number of coordinate axes + 'CRPIX1': 1000.0, # Pixel coordinate of reference point + 'CRPIX2': 500.0, # Pixel coordinate of reference point + 'CDELT1': -0.18, # [deg] Coordinate increment at reference point + 'CDELT2': 0.18, # [deg] Coordinate increment at reference point + 'CUNIT1': 'deg', # Units of coordinate increment and value + 'CUNIT2': 'deg', # Units of coordinate increment and value + 'CTYPE1': 'GLON-MOL', # galactic longitude, Mollweide's projection + 'CTYPE2': 'GLAT-MOL', # galactic latitude, Mollweide's projection + 'CRVAL1': 0.0, # [deg] Coordinate value at reference point + 'CRVAL2': 0.0, # [deg] Coordinate value at reference point + }) + hips = 'CDS/P/DSS2/red' + + def test_query_jpg(self): + result = hips2fits.query_with_wcs( + hips=self.hips, + wcs=self.w, + get_query_payload=False, + format='jpg', + min_cut=0.5, + max_cut=99.5, + cmap=Colormap('viridis'), + ) + + # import matplotlib.cm as cm + # import matplotlib.pyplot as plt + + # im = plt.imshow(result) + # plt.show(im) + + # We must get a numpy array with 3 dimensions, and the last one should be of size 3 (RGB) + assert isinstance(result, np.ndarray) and result.shape[2] == 3 + + def test_query_jpg_no_wcs(self): + from astropy.coordinates import Longitude + from astropy.coordinates import Latitude + from astropy.coordinates import Angle + import astropy.units as u + + result = hips2fits.query( + hips=self.hips, + width=1000, + height=500, + fov=Angle(20 * u.deg), + ra=Longitude(0 * u.deg), + dec=Latitude(20 * u.deg), + projection="TAN", + get_query_payload=False, + format='jpg', + min_cut=0.5, + max_cut=99.5, + cmap=Colormap('viridis'), + ) + + import matplotlib.cm as cm + import matplotlib.pyplot as plt + + im = plt.imshow(result) + plt.show(im) + + # We must get a numpy array with 3 dimensions, and the last one should be of size 3 (RGB) + assert isinstance(result, np.ndarray) and result.shape[2] == 3 + + def test_bad_strech(self): + with pytest.raises(AttributeError): + result = hips2fits.query_with_wcs( + hips=self.hips, + wcs=self.w, + get_query_payload=False, + format='jpg', + stretch="azs", + min_cut=0.5, + max_cut=99.5, + cmap=Colormap('viridis'), + ) + + def test_query_fits(self): + result = hips2fits.query_with_wcs( + hips=self.hips, + wcs=self.w, + get_query_payload=False, + ) + + assert isinstance(result, fits.HDUList) + + def test_query_fits(self): + result = hips2fits.query_with_wcs( + hips=self.hips, + wcs=self.w, + # Here we send additional keywords incompatible with + # the fits output format + min_cut=1.5, + max_cut=96, + stretch='asinh', + cmap='twilight', + ) + + def test_query_png(self): + result = hips2fits.query_with_wcs( + hips=self.hips, + wcs=self.w, + get_query_payload=False, + format='png' + ) + # We must get a numpy array with 3 dimensions, and the last one should be of size 4 (RGBA) + assert isinstance(result, np.ndarray) and result.shape[2] == 4 + + def test_bad_format_asked(self): + with pytest.raises(AttributeError): + result = hips2fits.query_with_wcs( + hips=self.hips, + wcs=self.w, + get_query_payload=False, + format='sdfsg' + ) + + def test_wcs_having_no_naxis(self): + w2 = astropy_wcs.WCS(header={ + 'WCSAXES': 2, # Number of coordinate axes + 'CRPIX1': 1000.0, # Pixel coordinate of reference point + 'CRPIX2': 500.0, # Pixel coordinate of reference point + 'CDELT1': -0.18, # [deg] Coordinate increment at reference point + 'CDELT2': 0.18, # [deg] Coordinate increment at reference point + 'CUNIT1': 'deg', # Units of coordinate increment and value + 'CUNIT2': 'deg', # Units of coordinate increment and value + 'CTYPE1': 'GLON-MOL', # galactic longitude, Mollweide's projection + 'CTYPE2': 'GLAT-MOL', # galactic latitude, Mollweide's projection + 'CRVAL1': 0.0, # [deg] Coordinate value at reference point + 'CRVAL2': 0.0, # [deg] Coordinate value at reference point + }) + + with pytest.raises(AttributeError): + result = hips2fits.query_with_wcs( + hips=self.hips, + wcs=w2, + get_query_payload=False, + format='jpg', + min_cut=0.5, + max_cut=99.5, + cmap=Colormap('viridis'), + ) diff --git a/docs/hips2fits/hips2fits.rst b/docs/hips2fits/hips2fits.rst new file mode 100644 index 0000000000..342846f333 --- /dev/null +++ b/docs/hips2fits/hips2fits.rst @@ -0,0 +1,113 @@ +.. doctest-skip-all + +.. _astroquery.hips2fits: + +****************************************** +HiPS2fits Service (`astroquery.hips2fits`) +****************************************** + +Getting started +=============== + +Query the `CDS hips2fits service `_ + +The `CDS hips2fits service `_ offers a way +to extract FITS images from HiPS sky maps. HiPS is an IVOA standard that combines individual images in +order to produce a progressive hierarchical sky map describing the whole survey. Please refer to the +`IVOA paper `_ for more info. + +Given an astropy user-defined WCS with a HiPS name, +hips2fits will return you the corresponding FITS image (JPG/PNG output formats are also implemented). + +See the list of valid HiPS names hosted in CDS `here `_. It is also +possible to use :meth:`astroquery.cds.CdsClass.find_datasets` to retrieve the list of HiPSes matching an expression +(e.g. \*HST\* will return the names of the HST surveys HiPSes). + +This package implements two methods: + +* :meth:`~astroquery.hips2fits.hips2fitsClass.query_with_wcs` extracting a FITS image from a HiPS and an astropy ``wcs.WCS``. + See `here `_ all the valid HiPS names hosted in CDS. +* :meth:`~astroquery.hips2fits.hips2fitsClass.query` extracting a FITS image from a HiPS given the output image pixel size, the center of projection, the type of projection and the field of view. + See `here `_ all the valid HiPS names hosted in CDS. + + +Examples +======== + +With a user defined astropy WCS +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + >>> from astroquery.hips2fits import hips2fits + >>> import matplotlib.pyplot as plt + >>> from matplotlib.colors import Colormap + >>> from astropy import wcs as astropy_wcs + >>> # Create a new WCS astropy object + >>> w = astropy_wcs.WCS(header={ + ... 'NAXIS1': 2000, # Width of the output fits/image + ... 'NAXIS2': 1000, # Height of the output fits/image + ... 'WCSAXES': 2, # Number of coordinate axes + ... 'CRPIX1': 1000.0, # Pixel coordinate of reference point + ... 'CRPIX2': 500.0, # Pixel coordinate of reference point + ... 'CDELT1': -0.18, # [deg] Coordinate increment at reference point + ... 'CDELT2': 0.18, # [deg] Coordinate increment at reference point + ... 'CUNIT1': 'deg', # Units of coordinate increment and value + ... 'CUNIT2': 'deg', # Units of coordinate increment and value + ... 'CTYPE1': 'GLON-MOL', # galactic longitude, Mollweide's projection + ... 'CTYPE2': 'GLAT-MOL', # galactic latitude, Mollweide's projection + ... 'CRVAL1': 0.0, # [deg] Coordinate value at reference point + ... 'CRVAL2': 0.0, # [deg] Coordinate value at reference point + ... }) + >>> hips = 'CDS/P/DSS2/red' + >>> result = hips2fits.query_with_wcs( + ... hips=hips, + ... wcs=w, + ... get_query_payload=False, + ... format='jpg', + ... min_cut=0.5, + ... max_cut=99.5, + ... cmap=Colormap('viridis'), + ... ) + >>> im = plt.imshow(result) + >>> plt.show(im) + +.. image:: ./query_wcs.png + +Without WCS +~~~~~~~~~~~ + + >>> from astroquery.hips2fits import hips2fits + >>> import matplotlib.pyplot as plt + >>> from matplotlib.colors import Colormap + >>> import astropy.units as u + >>> from astropy.coordinates import Longitude, Latitude, Angle + >>> hips = 'CDS/P/DSS2/red' + >>> result = hips2fits.query( + ... hips=hips, + ... width=1000, + ... height=500, + ... ra=Longitude(0 * u.deg), + ... dec=Latitude(20 * u.deg), + ... fov=Angle(80 * u.deg), + ... projection="AIT", + ... get_query_payload=False, + ... format='jpg', + ... min_cut=0.5, + ... max_cut=99.5, + ... cmap=Colormap('viridis'), + ... ) + >>> im = plt.imshow(result) + >>> plt.show(im) + +.. image:: ./query_no_wcs.png + +Reference/API +============= + +.. automodapi:: astroquery.hips2fits + :no-inheritance-diagram: + + +.. _hips2fits: http://alasky.u-strasbg.fr/hips-image-services/hips2fits + diff --git a/docs/hips2fits/query_no_wcs.png b/docs/hips2fits/query_no_wcs.png new file mode 100644 index 0000000000..90cc14455c Binary files /dev/null and b/docs/hips2fits/query_no_wcs.png differ diff --git a/docs/hips2fits/query_wcs.png b/docs/hips2fits/query_wcs.png new file mode 100644 index 0000000000..591ab618e2 Binary files /dev/null and b/docs/hips2fits/query_wcs.png differ diff --git a/docs/index.rst b/docs/index.rst index 74ced88434..3c7292cbf3 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -171,6 +171,7 @@ The following modules have been completed using a common API: gama/gama.rst gemini/gemini.rst heasarc/heasarc.rst + hips2fits/hips2fits.rst hitran/hitran.rst ibe/ibe.rst irsa/irsa.rst