Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Enh better colorbar axes#20054

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged

Conversation

jklymak
Copy link
Member

@jklymakjklymak commentedApr 23, 2021
edited
Loading

PR Summary

Redo of#18900 because GitHub was mad at me. PR is exactly the same...

closes#19543 and many other annoyances

PR Summary

This is a relatively major re-arrangement of how we create colorbars. This will simplify making colorbars for other norms if the norms are created with a scale. It also allows more normal manipulation of the colorbar axes. There are some negative consequences as well.

Issue

Currently most colorbars are drawn as a pcolormesh on a hidden axes. Whenextend='both'/'upper'/'lower' it makes the axes longer by a proportionextendlen and draws the extend regions by distorting the upper and/or lower cell of the pcolormesh. This is great,except it means that all the usual tick locators need to have special cases that do not continue drawing the ticks into the region of the axis that has the extend triangles. We do that now with a few piecemeal wrapper classes:_ColorbarAutoLocator(ticker.MaxNLocator),_ColorbarAutoMinorLocator(ticker.AutoMinorLocator), etc. Needless to say that is clunky.

The scale used for the colorbar also has to nicely invert pastvmin andvmax for the triangles to be drawn properly, despite the fact these regions are only meant for over/under colors that you wouldn't necessarily expect an arbitrary norm to be able to handle.

Proposal

The proposed solution here is to draw the main pcolor ("solid") on a full axes that goes from vmin to vmax, and draw the extend triangles ("extends") as patches appended to the end of that axes. They are drawn in axes space, so the scale of the axes and the limits of the axes no longer matter.

The problem with this approach is that all the sizing and placement of axes is with an axes that is the size of thesolid and theextends. i.e. if shrink=1 the whole axes, including theextends, is the height (or width) of the parent axes.

The solution proposed here is to draw thesolid on aninset_axes that is shrunk from the parent axes if there areextends.

Pros:

  • The visible axis labels are all on the "inner" axes and no longer require weird wrappers around the tick Locators.
  • Any scale can be used, and the user can set the scale manually as they like. The initial scale is chosen from the norm, if it has one, or chosen to be linear.
  • the user can change the min and max of the axes if they like. We sometimes get requests for this if the user wants to zoom in on some part of the colormap.
  • I think the code is simpler, but I wrote it so YMMV

Cons:

manual axes placement is not the main colorbar axes object any more:

The biggest con is that for

cax=fig.add_axes([0.1,0.1,0.8,0.07])cb=fig.colorbar(im,cax=cax)

the axes object we create is new so thatcb.ax is no longercax. In factcax == cb.ax.parent_axes. So this breaks cases where the user hoped to do things tocax after it was created. Of course they can accesscb.ax. This failed one test where we didcax.tick_params. We can pass these through as we find them (I did already fortick_params). However, this also means thatcax.xaxis points to an axis we make invisible now, and won't do anything (againcb.ax.xaxis points to the right thing.)

I will argue this breakage is worth it. Doing random things tocax failed much of the time anyway, whereas the new object stored oncb.axshould be much more robust. Unfortunately, however, I cannot think of a reasonable way to deprecate this.

Slight change in visibility of lines with extends

This is an obscure one, but lines are now partly cut off if they are drawn on the edge of the colorbar and there is an extend. The test that broke this didn't appear to have a reasonable reason to have an extend in that case anyhow (seecontour_colorbar.png, andcontour_manual_colors_and_levels.png)

Other changes:

  1. There seemed to have been a bug in colorbar widths with extends before, where they ended up a bit narrower than the colorbars without extends. This can be seen indouble_cbar.png and a few other tests.
  2. If there is only one extend, the new axes is offset from the center. This means that the label (which is attached to the inner_axes) is now offset as well. We could move it (by placing the label logic all on the parent), but I'm not convinced that it doesn't look better to let it move down and be centred on the spine. (Alsodouble_cbar.png)
  3. I ditched the private_internal.classic_mode for the colorbars. It doesn't changethat many tests, and it needlessly complicates the Locator code to keep it around.

Test:

fig,ax=plt.subplots(1,2,constrained_layout=True)pc=ax[0].pcolormesh(np.arange(100).reshape(10,10)+1)cb=fig.colorbar(pc,ax=ax[0],extend='both')cb.ax.set_yscale('log')pc=ax[1].pcolormesh(np.arange(100).reshape(10,10)+1)cb=fig.colorbar(pc,ax=ax[1],extend='both')cb.ax.set_ylim([20,90])plt.show()

Before

before

After

after

PR Checklist

  • Has pytest style unit tests (andpytest passes).
  • IsFlake 8 compliant (runflake8 on changed files to check).
  • New features are documented, with examples if plot related.
  • Documentation is sphinx and numpydoc compliant (the docs shouldbuild without error).
  • Conforms to Matplotlib style conventions (installflake8-docstrings and runflake8 --docstring-convention=all).
  • New features have an entry indoc/users/next_whats_new/ (follow instructions in README.rst there).
  • API changes documented indoc/api/next_api_changes/ (follow instructions in README.rst there).

@jklymakjklymak added this to thev3.5.0 milestoneApr 23, 2021
@jklymakjklymak mentioned this pull requestApr 23, 2021
7 tasks
@timhoffm
Copy link
Member

The biggest con is that [...] the axes object we create is new so thatcb.ax? is no longercax. In factcax == cb.ax.parent_axes.

Can we turn this around: make a new parent axes and injectcax as the inset?

@jklymak
Copy link
MemberAuthor

Can we turn this around: make a new parent axes and injectcax as the inset?

I don't know how we would make it socax.set_position andcax.set_xticks would both work, because in this implementation they are different axes.

@timhoffm
Copy link
Member

I see, but this is mostlyset_position vs. Everything else? In which case it might still be better to have everything else work?

@jklymak
Copy link
MemberAuthor

My thinking is that the usual pattern is to not specifycax

cb=fig.colorbar(im,ax=axs[0,1])# Note cb.ax is type ColorAxes that we control, cb.parent_axes is the usual outline axescb.ax.set_yticks()cb.ax.set_position()# note this passes through to the parent axes, so work as before.

Socb.ax behavesexactly as before (if I haven't missed something).

Currently we do not support most tick methods on colorbar axes: e.g.

cbax = fig.add_axes([0.9, 0.1, 0.07, 0.8])cb = fig.colorbar(im, cax=cbax)cbax.set_yticks(np.arange(-2, 2, 0.2))

returns a UserWarning:UserWarning: Use the colorbar set_ticks() method instead. Wecan however, access the xaxis and yaxis properties of the parent, so what we are breaking is the relatively low-level:

cbax.yaxis.set_major_locator(mticker.FixedLocator(np.arange(-2,2,0.1)))

which will be a no-op.

Of course the alternate works fine:

cb.ax.yaxis.set_major_locator(mticker.FixedLocator(np.arange(-2,2,0.1)))

Maybe we could make an Axis subclass that just warned when the y or xaxis is accessed and no-ops for everything.

@jklymak
Copy link
MemberAuthor

jklymak commentedApr 24, 2021
edited
Loading

One thing we could do to be super clear is destroy cax so no methods work on it and it is removed from the axes stack. EDIT: no we can't, because it is a kwarg and hence immutable.

@jklymak
Copy link
MemberAuthor

I don't see a clean way to cripple the parent axes (cax incb = colorbar(im, cax=cax)) or point it to the compound axes that we store atcb.ax. We can't just change the pointer because it is an immutable argument to a kwarg. So, if the user uses a pattern as above,cax is not a good thing for the user to operate on.

While that is awkward, the benefit is that it allows a lot of things to work forcb.ax that previously did not work at all (and of course did not work forcax either).

  • we can set all the locators
  • we can change the scale.
  • we can set the ticks with the usual methods and not lock them out.
  • we can have both axes be labeled if the user wants.
    This will all allow other non-linear norms other than logarithmic, and more user-customizable locators.

Personally I feel that the slight API annoyance (youmust usecb.ax and notcax if you want this thing to work as a colorbar) is a significant improvement over the existing situation, particularly as I expectmost users are not positioning colorbars usingcax=cax. (well, probably many are, but they probably are only doing so based on out-of-date stackoverflow recipes).

@jklymakjklymakforce-pushed theenh-better-colorbar-axes branch from4999aa7 to6b61028CompareMay 2, 2021 19:37
@jklymak
Copy link
MemberAuthor

... about 30 minutes after the above, I came up with a reasonable way to do this in the latest commit:

cb=fig.colorbar(im,cax=cax)assertcb.ax==cax

will returnFalse, but they are functionally the same becauseassert cb.ax.__dict__ == cax.__dict__ returnsTrue. So now anything the user may want to do tocb.ax should also work on their terribly modifiedcax.

This is acceptable -cax is only used to position the colorbar. In the current PR, if it is passed, we copy its position and its_axes_locator.

Copy link
Contributor

@greglucasgreglucas left a comment

Choose a reason for hiding this comment

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

Your last commit looks like a lot of extra code and keeping track of things to make it "functionally" the same without being exactly the same. I'm wondering if you can put something in the docstring to make it clear that you're overwriting the Axes passed in with a ColorbarAxes instead of adding a bunch of code to try and maintain half of the status quo.

Another option here is whether you need to create an entirely new ColorbarAxes or not? Could you just add an attribute to the Colorbar itself to store the inset axes instead (cb.inner_ax)? I think that might simplify some areas to not have to access nested Axes attributes (cb.ax.inner_ax). That might allow you to keepcb.ax ==cax ==cb.ax.parent_axes (now removed). That is just a quick thought I had, so I might be missing something fundamental too.

@@ -458,6 +458,7 @@ def _get_pos_and_bbox(ax, renderer):
bbox = pos
else:
bbox = tightbbox.transformed(fig.transFigure.inverted())
print('bbox', bbox)
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove

jklymak reacted with thumbs up emoji
@@ -0,0 +1,26 @@
Axes used to make colorbar now wrapped
Copy link
Contributor

Choose a reason for hiding this comment

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

Change this API change note to the new PR 20054

jklymak reacted with thumbs up emoji
else:
parent_ax = parent

inner_ax = parent_ax.inset_axes([0, 0, 1, 1])
Copy link
Contributor

Choose a reason for hiding this comment

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

You useinner andparent throughout, I think it would flow better as one of the pairs (inner,outer) or (child,parent) instead. I would vote in favor of inner/outer as that seems to signify this inset axes relationship a little better, but don't have a real strong preference at all.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Sure, did inner/outer....

self.parent_ax.set_yticks = self.inner_ax.set_yticks
for attr in ["get_position", "set_position", "set_aspect"]:
setattr(self, attr, getattr(self.parent_ax, attr))
if userax:
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this if statement is necessary? The other branch of this would just put this dict on a newly created axes.

Copy link
MemberAuthor

@jklymakjklymakMay 4, 2021
edited
Loading

Choose a reason for hiding this comment

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

This is necessary if we want the user's parent axes to be as much like the new axes as possible (short of being the same object). This is what locks out all the methods on the parent axes (not outer!) and points them at the inner axes. That way if the user doesparent.set_xscale('log') it is the same ascb.ax.set_xscale('log').

Copy link
Contributor

Choose a reason for hiding this comment

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

I still think you could do that toouter_ax regardless of whether auserax was passed in or not? In the other case it is just a new axis you've created so I don't think it hurts anything to assign the dict in that case.

I also think you missed the argumentparent change here too.

@jklymak
Copy link
MemberAuthor

Your last commit looks like a lot of extra code and keeping track of things to make it "functionally" the same without being exactly the same. I'm wondering if you can put something in the docstring to make it clear that you're overwriting the Axes passed in with a ColorbarAxes instead of adding a bunch of code to try and maintain half of the status quo.

I guess I'd argue the new code is pretty straight forward though - basically, if the user passed the axes we need to do some extra bookkeeping, otherwise don't bother.

I suppose its possible that the user could manually pass us a gridspec colorbar, but at that point they are tying then selves in knots.

Another option here is whether you need to create an entirely new ColorbarAxes or not? Could you just add an attribute to the Colorbar itself to store the inset axes instead (cb.inner_ax)? I think that might simplify some areas to not have to access nested Axes attributes (cb.ax.inner_ax). That might allow you to keep cb.ax == cax == cb.ax.parent_axes (now removed). That is just a quick thought I had, so I might be missing something fundamental too.

The whole point of all this is to makecb.ax behave like a normal axes, rather than the mostly-broken thing it is now. So the methods we would normally call on an axes shouldn't be buried incb.ax.inner_ax. (i.e. set_xscale...) Worse, we need to lock out those methods on cb.ax.parent because we don't want the user mucking with the outer axes by accident.

@greglucas
Copy link
Contributor

Where you have:

inner_ax=outer_ax.inset_axes([0,0,1,1])self.__dict__=inner_ax.__dict__self.outer_ax=outer_axself.inner_ax=inner_axself.outer_ax.xaxis.set_visible(False)self.outer_ax.yaxis.set_visible(False)self.outer_ax.set_facecolor('none')self.outer_ax.tick_params=self.inner_ax.tick_paramsself.outer_ax.set_xticks=self.inner_ax.set_xticksself.outer_ax.set_yticks=self.inner_ax.set_yticksforattrin ["get_position","set_position","set_aspect"]:setattr(self,attr,getattr(self.outer_ax,attr))

I was wondering if you could create a new method instead that gets called on initialization to create yourinner_ax? I think that you could just put your dispatching methods onto the new attribute axes, rather than needing a ColorbarAxes to do that work for you. Something like this:

def_add_insest_axes(self):# self.ax is outer_axinner_ax=self.ax.inset_axes([0,0,1,1])self.__dict__=inner_ax.__dict__self.inner_ax=inner_axself.ax.xaxis.set_visible(False)self.ax.yaxis.set_visible(False)self.ax.set_facecolor('none')self.ax.tick_params=self.inner_ax.tick_paramsself.ax.set_xticks=self.inner_ax.set_xticksself.ax.set_yticks=self.inner_ax.set_yticksforattrin ["get_position","set_position","set_aspect"]:setattr(self,attr,getattr(self.ax,attr))

@jklymak
Copy link
MemberAuthor

I was wondering if you could create a new method instead that gets called on initialization to create yourinner_ax? I think that you could just put your dispatching methods onto the new attribute axes, rather than needing a ColorbarAxes to do that work for you. Something like this:

I guess stylistically I think that delineating it as a different type of axes would be helpful, and itmay sprout its own methods.

Technically, your suggestions above won't work as written because we will have lost the outer-axes altogether, and we need to keep it and its methods, just make them basically private. i.e. we needsomething to continue to hold those methods.

Copy link
Contributor

@greglucasgreglucas left a comment

Choose a reason for hiding this comment

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

I do think the new Axes you created that is used for mapping between two other axes is a nice way to do it 👍.

I'm wondering if you want to defaultuserax to True instead of False... Let themake_axes routines steal the space and setuserax to False there. I think that would keep it consistent if someone is callingcolorbar.Colorbar(ax) directly themselves rather thanfigure.colorbar().

Comment on lines 1167 to 1168
else:
userax = True
Copy link
Contributor

Choose a reason for hiding this comment

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

You can get rid of some of the branches below if you populate the keywords here.

Suggested change
else:
userax=True
else:
kw['userax']=True
current_ax=cax

jklymak reacted with thumbs up emoji
cb = cbar.Colorbar(cax, mappable, **cb_kw)

self.sca(current_ax)
if not userax:
Copy link
Contributor

Choose a reason for hiding this comment

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

If you take the above suggestion, then this can become:
if current_ax is not cax orif not kw.get('userax', False)

jklymak reacted with thumbs up emoji
Now ``cb.ax`` returns the parent ``ColorbarAxes``, and to get
``cax`` back, you can do::

cb.ax.parent_axes
Copy link
Contributor

Choose a reason for hiding this comment

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

outer/inner in all of this documentation as well.

jklymak reacted with thumbs up emoji
@jklymak
Copy link
MemberAuthor

As discussed, this needs tests of the new monkeying around with the parent....

@anntzer
Copy link
Contributor

anntzer commentedMay 6, 2021
edited
Loading

Having spent a bit of time looking at the side idea (discussed during the call) of using an axis wrapper to fix the locator clipping issue (and solely that -- I'm not claiming it fixes anything else), it turns out one of the issues with that is that we don't use locator return values "as is": there's an additional clip-to-viewlims step which is performed by Axis._update_ticks (that's why Axis.get_majorticklocs can sometimes (for normal axes) return an additional tick out of bound on either side of the viewlims, which is another issue which has been discussed elsewhere...), so that would also need to know about the axis wrapper faked lims, and various tests based oncbar.ax.yaxis.get_majorticklocs() need to be rewritten as the ticklocs are not the actually drawn one anymore...

I may revisit this later, but probably will stash the idea for now. (See also polar._AxisWrapper, from where I stole the design.)

@jklymakjklymakforce-pushed theenh-better-colorbar-axes branch fromcef9099 tocf066f3CompareMay 7, 2021 04:48
@jklymak
Copy link
MemberAuthor

I'm wondering if you want to default userax to True instead of False..

I'm a little torn about this. Users really not really supposed to do this, and if they do, they should expect the axes to get subsumed. It was a problem becauseaxes_grid calls Colorbar directly and when I made this default to True, it of course didn't work. I've made it False but I think its questionable what the default should be.

@jklymakjklymakforce-pushed theenh-better-colorbar-axes branch fromcf066f3 to95e58d7CompareMay 7, 2021 04:52
@greglucas
Copy link
Contributor

I somewhat agree with you on the default choice being questionable, but since you've gone through the whole process of trying to make the originalcax passed in point to the newColorbarAxes it seems to me like the default should be to try and monkey-patch that original axis to keep the original object alive. My guess is that if you passcax into axes_grid, you lose that reference now, although axes_grid is a little bit different and I'm not sure that makes sense since it is really trying to createcax for you in the first place...

Would it be worth trying to deprecate thecax object being kept alive? You could put in the docstring thatcax is only used for placement of a newColorbarAxes, but should not be kept as a reference due to a new object being created.
If you want to have a reference to the axes you should do this instead:

cb = fig.colorbar(cax=cax)cax = cb.ax

(FYI I can't resolve any of my previous comments, so feel free to resolve them yourself)

@jklymak
Copy link
MemberAuthor

We discussed this on the dev call this week. Deprecating cax was definitely possible, and maybe what we will want to do. However in the meantime we would still need to do something with it, and having it do the wrong thing would be confusing. So something like the current setup is still necessary. Or poisoning the original cax.

The subtlety here is that cax can have a _locatable_axes method on it, and that should still work. Actually I should probably add a test that this still works.

@jklymak
Copy link
MemberAuthor

Interestingly this example doesn't properly take into account the colorbar in the constrained layout. This will take a bit of investigation:

importmatplotlib.pyplotaspltimportnumpyasnpfig,ax=plt.subplots(constrained_layout=True)pc=ax.imshow(np.arange(100).reshape(10,10))cax=ax.inset_axes([1.02,0.1,0.03,0.8])# cax.set_in_layout(True)fig.colorbar(pc,cax=cax)plt.show()

@jklymakjklymak marked this pull request as draftMay 7, 2021 15:12
@jklymakjklymakforce-pushed theenh-better-colorbar-axes branch 2 times, most recently from800b32c tod144f43CompareMay 7, 2021 17:27
@jklymakjklymak marked this pull request as ready for reviewMay 7, 2021 20:41
hatches = mappable.hatches
else:
hatches = [None]
if self._extend_lower:
Copy link
Member

Choose a reason for hiding this comment

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

This should be a function call.

jklymak reacted with thumbs up emoji
Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Oh hmmm, interesting I guess those just always ran, but worked fine becauseelower was set to 0...

antialiased=False, transform=self.ax.outer_ax.transAxes,
hatch=hatches[0])
self.ax.outer_ax.add_patch(patch)
if self._extend_upper:
Copy link
Member

Choose a reason for hiding this comment

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

Also a function call.

jklymak reacted with thumbs up emoji
Copy link
Contributor

@greglucasgreglucas left a comment

Choose a reason for hiding this comment

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

Some minor comments inline.

For the extra ticks in theget_ticklocs() tests now, I'm curious if that is because the locator thinks it can place ticks for the entire outer_axes viewLim, so it is placing some in the extends region for now. So, if you got rid of theextends="both" would the locator place the same as the old version? That seems like there may be some inner/outer dispatching mismatch that could be updated in the new ColorbarAxes class. Perhaps using theinner_ax._update_ticks() call from the inner_ax explicitly in the update_ticks() call on colorbar?

self._get_ticker_locator_formatter()
self._long_axis().set_major_locator(self.locator)
self._long_axis().set_minor_locator(self.minorlocator)
self._long_axis().set_major_formatter(self.formatter)

def _get_ticker_locator_formatter(self):
"""
Return the ``locator`` and ``formatter`` of the colorbar.
Copy link
Contributor

Choose a reason for hiding this comment

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

They aren't returned though, they are just set on self?

What about removing this function and adding the logic to update_ticks instead?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

🤷‍♂️ Maybe as a follow on. I'm not sure why this was made public in the first place, and I figure this will be invasive enough that I didn't want to try and deprecate things I didn't fully understand.

config_axis = _api.deprecate_privatize_attribute("3.3")
def update_ticks(self):
"""
Setup the ticks and ticklabels. This should not be needed by users.
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you want to start privatizing this function then?

expected = [r'$\mathdefault{10^{4}}$',
r'$\mathdefault{2\times10^{4}}$',
r'$\mathdefault{3\times10^{4}}$',
r'$\mathdefault{4\times10^{4}}$']
for l, exp in zip(lb, expected):
assert l.get_text() == exp
for exp in expected:
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems like you should expandexpected rather than using anin check because that doesn't say anything about where those ticks show up.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

This answers your question above as well: Before we used to manually clip the ticks on the colorbar axes in the private_ColorbarLocator and_ColorbarLogLocator. Before those, we manually addedall the ticks and there were no minor log ticks. This PR is a further improvement in that we get rid of these locators, and just use whatever normal Locator the user wants. This will allow any norm with a scale to be accurate represented on the colorbar.

As a consequence,cb.ax.yaxis.get_ticklabels behaves exactly the same as for anyyaxis and all these extra ticks are exactly what happens with normal axises. Thats a separate battle, and should/could probably be fixed, but it should be fixed for all axis, not here. Again the point is to remove as much special casing for colorbar axes as possible.

A long way of saying that the full list of ticks is very long because it includes many ticks out of sight, and I don't think its necessary for a more precise test here...

@jklymakjklymakforce-pushed theenh-better-colorbar-axes branch from537946e tof739c48CompareMay 23, 2021 16:08
Copy link
Contributor

@greglucasgreglucas left a comment

Choose a reason for hiding this comment

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

I agree, all of my other suggestions can be addressed in follow-up PRs. This looks good to me. Do you want to squash the commits down to a few descriptive ones?

@jklymakjklymakforce-pushed theenh-better-colorbar-axes branch fromcd36868 to146856bCompareMay 24, 2021 15:19
@jklymak
Copy link
MemberAuthor

Thanks a lot@greglucas I've squashed the whole thing - I probably should get better at curating commits, but I usually mash them all together as I fix things.

@greglucas
Copy link
Contributor

Good work on this,@jklymak!

@dstansby
Copy link
Member

@jklymak this has caused some issues downstream in astropy (astropy/astropy#11800) with custom sub-classes ofNormalize not locating the colorbar ticks properly. Off the top of your head, do you know if there's anything extra someone making aNormalize sub-class needs to now add to the new class? I couldn't find anything in the docs that went with this PR, but sorry if I missed something.

@tacaswell
Copy link
Member

I think we need a color-bar-con-fab in the next month or so (presumably by taking over one of our standing meetings).

@jklymak
Copy link
MemberAuthor

I mean this is why we have pre-releases...

But, I will discuss on your astropy issue, because it looks like there is some confusion on astropy's end. But happy to open a new issue here.

@jklymakjklymak mentioned this pull requestMay 28, 2021
7 tasks
@jklymakjklymak mentioned this pull requestJul 6, 2021
7 tasks
@oscargusoscargus mentioned this pull requestJun 14, 2022
6 tasks
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@efiringefiringefiring approved these changes

@greglucasgreglucasgreglucas approved these changes

Assignees
No one assigned
Projects
None yet
Milestone
v3.5.0
Development

Successfully merging this pull request may close these issues.

Extra ticks at an extended part of colorbar
7 participants
@jklymak@timhoffm@greglucas@anntzer@dstansby@tacaswell@efiring

[8]ページ先頭

©2009-2025 Movatter.jp