astropy: Cannot directly transform output from GCRS to AltAz / need ITRS location?

EDIT: put a milestone of 4.3 for the feature request part of this issue, which is to normalize they arguments used to indicate an observer location. EDIT 2: update to include more recent behaviour (#11069)

Following discussion in #10994, I thought the example below should work, where I use get_body() to get a SkyCoord with a GCRS position for Mars at a given time and location, and try to transform it to AltAz just using body.altaz.

However, it fails, because the temporary AltAz frame that was created does not have a .location attribute. This is unexpected, since the SkyCoord made by get_body does have that information - it is just stored in attributes with a different name, obsgeoloc and obsgeovel (which are CartesianRepresentation instead of EarthLocation for .location). Indeed, if I explicitly set my body.location to the relevant value, body.altaz does work.

So I guess one question is whether we can just translate between these attributes. In principle, the simplest route might be to just let AltAz understand obsgeopos as an alias for location (and maybe eventually deprecate AltAz.location in favour of it).

But perhaps a larger question is why AltAz and, e.g., GCRS are using different attributes in the first place for storing the observer location.

I should add that I’m not 100% sure either is ideal - for general use, I’d want to have the velocity stored on the position as a derivative (i.e., at least obsgeovel as a .derivatives['s'] on obsgeopos – of course, the derivatives did not exist when obsgeovel was first introduced!). Also, it would seem better to have something that is explicit in what frame the position and velocity are defined. I think the information needed matches exactly what would be an ITRS or GCRS frame instance. So, perhaps one should have a single attribute that replaces location, obsgeoloc and obsgeovel? (and deprecate those three?) Reuse location? New obs_itrs?

cc @StuartLittlefair, @mkbrewer, @eteq, @adrn, @astrojuanlu

from astropy.coordinates import get_body, ITRS
from astropy.time import Time
from astropy import units as u

body = get_body('mars', Time(['J2010'], location=(10.*u.deg, 20*u.deg)))
# The following fails:
body.altaz
AttributeError                            Traceback (most recent call last)
<ipython-input-4-d1487e3dca10> in <module>
----> 1 body.altaz

~/data/mhvk/packages/astropy/astropy/coordinates/sky_coordinate.py in __getattr__(self, attr)
    784             frame_cls = frame_transform_graph.lookup_name(attr)
    785             if frame_cls is not None and self.frame.is_transformable_to(frame_cls):
--> 786                 return self.transform_to(attr)
    787 
    788         # Fail

~/data/mhvk/packages/astropy/astropy/coordinates/sky_coordinate.py in transform_to(self, frame, merge_attributes)
    617         # Do the transformation, returning a coordinate frame of the desired
    618         # final type (not generic).
--> 619         new_coord = trans(self.frame, generic_frame)
    620 
    621         # Finally make the new SkyCoord object from the `new_coord` and

~/data/mhvk/packages/astropy/astropy/coordinates/transformations.py in __call__(self, fromcoord, toframe)
   1420 
   1421             curr_toframe = t.tosys(**frattrs)
-> 1422             curr_coord = t(curr_coord, curr_toframe)
   1423 
   1424         # this is safe even in the case where self.transforms is empty, because

~/data/mhvk/packages/astropy/astropy/coordinates/transformations.py in __call__(self, fromcoord, toframe)
   1023             return reprwithoutdiff.realize_frame(reprwithdiff)
   1024         else:
-> 1025             return supcall(fromcoord, toframe)
   1026 
   1027 

~/data/mhvk/packages/astropy/astropy/coordinates/builtin_frames/cirs_observed_transforms.py in cirs_to_altaz(cirs_coo, altaz_frame)
     39 
     40     # first set up the astrometry context for CIRS<->AltAz
---> 41     astrom = erfa_astrom.get().apio(altaz_frame)
     42     az, zen, _, _, _ = erfa.atioq(cirs_ra, cirs_dec, astrom)
     43 

~/data/mhvk/packages/astropy/astropy/coordinates/erfa_astrom.py in apio(frame_or_coord)
    109             For this function, an AltAz frame is expected.
    110         '''
--> 111         lon, lat, height = frame_or_coord.location.to_geodetic('WGS84')
    112         jd1_tt, jd2_tt = get_jd12(frame_or_coord.obstime, 'tt')
    113 

AttributeError: 'NoneType' object has no attribute 'to_geodetic'

# but this works
body.location = ITRS(body.obsgeoloc).earth_location
body.altaz

<SkyCoord (AltAz: obstime=['J2010.000'], location=[(-2061640.62248365, 5629477.088513, 2169697.89603318)] m, pressure=0.0 hPa, temperature=0.0 deg_C, relative_humidity=0.0, obswl=1.0 micron): (az, alt, distance) in (deg, deg, m)
    [(281.60515631, 26.39730811, 1.10516762e+11)]>

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Reactions: 1
  • Comments: 25 (25 by maintainers)

Most upvoted comments

@pllim - yes, milestone removed. Just goes to show that having a milestone on an issue is rarely useful - wish I had gotten to this though!

sunpy has also done some hacks to retain the name of an observer once it is converted to a real coordinate (via a call to get body), so something along those lines would also be useful.

I think you’d need a MarsLocation subclass for that and the theory for that hasn’t been invented yet. Not to mention precession and nutation.

Do CIRS, TETE and AltAz have an application for a non-terrestrial observer? It seems to me that earth rotation, precession and nutation wouldn’t be applicable there. If a non-terrestrial observer needs to use an Earth centered coordinate system, GCRS would be more appropriate and all that’s needed there is to specify the observer’s position and velocity wrt the geocenter.