Repository navigation
Implement transformation to / from CoordinateFrame from / to related WCS - #14820
Conversation
|
Thank you for your contribution to Astropy! 🌌 This checklist is meant to remind the package maintainers who will review this pull request of some common things to look for.
|
|
Hi @cmarmo, the actual PR looks pretty good, but I'm not sure I understand what it's meant to do... Is this a base class for use in custom frames outside of Astropy, or are you intending to refactor some existing frames in Astropy? If the former, I think this needs some examples demonstrating its usage; if the latter, this PR would be a good place to do the refactor 😄. |
It is meant to connect planetary WCS with a dedicated frame in astropy.
what kind of refactoring do you have in mind? |
I just saw this was a base class. If it was a base class for any existing frames, then that would be some small refactoring. If it's for new stuff, then carry on :) |
|
@nstarman I find the documentation about creating a custom frame quite detailed and usable with the base class introduced by this PR. I've added an example instead. |
mhvk
left a comment
There was a problem hiding this comment.
@cmarmo - good to see the next step! I think we need to be a bit careful here not to mix up the representation (which you did already) with the frame. If I have coordinates on Mars, then the representation can be cartesian, spherical, geodetic or bodycentric, but the frame does not care about that. The only thing it needs to know is that it is of Mars, so that, if needed, it can be transformed to something relative to the solar system barycentre, etc. Basically, the frame should be like ITRS is for Earth, i.e., something suitable to add to the frame transformation graph. Of course, there should be different frames for the different planets, since they have different transformations to barycentric. (In princple, there might also be different frames for, say, different precession or orbital models, but that is definitely for another time!)
This has consequences both for the example plot in the new documentation (so good to add that, it helps focus discussion!) and for WCS.
For the example, there should be only one MarsCoordinateFrame. I don't know what the standard is exactly, but given that there are different spheroids, safest would seem to make the default Cartesian (as it is for Earth, via ITRS). And then one can initialize this with different geodetic and bodycentric representations, i.e.,the following would all work,
mars = MarsCoordinateFrame(x=1000*u.km, y=2000*u.km, z=3000*u.km)
mars2 = MarsCoordinateFrame(lon=10*u.deg, lat=20*u.deg, representation_type=MarsBestFitAeroid)
Indeed, note that even now, with the changes you have already made and with your definition of MarsBestFitAeroid in your example, one can do
mars = ITRS(x=1000*u.km, y=2000*u.km, z=3000*u.km)
mars.represent_as(MarsBestFitAeroid) # or .represent_as("marsbestfitaeroid")
# <MarsBestFitAeroid (lon, lat, height) in (rad, rad, m)
# (1.10714872, 0.93481117, 357668.0311648)>
mars2 = ITRS(lon=10*u.deg, lat=10*u.deg, representation_type=MarsBestFitAeroid)
mars2.cartesian
# <CartesianRepresentation (x, y, z) in m
# (3293561.1722263, 580743.6972773, 583553.24056641)>
For WCS, this means that one should not instantiate a BodyBaseCoordinateFrame, but rather for each known object make a subclass, probably best on first use (i.e., cache the result), though perhaps it is fine to just define the 8 that we need in a dict (maybe replacing the one you already have).
One thing: if the default becomes Cartesian, there seems to be little point in having BodyBaseCoordinateFrame for now - it doesn't really do anything! One might as well add obstime attribute where needed.
Anyway, after this long comment, how about the following:
- Make the documentation plot a separate PR that just subclasses
BaseCoordinateFrame(and maybe includes the ITRS test; alternatively, rewrite that test to make a Mars frame as you do in the plot example - since those do not get tested, would be nice to have it! - Here, focus on WCS. I think creating custom classes may be the best way forward for now, but let me ping @mcara and @nden as WCS maintainers too.
- Eventually, move class creation to coordinates, but then it would be good to actually link the frames to the transform graph (so, e.g., one could know where to point a telescope to observe Olympus Mons). Since we do have the machinery in place to calculate barycentric coordinates of the planets, this should not be very hard, but perhaps out of scope here.
Note that I'm not really sure whether or not to keep your BodyBaseCoordinateFrame - I mostly think in the end we'd like it to do more and I don't want to preclude that (also, it should probably be BaseBodyCoordinateFrame...). I'm also worried I'm just not thinking this through completely properly - in a way, these coordinates are like EarthLocation, which is very similar but not identical to ITRS. If we can avoid that can of worms...
| ycoord = "LAT-" | ||
| if frame.obstime is not None: | ||
| wcs.wcs.dateobs = frame.obstime.utc.isot | ||
| if issubclass(frame.representation_type, BaseGeodeticRepresentation): |
There was a problem hiding this comment.
In principle, these two if statements would also apply to ICRS, so perhaps there is a way to store that?
There was a problem hiding this comment.
You mean ITRS, right?
I am confused about the ITRS WCS definition, because of the TLON TLAT descriptions which I cannot find in the literature... if someone has some references this will help me a lot... Thanks!
There was a problem hiding this comment.
Indeed, I meant ITRS. I don't know where TLON, TLAT comes from either... But google gives, e.g., http://tdc-www.harvard.edu/software/wcstools/wcstools.multiwcs.html and https://www.astro.rug.nl/software/kapteyn/allsky.html, so I guess we should just continue to support it.
There was a problem hiding this comment.
Thanks! I coudn't find those links.
|
I am converting this PR back to draft as #14990 needs to go in first. |
5bfc67c to
af7a6e1
Compare
|
Mark Calabretta has just released wcslib8.1, this pr is meant to work when built against wcslib versions >= 8. |
|
I'll make a PR to update bundled WCSLIB first thing in the morning. You can rebase this PR after that. |
|
A PR to update WCSLIB was made here: #15035 Once it is approved, please rebase this PR. Thanks! |
|
I have addressed some of the comments ... looking forward to find some time to address the remaing ones... hopefully soon... |
|
Great! Do ping when all are done, and I'll re-review. |
c2fefc7 to
9616bf6
Compare
|
@mhvk I believe I have addressed your comments. |
mhvk
left a comment
There was a problem hiding this comment.
Thanks! This seems almost done: the main code now has two unnecessary clauses, and I clarified what I meant with reorganizing the tests.
| equatorial_radius, | ||
| flattening, | ||
| ) | ||
| if representation_type is None: |
There was a problem hiding this comment.
This whole clause is no longer needed - the above function call will either create the representation or return the cached function!
Indeed, this is why there is no coverage of these lines.
| frame = solar_system_body_frame( | ||
| SOLAR_SYSTEM_OBJ_DICT.get(xcoord[:2]), representation_type | ||
| ) | ||
| if frame is None: |
There was a problem hiding this comment.
Same, this is no longer needed - the function call will do it.
| name = "Mars" | ||
| representation_type = SphericalRepresentation | ||
|
|
||
| frame = IAUMARSSphereFrame() |
There was a problem hiding this comment.
No, that is not what I meant: I just meant that in this function I feel it is better first to test things as they are supposed to work, and then at the end test that the right errors are raised. I.e., move this pytest.raises to the very end of the test function.
|
|
||
|
|
||
| def test_wcs_to_body_frame(): | ||
| unknown_wcs = WCS(naxis=2) |
There was a problem hiding this comment.
Like for the other direction, I would move the tests for failures to the very end of the test function. I think it is more helpful if a test function first shows how things are supposed to work, and then checks that you get an appropriate error if you do something incorrectly.
| ): | ||
| frame = wcs_to_celestial_frame(triaxial_wcs) | ||
|
|
||
| mywcs = WCS(naxis=2) |
There was a problem hiding this comment.
So, I would start the test function with this part.
| mywcs.wcs.aux.a_radius = 3396190.0 | ||
| mywcs.wcs.aux.b_radius = 3396190.0 | ||
| mywcs.wcs.aux.c_radius = 3376190.0 | ||
| frame = wcs_to_celestial_frame(mywcs) |
There was a problem hiding this comment.
Maybe call this frame3, so that one has
frame3 = wcs_to_celestial_frame(mywcs)
assert frame3.representation_type is not frame.representation_type
assert frame3 is not frame
|
Better now? 😁 |
mhvk
left a comment
There was a problem hiding this comment.
Looks all great now! Let's get it in!
|
@cmarmo - there's a bit of a what's-new already, but I realize now (after merging, of course, sigh) that it may be good to add a little to it, noting that WCS can also be written (with perhaps a link to the article describing the new standard). Perhaps add to the documentation too, if you think that is already useful. |
PR coming soon. |
|
Does this have anything to do with this pyinstaller failure I started seeing? https://github.com/astropy/astropy/actions/runs/6294343966/job/17086053141 |
|
@pllim - I fear so. I think the combination of importing and |
Description
This pull request Implement transformation to / from CoordinateFrame from / to related WCS.
This new frames are meant to be used for body-fixed reference frames for spheroidal Solar System bodies.
The implementation of the already available related WCS is also added.
Towards #11170.
Thanks for considering it.
This work is funded by the Europlanet 2024 Research Infrastructure (RI) Grant.