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

Bug fixes in WCSAxes.plot_coord #8927

Merged
merged 9 commits into from Oct 4, 2019
Merged

Conversation

Cadair
Copy link
Member

@Cadair Cadair commented Jun 27, 2019

This fixes two bugs in plot_coord:

  1. It more correctly uses .spherical rather than .data to get lon and lat.
  2. It does the frame transform on the input coordinate frame rather than relying on the matplotlib transforms later.

The latter is a fix because the matplotlib transforms are only ever two dimensional, and it should not be assumed that the input coordinate to plot_coord is two dimensional, as it can be in any frame transformable to the native frame of the plot.

I can't really think of a way to test this with just the astropy frames, if anyone has any suggestions of a transformation which would work in 3D but not 2D in astropy frames that would be great.

ping @ayshih

@pllim
Copy link
Member

pllim commented Jun 27, 2019

Is this addressing any existing Astropy issue(s)? Also please add change log and tests. Thanks!

CHANGES.rst Outdated Show resolved Hide resolved
Copy link
Member

@astrofrog astrofrog left a comment

Choose a reason for hiding this comment

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

Just a couple of comments below - thanks!

@@ -299,7 +304,7 @@ def plot_coord(self, *args, **kwargs):
raise TypeError("The 'transform' keyword argument is not allowed,"
" as it is automatically determined by the input coordinate frame.")

transform = self.get_transform(frame0)
transform = self.get_transform(native_frame)
Copy link
Member

Choose a reason for hiding this comment

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

I think you can maybe just do 'world' here

Copy link
Member Author

Choose a reason for hiding this comment

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

I did that at one point but this seemed easier to read. Is there a good reason for one or the other?

elif coord.coord_type == 'latitude':
plot_data.append(frame0.data.lat.to_value(coord.coord_unit))
plot_data.append(frame0.spherical.lat.to_value(u.deg))
Copy link
Member

Choose a reason for hiding this comment

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

I wonder whether it would make sense to only access spherical if the current representation is not spherical or unitspherical, for performance? Or at least pre-compute frame0.spherical to avoid computing it twice.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think that frames cache their different representations anyway, so this should be basically free on both counts.

@astrofrog
Copy link
Member

astrofrog commented Jun 28, 2019

Possible regression test (fails pretty badly with master):

from astropy import units as u
from astropy.wcs import WCS
from astropy.coordinates import SkyCoord
from astropy.utils.data import get_pkg_data_filename
from astropy.io import fits

import matplotlib.pyplot as plt

filename = get_pkg_data_filename('galactic_center/gc_msx_e.fits')
wcs = WCS(filename)
data = fits.getdata(filename)

coord = SkyCoord(0 * u.kpc, 0 * u.kpc, 0 * u.kpc, frame='galactocentric')

fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection=wcs)
ax.imshow(data)
ax.plot_coord(coord, 'ro')
fig.savefig('galcen.png')

The marker should be at the center of the image.

@Cadair
Copy link
Member Author

Cadair commented Jul 4, 2019

@astrofrog Not sure what the procedure is for the image tests?

@astrofrog
Copy link
Member

The image test docs are here: http://docs.astropy.org/en/stable/development/testguide.html#image-tests-with-pytest-mpl

However, can't you just check the pixel coordinates of the resulting marker without doing a full image test? (which seems overkill here)

@Cadair
Copy link
Member Author

Cadair commented Jul 5, 2019

@astrofrog I changed the test. Can you take a look to check I am comparing it to the right thing?

Also I changed the isort config because I needed to use isort.

ax.imshow(data)
point, = ax.plot_coord(coord, 'ro')

np.testing.assert_allclose(point.get_xydata()[0], [0, 0], atol=1e-4)
Copy link
Member

Choose a reason for hiding this comment

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

I'm surprised that this would be at [0,0], it should be near the center of the image.

Copy link
Member Author

Choose a reason for hiding this comment

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

yeah this one confused me as well. is get_xydata in pixels?

Copy link
Member

Choose a reason for hiding this comment

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

Looks like it should be 😕

Copy link
Member

Choose a reason for hiding this comment

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

Ah no I think it might be the data before applying the transform. So to some extent this test is correct but I'd still be happier if we can figure out how to get the transformed data.

Copy link
Member

Choose a reason for hiding this comment

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

You might need point.get_transform().transform(point.get_xydata())

Copy link
Member Author

Choose a reason for hiding this comment

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

That didn't work either, I can't see any other sensible solution other than this here 😕

@Cadair Cadair force-pushed the fix_plotcoord branch 2 times, most recently from 7532a66 to 4c2012e Compare September 23, 2019 10:54
Copy link
Member

@astrofrog astrofrog left a comment

Choose a reason for hiding this comment

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

Looks great, and thanks for fixing the isort config!

@astrofrog astrofrog merged commit 8e5831a into astropy:master Oct 4, 2019
bsipocz pushed a commit that referenced this pull request Oct 5, 2019
Bug fixes in WCSAxes.plot_coord
@@ -289,12 +290,16 @@ def plot_coord(self, *args, **kwargs):
if isinstance(frame0, SkyCoord):
frame0 = frame0.frame

native_frame = self._transform_pixel2world.frame_out
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't work on 3.2.2:

_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <astropy.visualization.wcsaxes.core.WCSAxes object at 0x13297d210>
args = (<SkyCoord (ICRS): (ra, dec) in deg
    (359.76045223, 0.26876217)>, 'o')
kwargs = {'transform': <matplotlib.transforms.CompositeGenericTransform object at 0x131f72190>}
frame0 = <ICRS Coordinate: (ra, dec) in deg
    (359.76045223, 0.26876217)>

    def plot_coord(self, *args, **kwargs):
        """
        Plot `~astropy.coordinates.SkyCoord` or
        `~astropy.coordinates.BaseCoordinateFrame` objects onto the axes.
    
        The first argument to
        :meth:`~astropy.visualization.wcsaxes.WCSAxes.plot_coord` should be a
        coordinate, which will then be converted to the first two parameters to
        `matplotlib.axes.Axes.plot`. All other arguments are the same as
        `matplotlib.axes.Axes.plot`. If not specified a ``transform`` keyword
        argument will be created based on the coordinate.
    
        Parameters
        ----------
        coordinate : `~astropy.coordinates.SkyCoord` or `~astropy.coordinates.BaseCoordinateFrame`
            The coordinate object to plot on the axes. This is converted to the
            first two arguments to `matplotlib.axes.Axes.plot`.
    
        See Also
        --------
    
        matplotlib.axes.Axes.plot : This method is called from this function with all arguments passed to it.
    
        """
    
        if isinstance(args[0], (SkyCoord, BaseCoordinateFrame)):
    
            # Extract the frame from the first argument.
            frame0 = args[0]
            if isinstance(frame0, SkyCoord):
                frame0 = frame0.frame
    
>           native_frame = self._transform_pixel2world.frame_out
E           AttributeError: 'WCSAxes' object has no attribute '_transform_pixel2world'

astropy/visualization/wcsaxes/core.py:295: AttributeError

Copy link
Member

Choose a reason for hiding this comment

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

(relies on the reorganization from #8874)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants