Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature skycoord #591

Merged
merged 15 commits into from
Oct 1, 2024
Merged

Feature skycoord #591

merged 15 commits into from
Oct 1, 2024

Conversation

lmichel
Copy link
Contributor

@lmichel lmichel commented Sep 4, 2024

Extension of the MIVOT feature answering #523.

If the MIVOT annotation block contains a valid instance of the mango:EpochPosition class, the dynamic object describing the mapped data can now generate a valid SkyCoord instance.

This feature makes quite easier the epoch propagation computation with the Astropy library.

from pyvo.mivot import MivotViewer
m_viewer = MivotViewer(path_to_votable)
mivot_instance = m_viewer.dm_instance
print(mivot_instance.get_SkyCoord())
<SkyCoord (ICRS): (ra, dec) in deg(52.26722684, 59.94033461)
          (pm_ra_cosdec, pm_dec) in mas / yr(-0.82, -1.85)>

Although not a standard at the time of writing, the class structure supported by the implementation must match the figure in the documentation:

EpochPosition

The logic relies only on the vodml-id it finds in the annotation, without regard to the model structure; it can be easily extended to components of other models such as measurement.

  • The space frame implementation supports FK4/5 (with equinox), ICRS and Galactic.
  • The space frame standard location is not supported
  • Time frame are not supported
  • Epochs in year are processed as Jyear

Copy link

codecov bot commented Sep 4, 2024

Codecov Report

Attention: Patch coverage is 90.19608% with 10 lines in your changes missing coverage. Please review.

Project coverage is 81.84%. Comparing base (e0684d1) to head (ff0eeea).
Report is 135 commits behind head on main.

Files with missing lines Patch % Lines
pyvo/mivot/viewer/mivot_viewer.py 60.00% 4 Missing ⚠️
pyvo/mivot/features/sky_coord_builder.py 95.58% 3 Missing ⚠️
pyvo/mivot/features/static_reference_resolver.py 50.00% 1 Missing ⚠️
pyvo/mivot/utils/dict_utils.py 50.00% 1 Missing ⚠️
pyvo/mivot/utils/xml_utils.py 50.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #591      +/-   ##
==========================================
+ Coverage   81.46%   81.84%   +0.38%     
==========================================
  Files          68       70       +2     
  Lines        7034     7156     +122     
==========================================
+ Hits         5730     5857     +127     
+ Misses       1304     1299       -5     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@bsipocz bsipocz added this to the v1.6 milestone Sep 4, 2024
@bsipocz
Copy link
Member

bsipocz commented Sep 4, 2024

One of the test failures in unrelated and we experience it on main, too; but the other one is in mivot.

@lmichel
Copy link
Contributor Author

lmichel commented Sep 4, 2024

There is a coverage issue, I'm on it

Copy link
Member

@bsipocz bsipocz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One tip for fixing the docs build.

@@ -164,6 +164,27 @@ with the `astropy.io.votable` API:

In this case, it is up to the user to ensure that the read data rows are those mapped by the Mivot annotations.

Get a SkyCoord Instance Directly From the Annotations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be the -------- heading level? That would fix the docs build error

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(if not I'll look into a solution)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

return None
if self._mivot_instance_dict["dmtype"] == "mango:EpochPosition":
return self._build_sky_coord_from_mango()
return None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need to return a None here.

Copy link
Contributor Author

@lmichel lmichel Sep 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some linters (e.g. sonarqube) require method to always return something or never, no mixing is allowed. This is not the case here, I removed the return None

- SkyCoord instance or None
"""
if not self._mivot_instance_dict:
return None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could this be a return only? Any particular reason to emphasize it's None?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see comment above

@bsipocz bsipocz added the Affects-dev To be used for follow-up PRs for new engancements or bugs only present on dev label Sep 6, 2024
Copy link
Member

@bsipocz bsipocz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @lmichel for addressing the review. This looks good to me now, but I don't yet merge it to give a chance for the others to chime in.

Copy link
Member

@ManonMarchand ManonMarchand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just saw a few typos.

Also I'm wondering why SkyCoordBuilder has to take a dict in entry and not a MivotInstance. Could it call to_dict internally and spare a call each time? Or is it meant to work on a dictionary not instantiated with a call of to_dict on a MivotInstance?


def _build_sky_coord_from_mango(self):
"""
Build silently a SlyCoord instance from the mango:EpochPosition instance.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo (SlyCoord)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

corrected

https://cds/viz-bin/mivotconesearch/VizierParams
Data are mapped on the mango:EpochPropagtion class as it is implemented in the current code.
This test case is based on 2 VOTables:
Both tests check the generation of SkyCoord instances from the MivotInstances buil
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo (built)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

corrected

:caption: Accessing the model view of Astropy table rows

from pyvo.mivot import MivotViewer
from pyvo.mivot.utils.dict_utils import DictUtils
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you import DictUtils?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a trace of removed statement: corrected


This feature works under the condition that the annotations contain a valid instance of ``mango:EPochPosition``.
Although not a standard at the time of writing, the class structure supported by this implementation must match the figure above.
If the annotation do no contain any vali object, `None` is returned.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo (valid)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

corrected

@lmichel
Copy link
Contributor Author

lmichel commented Sep 19, 2024

Using a dict as input parameter for SkyCoordBuilder is a deliberate choice.

There were several reasons for this:

  • This set up a loose coupling between this class and MivotInstance and allows SkyCoordBuilder to be used out of the MivotInstance context.

  • In a normal use of the API, the user does not need to worry about this dictionary, as it is wrapped into the factory capability of MivotInstance:

mivot_instance.get_SkyCoord()
  • More practically, importing MivotInstance in sky_coord_builder.pywould lead to an import cycle.


This feature works under the condition that the annotations contain a valid instance of ``mango:EPochPosition``.
Although not a standard at the time of writing, the class structure supported by this implementation must match the figure above.
If the annotation do no contain any valid object, `None` is returned.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
If the annotation do no contain any valid object, `None` is returned.
If the annotations do not contain any valid object, `None` is returned.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

- SkyCoord instances can only be built from model classes containing the minimal
set of required parameters (a position).
- In this implementation, only the mango:EpochPosition class is supported since
it proposes anything required to compute the epoch propagation which is a major use-case
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
it proposes anything required to compute the epoch propagation which is a major use-case
it contains the information required to compute the epoch propagation which is a major use-case

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

self._map_coord_names = skycoord_param_default
if "equinox" in coo_sys:
equinox = self._set_year_time_format(coo_sys["equinox"], True)
print(coo_sys["equinox"])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

print? Are these leftovers from debugging?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

@@ -0,0 +1,166 @@
'''
The first service in operation the annotates query responses in the fly is Vizier
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The first service in operation the annotates query responses in the fly is Vizier
The first service in operation that annotates query responses on the fly is Vizier

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@lmichel
Copy link
Contributor Author

lmichel commented Sep 24, 2024

I also updated the change log

- SkyCoord instance or None
"""
if self._mivot_instance_dict and self._mivot_instance_dict["dmtype"] == "mango:EpochPosition":
return self._build_sky_coord_from_mango()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if we should throw an exception instead if a user wants to get the sky coordinates but the MIVOT metadata is not available with the results. The advantage is that the message in the exception can state the issue clearly whereas None is less informative. Opinions?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've no strong opinion about this.
Having no mango:EpochPosition instance in the mapping is not an error, this why I choose to return None, but I agree that using an exception allows to tell the users why he/she cannot get a SkyCoord .
If we do so, we cannot use the MivotException which is risen when something went wrong with the annotations which is not the case here.
I think that if we decide to use an exception, we should add a new one (e.g. NoMatchingClass) or use Python built-in one.

Copy link
Contributor Author

@lmichel lmichel Sep 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After 2 days of thinking, followed by a short discussion with @ManonMarchand, who also suggested a warning, I am leaning towards implementing an exception.
The reason for this is that I bet that similar cases will occur again as long as the binding of PyVO to Mivot gets tighter. So it is wise to foresee a pattern for cases where mapped data does not cover the PYVO API expectations.

The last difficulty is find out a relevant name since the concepts of class/instance also refer to Python stuff.
I propose : NoMatchingDMType.
If none has objection, I'll implement this quickly

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@ManonMarchand ManonMarchand removed the Affects-dev To be used for follow-up PRs for new engancements or bugs only present on dev label Sep 30, 2024
Copy link
Member

@bsipocz bsipocz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, so I started to give this a final review and found the following, albeit nitpicky things:

  • docstrings should be in numpydocs format. Since there wasn't that terribly many of them I left all the fixes as code suggestions.

  • in practice exception and warning classes have Error and Warning in their name. Thus please use NoMatchingDMTypeError. I started adding comments for these, but it's
    way to error-prone on github, so I haven't done it for the full code diff.

  • I would also say to have it as a subclass of TypeError or ValueError, but I can be convinced otherwise.

I leave the Affect-dev label on, so please ignore any changelog check failures. This label will help me as a reminder at release time to condense down the entries to be the most useful for the users.

Once the first two are addressed, I think this should go ahead and be merged.

pyvo/mivot/features/sky_coord_builder.py Outdated Show resolved Hide resolved
Comment on lines 67 to 73
returns
-------
- SkyCoord instance

raises
------
- NoMatchingDMType: if the SkyCoord instance cannot be built.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
returns
-------
- SkyCoord instance
raises
------
- NoMatchingDMType: if the SkyCoord instance cannot be built.
Raises
------
NoMatchingDMTypeError
If the SkyCoord instance cannot be built.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment on lines 77 to 78
raise NoMatchingDMType("No INSTANCE with dmtype='mango:EpochPosition' has been found:"
" cannot build a SkyCoord from annotations")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
raise NoMatchingDMType("No INSTANCE with dmtype='mango:EpochPosition' has been found:"
" cannot build a SkyCoord from annotations")
raise NoMatchingDMTypeError("No INSTANCE with dmtype='mango:EpochPosition' has been "
"found: cannot build a SkyCoord from annotations")

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

"""
Test that a NoMatchingDMType is raised not mapped on mango:EpochPosition
"""
with pytest.raises(NoMatchingDMType):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
with pytest.raises(NoMatchingDMType):
with pytest.raises(NoMatchingDMTypeError):

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

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 NoMatchingDMType
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
from pyvo.mivot.utils.exceptions import NoMatchingDMType
from pyvo.mivot.utils.exceptions import NoMatchingDMTypeError

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment on lines 84 to 90
parameters
----------
- hk_field: MIVOT instance parameter as a dict

returns
-------
- formatted string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
parameters
----------
- hk_field: MIVOT instance parameter as a dict
returns
-------
- formatted string
Parameters
----------
hk_field : dict
MIVOT instance parameter as a dict

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment on lines 103 to 110
parameters
----------
- obstime: string
Observation time is given here because KF4 set an default value
if it is not given
returns
-------
- Astropy space frame
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
parameters
----------
- obstime: string
Observation time is given here because KF4 set an default value
if it is not given
returns
-------
- Astropy space frame
Parameters
----------
obstime : str
Observation time is given here because KF4 set an default value
if it is not given
Returns
-------
Astropy space frame

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment on lines 148 to 150
returns
-------
- SkyCoord instance
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
returns
-------
- SkyCoord instance

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment on lines +130 to +132
returns
-------
- a SkyCoord instance or None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
returns
-------
- a SkyCoord instance or None
Returns
-------
SkyCoord instance or None

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -128,6 +125,14 @@ def update(self, row, ref=None):
setattr(self, self._remove_model_name(key),
MivotUtils.cast_type_value(row[ref], getattr(self, 'dmtype')))

def get_SkyCoord(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this have to be a public method, are the users expected to run it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the public method the must be run by users. The SkyCoordBuilder class is that one that is hidden for the users

@bsipocz bsipocz added the Affects-dev To be used for follow-up PRs for new engancements or bugs only present on dev label Sep 30, 2024
- MappingException renamed as MappingError and MivotException as MivotError
- NoMatchingDMTypeError typed as child of TypeError.
- Both MappingError and MivotError keep children of Exception.
@lmichel
Copy link
Contributor Author

lmichel commented Oct 1, 2024

After the last review I did the following changes

  • Docstrings of sky_coord_builder.py have been reformatted according to the numpy style, including the return statements that ware not mentioned in the review.
  • Some doctring content has been made more explicit
  • NoMatchingDMType as been renamed as NoMatchingDMTypeError and set as child of TypeError
  • I've also renamed MivotException and MappingException with a trailing Error (commit 7b635a34958404fb3000b88945509f098f6d07d7). As this concern most of package files, the commit can be reverted to save review time

Copy link
Member

@bsipocz bsipocz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @lmichel for addressing all the comments! I'll go ahead and merge this now, if anything else comes up please open a follow-up issue/PR.

@bsipocz bsipocz merged commit 9d3c5be into astropy:main Oct 1, 2024
12 of 13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Affects-dev To be used for follow-up PRs for new engancements or bugs only present on dev component: mivot
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants