Skip to content

Commit

Permalink
Merge pull request #148 from cds-astro/feat-user-vizier-moc-microservice
Browse files Browse the repository at this point in the history
Switch to use VizieR microservice instead of the MOCServer in from_vizier_table
  • Loading branch information
ManonMarchand authored Jul 4, 2024
2 parents 9dedb2d + ae7bbea commit c635418
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 29 deletions.
1 change: 1 addition & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ jobs:
# See https://github.com/pypa/manylinux for this particular container:
# * CPython 3.8, 3.9, 3.10, 3.11 and 3.12 installed in /opt/python/<python tag>-<abi tag>
container: quay.io/pypa/manylinux2014_x86_64:latest
env: {ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true} # Allow using Node16 actions required for CentOS7
steps:
- name: "Checkout the full project"
uses: actions/checkout@v3
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ jobs:
# See https://github.com/pypa/manylinux for this particular container:
# * CPython 3.7, 3.8, 3.9, 3.10 and 3.11 installed in /opt/python/<python tag>-<abi tag>
container: quay.io/pypa/manylinux2014_x86_64
env: {ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true} # Allow using Node16 actions required for CentOS7
steps:
- name: "Checkout branch ${{ github.head_ref }}"
uses: actions/checkout@v3
Expand Down
18 changes: 14 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [unreleased]

### Added

* the `mocpy.WCS` class can now accept a sequence of angles as its fov argument rather than always
representing square areas of the sky.
* `MOC.from_polygons` and `MOC.from_polygons` now accept a boolean `complement` that allows to chose
between the small MOC described by the polygon or the bigger one (its complement)

### Changed

* `MOC.from_vizier_table()` does not call the MOCServer anymore. It now raises an error if the
catalog of table name is invalid (see #143). It also accepts `max_depth` as an argument. This
should replace `nside` in a future version.
* `MOC.from_ivorn()` now accepts `max_depth` as an argument. This should reb=place `nside` later.

## [0.15.0]

### Added

* a new method `MOC.from_cones` allows to create a list of MOCs from a list of centers
and radii. This is multi-threaded.
* new method `MOC.from_boxes` allows to create lists of MOCs from boxes. This is multi-threaded.
* the `mocpy.WCS` class can now accept a sequence of angles as its fov argument rather than always
representing square areas of the sky.
* `MOC.from_polygons` and `MOC.from_polygons` now accept a boolean `complement` that allows to chose
between the small MOC described by the polygon or the bigger one (its complement)

### Changed

Expand Down
141 changes: 116 additions & 25 deletions python/mocpy/moc/moc.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import contextlib
import functools
from io import BytesIO
from math import log2
from pathlib import Path
from urllib.error import HTTPError
from urllib.parse import urlencode

import numpy as np
Expand Down Expand Up @@ -542,57 +544,146 @@ def from_fits_images(cls, path_l, max_norder, hdu_index=0):
return moc

@classmethod
def from_vizier_table(cls, table_id, nside=256):
"""
Create a `~mocpy.moc.MOC` object from a VizieR table.
**Info**: This method is already implemented in `astroquery.cds <https://astroquery.readthedocs.io/en/latest/cds/cds.html>`__. You can ask to get a `mocpy.moc.MOC` object
from a vizier catalog ID.
def from_vizier_table(cls, table_id, max_depth=None, nside=None):
"""Create a `~mocpy.moc.MOC` object from a VizieR table or catalog.
Parameters
----------
table_id : str
table index
nside : int, optional
256 by default
Table or catalog identifier
max_depth : int, optional
The depth at which the MOC should be retrieved. The default (which is also the
most precise available on the server) is order 11 for tables and order 10 for
catalogs.
nside : int, optional and deprecated
It is deprecated in favor of max_depth since version 0.6.0
You can switch to maw_depth by calculating max_depht = log2(nside).
Returns
-------
result : `~mocpy.moc.MOC`
The resulting MOC.
Examples
--------
>>> from mocpy import MOC
>>> moc = MOC.from_vizier_table("J/A+A/675/A154/tableb1") # download the MOC
>>> round(moc.sky_fraction, 6) # let's print the sky fraction of the MOC
4e-06
Notes
-----
VizieR is organized by catalogs that correspond to published articles or to data
releases. These catalogs contain one or more tables.
Here are two webpages where you can read the
`list of catalogs https://cdsarc.cds.unistra.fr/viz-bin/moc/?format=html`_
and the `list of tables https://cdsarc.cds.unistra.fr/viz-bin/moc/?format=html&list=tables`_
currently available.
"""
nside_possible_values = (8, 16, 32, 64, 128, 256, 512)
if nside not in nside_possible_values:
raise ValueError(
f"Bad value for nside. Must be in {nside_possible_values}",
if nside:
import warnings

warnings.warn(
"'nside' is deprecated in favor of 'max_depth'. We use the nside"
"value for this request. You can switch to max_depth with "
"max_depth = log2(nside).",
DeprecationWarning,
stacklevel=2,
)
return cls.from_ivorn("ivo://CDS/" + table_id, nside)
nside_possible_values = (8, 16, 32, 64, 128, 256, 512)
if nside not in nside_possible_values:
raise ValueError(
f"Bad value for nside. Must be in {nside_possible_values}",
)
max_depth = log2(nside)

MOC_SERVER_ROOT_URL = "http://alasky.unistra.fr/MocServer/query"
url = f"https://cdsarc.cds.unistra.fr/viz-bin/moc/{table_id}"

if max_depth:
url += f"?order={int(max_depth)}"

try:
moc = cls.from_url(url)
except HTTPError as error:
if error.code == 400:
# we provide a clearer error for code 400 bad request
raise ValueError(
f"No catalog/table was found for '{table_id}'. You can see the list of "
"catalogs at https://cdsarc.cds.unistra.fr/viz-bin/moc/?format=html "
"and the list of tables at "
"https://cdsarc.cds.unistra.fr/viz-bin/moc/?format=html&list=tables .",
) from None
raise error
return moc

@classmethod
def from_ivorn(cls, ivorn, nside=256):
"""
Create a `~mocpy.moc.MOC` object from a given ivorn.
def from_ivorn(cls, ivorn, max_depth: int = 8, nside=None):
"""Create a `~mocpy.moc.MOC` object from a given IVORN.
IVORNs are standardized unique identifiers used within the virtual observatory.
This method queries the MOCServer, a CDS service that can also be found through
its webpages https://alasky.cds.unistra.fr/MocServer/query
Parameters
----------
ivorn : str
nside : int, optional
256 by default
A valid Virtual Observatory IVORN
max_depth : int, defaults to 8
The depth at which the MOC should be retrieved.
nside : int, optional and deprecated
It is deprecated in favor of max_depth since version 0.6.0
Returns
-------
result : `~mocpy.moc.MOC`
The resulting MOC.
Examples
--------
>>> from mocpy import MOC
>>> MOC.from_ivorn("ivo://CDS/J/A+AS/133/387/table5")
7/96462 96481 96484-96486
8/385839 385852 385854-385855 385933 385948-385950 385969 385984 385986
Notes
-----
This is a rudimentary way to retrieve MOCs from the MOCServer. For a more
complete implementation, see the MOCServer module in the astroquery library.
"""
return cls.from_url(
"{}?{}".format(
MOC.MOC_SERVER_ROOT_URL,
urlencode({"ivorn": ivorn, "get": "moc", "order": int(np.log2(nside))}),
),
if nside:
import warnings

warnings.warn(
"'nside' is deprecated in favor of 'max_depth'. We use the nside"
"value for this request. You can switch to max_depth with "
"max_depth = log2(nside).",
DeprecationWarning,
stacklevel=2,
)
nside_possible_values = (8, 16, 32, 64, 128, 256, 512)
if nside not in nside_possible_values:
raise ValueError(
f"Bad value for nside. Must be in {nside_possible_values}",
)
max_depth = log2(nside)

moc = cls.from_url(
"http://alasky.unistra.fr/MocServer/query?"
+ urlencode({"ivorn": ivorn, "get": "moc", "order": int(max_depth)}),
)

if moc.empty():
import warnings

warnings.warn(
"This MOC is empty. Possible causes are that this IVORN has no "
"positions or this is not a valid IVORN.",
UserWarning,
stacklevel=2,
)
return moc

@classmethod
def from_url(cls, url):
"""
Expand Down
35 changes: 35 additions & 0 deletions python/mocpy/tests/test_moc.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,41 @@ def test_from_polygons():
assert list_mocs == list_mocs_no_skycoord


def test_from_vizier():
# deprecated nside should still work (nside=8 means order=3)
with pytest.warns(
DeprecationWarning,
match="'nside' is deprecated in favor of 'max_depth'.*",
):
moc = MOC.from_vizier_table("I/355", nside=8)
assert moc.max_order == 3
# gaia is the whole sky at order 3
assert moc.sky_fraction == 1
with pytest.warns(
DeprecationWarning,
match="'nside' is deprecated in favor of 'max_depth'.*",
), pytest.raises(ValueError, match="Bad value for nside.*"):
moc = MOC.from_vizier_table("I/355", nside=1)
moc = MOC.from_vizier_table("I/355")
# default order is 10 for catalogs
assert moc.max_order == 10
# non valid table or catalog
with pytest.raises(ValueError, match="No catalog/table was found for 'abc'*"):
moc = MOC.from_vizier_table("abc")


def test_from_ivorn():
with pytest.warns(
DeprecationWarning,
match="'nside' is deprecated in favor of 'max_depth'.*",
):
moc = MOC.from_ivorn("ivo://CDS/J/A+AS/133/387/table5", nside=8)
assert moc.max_order == 3

with pytest.warns(UserWarning, match="This MOC is empty.*"):
MOC.from_ivorn("abc")


def test_moc_from_fits():
fits_path = "resources/P-GALEXGR6-AIS-FUV.fits"
MOC.load(fits_path, "fits")
Expand Down

0 comments on commit c635418

Please sign in to comment.