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

Position Angle Frame Transformations #3093

Open
sargas opened this issue Nov 10, 2014 · 19 comments

Comments

@sargas
Copy link
Contributor

commented Nov 10, 2014

This is a feature request to convert angles between coordinate systems with SkyCoord. An example by @cdeil in #2311 :

from astropy.coordinates import ICRS, Galactic, Angle
coordinate = ICRS(0, 0, ('deg', 'deg'))
coordinate.position_angle = Angle(42, 'deg')
print(coordinate.to(Galactic).position_angle)

This example used the ICRS object from the old API, so I'd like to propose two alternatives:

  1. BaseCoordinateFrame carries a class method, perhaps called transform_angle_to, that takes a SkyCoord, Angle, and frame and returns another Angle in the new frame.
  2. SkyCoord contains an angle parameter that gets transformed (if set) when SkyCoord.transform_to is called. I'm inclined against this one, since it effectively turns points (SkyCoords) into vectors. Also, since the transformation of position angles across coordinate systems would be one of the main use cases, this might require a note in the docs that the position_angle function is not related to this functionality.

This functionality would be appreciated to avoid some more wcs-handling for astropy/pyregion#43. Specifically, the ability to transform an angle measured off a point (which may not have the same coordinate system) to another coordinate system is needed.

I'm not familiar enough with WCS transformations to do this myself; I've seen @cdeil's gist attempt to find the new angle by using a small offset and pyregion's existing code to estimate it the new basis vectors with several transformations back and forth. I haven't found how the NED calculator makes the transformation.

@astrofrog

This comment has been minimized.

Copy link
Member

commented Nov 10, 2014

Can we do option 1 but not bother with making it a class method? We could simply use a standalone function, right?

@sargas

This comment has been minimized.

Copy link
Contributor Author

commented Nov 10, 2014

I like that approach. How about something like this: (to use a mix of java syntax):

Angle astropy.coordinates.transform_angle(SkyCoord origin, Angle angle, BaseCoordinateFrame new_frame)
""" Convert angle (defined as east of north from origin in origin's frame) to new_frame """
@astrofrog

This comment has been minimized.

Copy link
Member

commented Nov 10, 2014

@sargas - yes, that seems fine. Thinking about it more, this could be a method of Angle actually. Note that for the first argument, it doesn't have to be SkyCoord, it could also be e.g. an ICRS frame with data (which is the underlying API, SkyCoord is just a convenience).

@mhvk

This comment has been minimized.

Copy link
Contributor

commented Nov 10, 2014

Similar functionality will be needed for postiion differences between two SkyCoord (and for proper motions, etc.). I'd guess we want this as part of coordinates generally rather than Angle (which is really just a Quantity with some nice input parsing). Maybe easiest to start with a function... (CC @eteq, who may have thought much more about this).

@astrofrog

This comment has been minimized.

Copy link
Member

commented Nov 10, 2014

@mhvk - positions between two SkyCoord is already implemented, right?

@mhvk

This comment has been minimized.

Copy link
Contributor

commented Nov 10, 2014

Sorry, I was unclear: I meant similar functionality is needed to transform position differences between two SkyCoord from one system to another. Position angle is just one aspect where the vector from one SkyCoord is in the sky and where one is interested in the angle only, not its norm.

@eteq

This comment has been minimized.

Copy link
Member

commented Nov 10, 2014

@astrofrog @mhvk @sargas - I don't quite understand what is being discussed here. Some of the functionality that's related here:

  • #2487 , which gives the position angle between two SkyCoords.
  • separation and separation_3d, which gives the on-sky angle between two SkyCoords, or the 3d (unitful) distance.

So is the request here to do sort of the inverse of one or both of these? That is, get a skycoord that's some position angle offset from another, or offset by a specific RA and Dec?

I'm confused mostly by the use of the word "transform" - in SkyCoord, "transform" means "convert from one frame to another", which seems to be different from what you're saying (but maybe not?).

@sargas

This comment has been minimized.

Copy link
Contributor Author

commented Nov 10, 2014

@astrofrog - ah, I see how it's done in SkyCoord.separation. Perhaps just Angle.transform_to(self, origin, new_frame), with a return-ed Angle

@eteq - I think my request is different then @mhvk, but the idea is this:

  • You have a point, and an angle measured from this point that depends on the coordinate system. The position angle between two SkyCoord is one example.
  • This angle is defined in a certain direction, so it is frame dependent.
  • SkyCoord.transform_to will change the point to the new coordinate system. The angle must also change numerically, but there is no function in astropy to calculate this.

If the angle is given by the position angle between two SkyCoord, then you could transform them and re-run SkyCoord.position_angle to get the new angle.

@sargas

This comment has been minimized.

Copy link
Contributor Author

commented Nov 10, 2014

As an example, let's say you have a point at (0,0) in FK5, with a vector going due west (so, measuring from east of north, described by this point and Angle('270d')). If I transform to a coordinate frame that is the exact same as FK5 but with the poles reversed, the angle described in this new frame should be Angle('90d')

Another way to state it is that if star A is exactly due west of star B in the FK5 coordinate system (i.e., star_a.position_angle(star_b) == Angle('270d')), then after converting both stars to an upside-down FK5 system I know I should get a position angle of Angle('90d') regardless of where star B was (since it is now due east).

@mhvk

This comment has been minimized.

Copy link
Contributor

commented Nov 10, 2014

@eteq - sorry for the confusion. I think in the end @sargas and I would like to do similar things; in my case, suppose I have a position and proper motion in ICRS, I'd like to be able to transform both to Galactic, but so far, as I understood it, I can easily transform the coordinate only. Since, of course, the magnitude of the proper motion does not change, in the end all I really need is only the transformed position angle on the sky.

@eteq

This comment has been minimized.

Copy link
Member

commented Nov 10, 2014

@sargas - so is the following snippet what you're asking about?

sc1 = SkyCoord(0*u.deg,0*u.deg,frame='fk5')
sc2 = SkyCoord(270*u.deg,0*u.deg,frame='fk5')
pa1 = sc1.position_angle(sc2)
assert pa1.deg == 270

sc1_t = sc1.transform_to(UpsideDownFK5)
sc2_t = sc2.transform_to(UpsideDownFK5)
pa2 = sc1_t.position_angle(sc2_t)
assert pa2.deg == 90

(Of course you'd have to define the transform to UpsideDownFK5 somewhere, but I think this gets the idea across)

@eteq

This comment has been minimized.

Copy link
Member

commented Nov 10, 2014

@mhvk - re: proper motion, that's definitely something we need to consider a "first class citizen" for coordinates (@adrn has been pushing this repeatedly), but I think we need to think carefully about the interface for proper motions (and radial velocities, too, to be fully 3D). But I'm I right you're thinking about this as a "lower-level" implementation for this?

@sargas

This comment has been minimized.

Copy link
Contributor Author

commented Nov 10, 2014

@eteq Yes. This issue is about having a function that takes sc1, pa1, and UpsideDownFK5 and returns pa2

One possible implementation (basically what's done in https://gist.github.com/cdeil/10597172 ) is to generate an sc2 that gives the right position angle.

In @mhvk 's case, he/she would need to get pa2 and sc1_t from sc1, pa1, and UpsideDownFK5. sc1_t is already obtained without sc2, as shown in your snippet.

@sargas

This comment has been minimized.

Copy link
Contributor Author

commented Nov 10, 2014

Actually, I think I should ask clarification of @mhvk's case, since proper motion should be a frame-independent Angle/time AFAIK. Are you trying to transform the direction of the proper motion?

@mhvk

This comment has been minimized.

Copy link
Contributor

commented Nov 11, 2014

@sargas - I was simply thinking of converting μ(α), μ(δ) to μ(lᴵᴵ), μ(bᴵᴵ); you're right that of course |μ| remains the same, but the angle changes -- in just the same way your position angle would change, which is why I brought it up as an example.

@eteq - I was indeed only thinking of the low-level implementation, which requires to have a way to transform position angles -- I'm sorry that in the end my example only seems to have confused matters! But with something along the lines of what @sargas wants in place, proper motions will be all the easier to do.

@jwoillez

This comment has been minimized.

Copy link
Member

commented Nov 11, 2014

Can this request be generalized to transform of vectors?

Connected use cases could be:

  • what does an instrument axis (telescope axis, interferometer baseline) looks like on sky?
  • what is the direction of celestial (ICRS) north on your instrument? How do you attach a compass rose to you images?

Seems connected to SOFA/ERFA for the ICRF to ITRF transform.

@eteq

This comment has been minimized.

Copy link
Member

commented Nov 19, 2014

@sargas - ok, so would something like this work for you?

sc1 = SkyCoord(0*u.deg,0*u.deg,frame='fk5')
pa1 = 270*u.deg 
sc2 = sc1.skycoord_from_position_angle(pa1)  #there's probably a better name for this...

sc1_t = sc1.transform_to(UpsideDownFK5)
sc2_t = sc2.transform_to(UpsideDownFK5)
pa2 = sc1_t.position_angle(sc2_t)

That is, the only thing that new that would need to be added to do this would be skycoord_from_position_angle (which could be useful in other circumstances, anyway).

The direction I'm coming at is that we don't really want to duplicate all the convenience functions with functions that just do the transforms and then the convenience functions - it's ok to just say that you have to do the transforms yourself. I think this would end up more as more readable code because it avoids the problem you mentioned in the initial issue comment about mixing the vector/point concepts.

But if you think that's too repetitive, maybe a utility function that just does a transform and a method in one go is the solution? So that would look something like the top three lines from the above, then the bottom three condensed into a helper function like transform_then_do([sc1, sc2], UpsideDownFK5, sc1.position_angle). That's almost as readable as the above, but more convinient if you're going to repeat many times...

@jwoillez - For "generalized transform of vectors", we definitely want to piggy-back on the work with the frames. But I think we need to think hard about the best way to do this in a way that won't be confusing to users by mixing the vector and point concepts too much - this includes the stuff @mhvk is saying about proper motions. I'm thinking this is a major goal for the version after 1.0, because it would be much more useful once we have the AltAz <-> ICRS conversions in place.

@sargas

This comment has been minimized.

Copy link
Contributor Author

commented Nov 23, 2014

@eteq - Originally I was thinking this could be implemented analytically without approximating the temporary point (the result of the skycoord_from_position_angle function you give). As a stop-gap measure, I implemented this in an earlier pyregion PR:

def _estimate_angle(angle, origin, new_frame, offset=1e-7):
    angle_deg = angle*np.pi/180
    newlat = offset * np.cos(angle_deg) + origin.data.lat.degree
    newlon = (offset * np.sin(angle_deg) / np.cos(newlat * np.pi/180) + origin.data.lon.degree)
    sc = SkyCoord(newlon, newlat, unit='degree', frame=origin.frame.name)
    new_origin = origin.transform_to(new_frame)
    new_sc = sc.transform_to(new_frame)
    return new_origin.position_angle(new_sc)

If you look at other SkyCoord convenience functions (match_to_catalog_*, separation*, to_pixel), none of those depend on the frames used by the SkyCoord or the other object(s) (with the exception of to_pixel's wcs). I don't know any other use for a skycoord_from_position_angle or transform_then_do besides one possible implementation of this, either.

I did move away from this approach in order to be sure I remained consistent with previous code. Given that this will be needed whenever proper motions get handled and for @jwoillez's ideas with generalized frames, perhaps this could be tagged as a wishlist item for astropy.wcs.utils?

@bsipocz

This comment has been minimized.

Copy link
Member

commented Mar 27, 2019

@eteq @adrn - isn't this one being addressed over the years by e.g. http://docs.astropy.org/en/stable/coordinates/matchsep.html#offsets ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
6 participants
You can’t perform that action at this time.