Skip to content

Initial API document for WCSAxes#1

Closed
astrofrog wants to merge 13 commits into
wcs-plottingfrom
wcs-plotting-proposal
Closed

Initial API document for WCSAxes#1
astrofrog wants to merge 13 commits into
wcs-plottingfrom
wcs-plotting-proposal

Conversation

@astrofrog

Copy link
Copy Markdown
Owner

Here is an initial version of the document describing the proposed API for WCS Axes-like functionality in Astropy. I've looked over the APLpy and pywcsgrid2 API, and tried to take elements of each which worked well. I think we should have no interest in making the proposed API backward compatible with either pywcsgrid2 and APLpy, and we should see this as an opportunity to have a 'clean slate'. I also feel that we should only include the bare minimum to start with, i.e. no conveniences for e.g. layout (beyond what matplotlib offers by default), beams, compasses, re-projection, etc. I envisage that there could still be higher-level packages that would make use of the base WCS class and that provide these conveniences (unless we decide some of them belong in the core).

Note that this document makes no statements as to the internal organization of the code, only the final API.

For the moment, I'm only opening the PR on one of my own branches. Once the authors of the document are happy, we can open a PR on the official astropy-api repository and get feedback from the mailing list developers.

One organizational question - do we want to propose this be included in astropy.wcs? (which is what it says at the moment). This seems like a natural place to put it, and allows tight coupling of WCS objects and WCSAxes. For example, the current API does not need to make any statement as to what methods are required on WCS classes.

If you want to endorse this and be listed as an author, just let me know!

@leejjoon

leejjoon commented Oct 5, 2012

Copy link
Copy Markdown

This is a great start. Thank you.

One issue I want to raise is what else information (other than the WCS object) we need for creating an WCSAXes.

In some sense, this is somewhat related to how far we want to go to support multiple axes which shares same WCS information.

Let's say we have two (or more) axes with same WCS (for example a channel maps in radio). And often it is desirable that we change a griding parameters in one axes and it affects all other axes. If we want to support this kind of functionality, one option is to have a object, that defines grid-related parameters, shared between multiple axes.

In pywcsgrid2, which heavily uses axisartist toolkit, we do this by creating a grid_helper object and then share them between multiple axes.

For example,

wcs=WCS('image.fits')
grid_param = GridParam(wcs)
ax1 = wcs.subplot(121, wcs=grid_param)
ax2 = wcs.subplot(122, wcs=grid_param)

Of course we should be able to support easy cases like

ax = wcs.subplot(111, wcs=WCS('image.fits'))

I think this is just one way of supporting multiple axes. What I am saying is that we'd better consider how we are going to support such cases.

Comment thread wcs_plotting.py

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

With mpl v1.2 and later, we can configure the axes (or subplot) creation using the projection parameter.

If the value of projection parameters is an object with a "_as_mpl_axes" method, the method will be called, which should return a tuple of the Axes class to create and a dictionary of additional parameters, and these will be used to create an Axes or a Subplot.

For example, if the WCS object implements following method,

def _as_mpl_axes(self):
    return WCSAxes, dict(wcs=self)

We should be able to do

plt.subplot(111, projection=wcs.WCS('image.fits')

And I think this approach is better than having our own wcs.subplot or wcs.axes function.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

This is a good suggestion, and actually brings up a related question. Should we just develop with the 1.2.x API? Or should we aim to be more backward-compatible?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Given that 1.2 is not released yet (although expected soon), I am inclined to support 1.1 at least.

@astrofrog

Copy link
Copy Markdown
Owner Author

@leejjoon - Thanks for your initial review!

Regarding your question about what else we need to initialize WCSAxes, are you thinking that maybe the image data itself should be passed upon initialization? Or are you thinking about something else?

Regarding the issue of the grid helper and sharing parameters between subplots, does the grid helper get used to set parameters (e.g. tick/grid spacing, etc.), or is it only used to tell WCSAxes about the links between axes? For example, if I do:

grid_helper = GridHelper(wcs)
ax1 = plt.subplot(2,2,1, projection=grid_helper)
ax2 = plt.subplot(2,2,2, projection=grid_helper)
ax3 = plt.subplot(2,2,3, projection=grid_helper)
ax4 = plt.subplot(2,2,4, projection=grid_helper)

then should I then do:

ax1.set_xspacing(0.1)

which, given that the projection is a GridHelper object, would know to somehow contact all the other axes, or would I need to do:

grid_helper.set_xspacing(0.1)

?

Just to throw some other ideas out there for linked axes:

ax1 = plt.subplot(2,2,1, projection=wcs)
ax2 = plt.subplot(2,2,2, projection=wcs, linked=ax1)
ax3 = plt.subplot(2,2,3, projection=wcs, linked=ax1)
ax4 = plt.subplot(2,2,4, projection=wcs, linked=ax1)

or

ax1 = plt.subplot(2,2,1, projection=wcs)
ax2 = ax1.add_linked_subplot(2,2,2, projection=wcs)
ax3 = ax1.add_linked_subplot(2,2,3, projection=wcs)
ax4 = ax1.add_linked_subplot(2,2,4, projection=wcs)

In these cases, any change on one axes will propagate to the others, and we don't need to introduce a new class. What do you think?

@leejjoon

Copy link
Copy Markdown

I was only thinking about WCS-related information. This may include

  • slice
  • coordinate system to display (fk5, gal, etc.)

We may further include tick- and/or grid-related parameters. But this could be an implementation detail. As far as we have some way to synchronize these parameters across multiple axes, I think we are good.

My preference for separate object comes from my heavy usage of axes_grid1 toolkit, so I am biased.
Anyhow, if we consider the case like plotting a channel map using axes_grid1, it is good to create these axes by simply changing the axes initialization parameters (e.g., projection).

grid_helper = GridHelper(wcs)
grid = ImageGrid(fig, 111, 
                nrows_ncols = (2, 2), 
                projection=grid_helper
                )

I guess it is not just using with axes_grid1. I believe symmetric parameters makes simple code.

grid_helper = GridHelper(wcs)
for i in range(4):
    plt.subplot(2, 2, i+1, projection=grid_helper)

Asymmetric parameters or new functions (or methods) make things a bit more difficult.

Comment thread wcs_plotting.py

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Again, it would be good if these information can be easily shared between multiple axes.

@leejjoon

Copy link
Copy Markdown

I think my approach with the tick-related api is somewhat different from yours, maybe it would be better if I write an alternative api and compare?

@astrofrog

Copy link
Copy Markdown
Owner Author

@leejjoon - I will look over your comments today during one of the breakout sessions (at the Astropy meeting) but feel free to open a new PR with an alternate API! If it is only different in places, then you could always open a PR on my wcs-plotting-proposal branch, otherwise open it on my master branch.

@astrofrog

Copy link
Copy Markdown
Owner Author

@leejjoon - thanks for all the comments, and sorry for not getting back to this sooner! So far, the two main issues that are unresolved above are:

  • how to best have users control tick/label location/spacing/formatting
  • how to make it easy for people to make plots with several axes sharing parameters

However, I think we shouldn't worry too much about the second so far, because to me that is a convenience that could be implemented later - I don't think people should have to use that convenience layer for single plots, so it doesn't define the base API.

I would be interested to see what you propose for the API for ticks/labels, so feel free to open a new PR with an alternative API (you can always start from my document and just adapt it). For example, how would you make the plot that you showed in your comment above?

@astrofrog

Copy link
Copy Markdown
Owner Author

@leejjoon - following your suggestion, I started to write some examples, which you can see here:

https://github.com/astrofrog/astropy-api/blob/wcs-plotting-proposal/wcs/wcs_examples.md

I liked your suggestion of being able to control the coordinates not by the x and y axis, but by their indices. I took this one step further, and never actually use '1' and 2' in the method name, but use the first argument to indicate the coordinate index this applies to. This is because if we have a 4-d cube, it might be nice to show coordinate 0 and 2, and we don't want to have to hard-code methods for arbitrary numbers of dimensions (also, I don't really like using a 1-based system when everything else in python is zero-based, but that can be tweaked either way).

What do you think about the examples? They are now part of this PR, so you can comment on them directly. The API document would need to be updated a little, but I wanted to first focus on getting the examples the way we want them to be. Do you have any suggestions for improvement?

@leejjoon

Copy link
Copy Markdown

@astrofrog: Thanks a lot for your work with the examples! While it seems that the output of example 3 is a bit tricky to implement, it looks very good overall. And I think we should have feedback from broader audience. Thanks again.

@astrofrog

Copy link
Copy Markdown
Owner Author

@leejjoon - thanks! One final question before I tidy it up and present it to a broader audience - this is something I sent you by email, but in case it got lost I'll repeat it here. What about modifying the API to have:

ra = ax_gal.coords[0]
ra.set_ticklabel_format('ddd')
ra.set_ticklabel_color('green')
ra.set_ticklabel_location('t')

dec = ax_gal.coords[1]
dec.set_ticklabel_format('ddd')
dec.set_ticklabel_color('green')
dec.set_ticklabel_location('r')

which doesn't require the 0 or 1 (or other values) to always be passed as an argument?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants