From 0789a51fe1a1571eeff24b98f46812bb6ff11ffa Mon Sep 17 00:00:00 2001 From: lmichel Date: Tue, 25 Nov 2025 10:06:36 +0100 Subject: [PATCH 1/4] fix a bug that made the readout of the coords:SpaceFrame.equinox failing --- pyvo/mivot/features/sky_coord_builder.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pyvo/mivot/features/sky_coord_builder.py b/pyvo/mivot/features/sky_coord_builder.py index 97702635..2218795f 100644 --- a/pyvo/mivot/features/sky_coord_builder.py +++ b/pyvo/mivot/features/sky_coord_builder.py @@ -89,10 +89,14 @@ def _get_time_instance(self, hk_field, besselian=False): ----- MappingError: if the Time instance cannot be built for some reason """ - # Process complex type "mango:DateTime + # Process complex type "mango:DateTime" if hk_field['dmtype'] == "mango:DateTime": representation = hk_field['representation']['value'] timestamp = hk_field['dateTime']['value'] + # Process complex type "coords:epoch" used for the space frame equinox + elif hk_field['dmtype'] == "coords:Epoch": + representation = 'yr' if "unit" not in hk_field else hk_field.get("unit") + timestamp = hk_field['value'] # Process simple attribute else: representation = hk_field.get("unit") @@ -104,7 +108,8 @@ def _get_time_instance(self, hk_field, besselian=False): time_instance = self. _build_time_instance(timestamp, representation, besselian) if not time_instance: - raise MappingError(f"Cannot build a Time instance from {hk_field}") + mode = "besselian" if besselian else "julian" + raise MappingError(f"Cannot build a Time instance from {hk_field} ({mode} date)") return time_instance @@ -175,7 +180,6 @@ def _get_space_frame(self): coo_sys = self._mivot_instance_dict["spaceSys"]["frame"] equinox = None frame = coo_sys["spaceRefFrame"]["value"].lower() - if frame == 'fk4': self._map_coord_names = SkyCoordMapping.default_params if "equinox" in coo_sys: From 0d550807d20a978d4d4177f6b2fe2a44666223a2 Mon Sep 17 00:00:00 2001 From: lmichel Date: Tue, 25 Nov 2025 10:49:47 +0100 Subject: [PATCH 2/4] use a common function checking the SkyCoord field, extend the test cases a little bit --- pyvo/mivot/tests/test_sky_coord_builder.py | 82 +++++++++++++--------- 1 file changed, 49 insertions(+), 33 deletions(-) diff --git a/pyvo/mivot/tests/test_sky_coord_builder.py b/pyvo/mivot/tests/test_sky_coord_builder.py index 11ef86c7..f9bf98bd 100644 --- a/pyvo/mivot/tests/test_sky_coord_builder.py +++ b/pyvo/mivot/tests/test_sky_coord_builder.py @@ -9,11 +9,10 @@ import pytest from copy import deepcopy from astropy.utils.data import get_pkg_data_filename -from astropy import units as u from pyvo.mivot.version_checker import check_astropy_version from pyvo.mivot.viewer.mivot_instance import MivotInstance from pyvo.mivot.features.sky_coord_builder import SkyCoordBuilder -from pyvo.mivot.utils.exceptions import NoMatchingDMTypeError +from pyvo.mivot.utils.exceptions import NoMatchingDMTypeError, MappingError from pyvo.mivot.viewer.mivot_viewer import MivotViewer from pyvo.utils import activate_features @@ -118,9 +117,8 @@ "value": "FK5" }, "equinox": { - "dmtype": "coords:SpaceFrame.equinox", + "dmtype": "coords:Epoch", "value": "2012", - "unit": "yr", } } }, @@ -176,7 +174,7 @@ "ref": None, }, "equinox": { - "dmtype": "coords:SpaceFrame.equinox", + "dmtype": "coords:Epoch", "value": "2012", "unit": "yr", }, @@ -184,6 +182,27 @@ } +def check_skycoo(scoo, ra, dec, distance, pm_ra_cosdec, pm_dec, obstime): + """ + Check the SkyCoord instance against the constant values given as parameters + """ + try: + assert scoo.ra.degree == pytest.approx(ra) + assert scoo.dec.degree == pytest.approx(dec) + if distance: + assert scoo.distance.pc == pytest.approx(distance) + if pm_ra_cosdec: + assert scoo.pm_ra_cosdec.value == pytest.approx(pm_ra_cosdec) + if pm_dec: + assert scoo.pm_dec.value == pytest.approx(pm_dec) + except AttributeError: + assert scoo.galactic.l.degree == pytest.approx(ra) + assert scoo.galactic.b.degree == pytest.approx(dec) + + if obstime: + assert str(scoo.obstime) == obstime + + def test_no_matching_mapping(): """ Test that a NoMatchingDMTypeError is raised not mapped on mango:EpochPosition @@ -201,13 +220,9 @@ def test_vizier_output(): mivot_instance = MivotInstance(**vizier_dict) scb = SkyCoordBuilder(mivot_instance) scoo = scb.build_sky_coord() - assert (str(scoo).replace("\n", "").replace(" ", "") - == "") - scoo = mivot_instance.get_SkyCoord() - assert (str(scoo).replace("\n", "").replace(" ", "") - == "") + check_skycoo(scoo, 52.26722684, 59.94033461, None, + -0.82, -1.85, + None) vizier_dict["spaceSys"]["frame"]["spaceRefFrame"]["value"] = "Galactic" mivot_instance = MivotInstance(**vizier_dict) @@ -215,13 +230,16 @@ def test_vizier_output(): assert (str(scoo).replace("\n", "").replace(" ", "") == "") + check_skycoo(scoo, 52.26722684, 59.94033461, None, + -0.82, -1.85, + None) vizier_dict["spaceSys"]["frame"]["spaceRefFrame"]["value"] = "QWERTY" mivot_instance = MivotInstance(**vizier_dict) scoo = mivot_instance.get_SkyCoord() - assert (str(scoo).replace("\n", "").replace(" ", "") - == "") + check_skycoo(scoo, 52.26722684, 59.94033461, None, + -0.82, -1.85, + "J1991.250") @pytest.mark.skipif(not check_astropy_version(), reason="need astropy 6+") @@ -232,19 +250,24 @@ def test_vizier_output_with_equinox_and_parallax(): mivot_instance = MivotInstance(**vizier_equin_dict) scb = SkyCoordBuilder(mivot_instance) scoo = scb.build_sky_coord() - assert (str(scoo).replace("\n", "").replace(" ", "") - == "") + check_skycoo(scoo, 52.26722684, 59.94033461, 1666.66666667, + -0.82, -1.85, + "J1991.250") mydict = deepcopy(vizier_equin_dict) mydict["spaceSys"]["frame"]["spaceRefFrame"]["value"] = "FK4" mivot_instance = MivotInstance(**mydict) scoo = mivot_instance.get_SkyCoord() - assert (str(scoo).replace("\n", "").replace(" ", "") - == "") + check_skycoo(scoo, 52.26722684, 59.94033461, 1666.66666667, + -0.82, -1.85, + "B1991.250") + + mydict = deepcopy(vizier_equin_dict) + mydict["spaceSys"]["frame"]["spaceRefFrame"]["value"] = "FK4" + mydict["spaceSys"]["frame"]["equinox"]["value"] = "J2012" + with pytest.raises(MappingError, match=r".*besselian date.*"): + mivot_instance = MivotInstance(**mydict) + scoo = mivot_instance.get_SkyCoord() @pytest.mark.skipif(not check_astropy_version(), reason="need astropy 6+") @@ -257,16 +280,9 @@ def test_simad_cs_output(): scb = SkyCoordBuilder(mivot_instance) scoo = scb.build_sky_coord() - assert scoo.ra.degree == pytest.approx(269.45207696) - assert scoo.dec.degree == pytest.approx(4.69336497) - assert scoo.distance.pc == pytest.approx(1.82823411) - x = scoo.pm_ra_cosdec.value - y = (-801.551 * u.mas/u.yr).value - assert x == pytest.approx(y) - x = scoo.pm_dec.value - y = (10362.394 * u.mas/u.yr).value - assert x == pytest.approx(y) - assert str(scoo.obstime) == "J2000.000" + check_skycoo(scoo, 269.45207696, 4.69336497, 1.82823411, + -801.551, 10362.394, + "J2000.000") def test_time_representation(): From b15f5e62946c54e340945a6c8cbb310fa2684ed6 Mon Sep 17 00:00:00 2001 From: lmichel Date: Tue, 25 Nov 2025 10:50:41 +0100 Subject: [PATCH 3/4] Example based on the last version of Vizier hipparcos --- docs/mivot/viewer.rst | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/docs/mivot/viewer.rst b/docs/mivot/viewer.rst index 688195a1..47c92b85 100644 --- a/docs/mivot/viewer.rst +++ b/docs/mivot/viewer.rst @@ -50,12 +50,13 @@ or a ``DALResults`` instance. .. doctest-skip:: >>> import astropy.units as u + >>> from astropy.io.votable import parse >>> from astropy.coordinates import SkyCoord >>> from pyvo.dal.scs import SCSService >>> from pyvo.utils.prototype import activate_features >>> from pyvo.mivot.viewer.mivot_viewer import MivotViewer >>> - >>> scs_srv = SCSService("https://vizier.cds.unistra.fr/viz-bin/conesearch/V1.5/I/239/hip_main") + >>> scs_srv = SCSService("https://vizier.cds.unistra.fr/viz-bin/conesearch/V1.5/I/311/hip2") >>> m_viewer = MivotViewer( ... scs_srv.search( ... pos=SkyCoord(ra=52.26708 * u.degree, dec=59.94027 * u.degree, @@ -95,13 +96,14 @@ and that the data rows can be interpreted as instances of the ``mango:EpochPosit .. doctest-skip:: >>> print(mivot_instance.spaceSys.frame.spaceRefFrame.value) - ICRS + FK5 .. doctest-skip:: >>> while m_viewer.next_row_view(): >>> print(f"position: {mivot_instance.latitude.value} {mivot_instance.longitude.value}") - position: 59.94033461 52.26722684 + position: 59.90665631 52.12106214 + position: 59.94033468 52.26722736 .... .. important:: @@ -125,19 +127,22 @@ We can also provide a complete instance representation that includes all fields >>> print(repr(globals_instance)) { - "dmtype": "coords:SpaceSys", - "dmid": "SpaceFrame_ICRS", - "frame": { + "dmtype": "coords:SpaceSys", + "dmid": "SpaceFrame_ICRS", + "frame": { "dmrole": "coords:PhysicalCoordSys.frame", "dmtype": "coords:SpaceFrame", "spaceRefFrame": { - "dmtype": "ivoa:string", - "value": "ICRS" - } - } + "dmtype": "ivoa:string", + "value": "FK5" + }, + "equinox": { + "dmtype": "coords:Epoch", + "value": "J2000" + } + } } - As you can see from the previous examples, model leaves (class attributes) are complex types. This is because they contain additional metadata as well as values: From 697121444828150c1f1eb71589ce1409b31e3aaa Mon Sep 17 00:00:00 2001 From: lmichel Date: Tue, 25 Nov 2025 11:12:48 +0100 Subject: [PATCH 4/4] change log update --- CHANGES.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 06e81a8d..bcaf5c53 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -17,7 +17,8 @@ Deprecations and Removals Bug Fixes --------- - +- Fix a bug in the space frame equinox processing. + Update of the Hipparcos catalogue (I/311/hip2) used in the viewer doc [710] 1.8 (2025-11-13)