-
Notifications
You must be signed in to change notification settings - Fork 220
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
Wrap subplot using with statement #822
Conversation
Wrapping the `subplot` function, in a `with` statement! Original GMT `subplot` function can be found at https://docs.generic-mapping-tools.org/6.1/subplot.html. This is my 3rd attempt at implementing `subplot` in PyGMT, with commits heavily re-adapted/cherry-picked from #412 and #427.
Also updated tutorial a bit
@weiji14 Any thought to using |
@weiji14 It's really a nice job! This is almost what I expect, although I haven't read the details. A quick question about |
I did think of this, but it's technically quite hard to implement. What should a
Thanks, it's really looking at @willschlitzer's
Tough question. I think |
pygmt/src/subplot.py
Outdated
with Session() as lib: | ||
arg_str = " ".join(["set", f"{ax}", build_arg_string(kwargs)]) | ||
lib.call_module(module="subplot", args=arg_str) | ||
yield |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@weiji14 Any thought to using
sca
in a context manager? I know it doesn't have asca_end
equivalent the way thatinset
andsubplot
do, but I think using it withwith
statements will make it more clear that the code is creating different subplots, as opposed to a long line of similar looking plotting functions.I did think of this, but it's technically quite hard to implement. What should a
with fig.sca()
block reset to? The first subplot? The next subplot? There might be a way using function decorators (?) to insertax=1
for allfig.*
functions under the with block but I'll need more time to confirm that it's possible. Or feel free to suggest ideas :)
fig.sca
is now a context manager as implemented here. There's no try... finally
block because there's nothing to reset to. Have a look at the unit tests and tutorials and let me know what you think (i.e. if with fig.sca():
is a good syntax to have, or if we should just stick to fig.sca()
).
A problem with this implementation is that any fig.*
calls outside the with
block would still get applied to the previous panel, which might be counter-intuitive:
with fig.sca(ax=1):
fig.basemap(...) # This is put inside panel 1
fig.logo(...) # This also gets put inside panel 1
with fig.sca(ax=2):
fig.basemap(...) # This is put inside panel 2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the fig.sca
context manager makes no sense here. I'm wondering if we can ask for upstream changes to be able to reset the ax number?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But that would have to wait until GMT 6.2.0 🙂 Also as mentioned at #822 (comment), what do we reset to? The first subplot? The previous one (-1)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The first subplot? The previous one (-1)?
Both don't make sense to me. It's weird and confusing to have sca
as a context manager, but can add plotting elements to panels outside the context manager.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, but map elements outside a with fig.sca()
block can't just go into the main figure, as it would need to be outside the fig.subplot()
indent. It's hard to come up with a good solution here.
So should we 1) just go back to having fig.sca
as an ordinary function (i.e. not a context manager)? Or 2) keep fig.sca()
as a context manager so that people can organize their subplots using indentation (if they choose to, or not)?
Quick comments. In the 2nd example of the tutorial, you use:
It seems passing the index, or row,col also works,
but passing
I think we can just set BTW, it's still unclear to me how |
Yes, in pure GMT, you would use something like
Ok, I'll read this as aliasing
|
Add validation checks for inputs to
Co-Authored-By: Dongdong Tian <[email protected]>
Co-Authored-By: Dongdong Tian <[email protected]>
@@ -0,0 +1,110 @@ | |||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm afraid some of the tests may always pass even though we make some backward-incompatible changes. We can explore how to write better and robust tests later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, definitely not my best attempt as it's mostly testing the aliases and a few subtle quirks. There are probably more complicated tests we can write (especially around fig.set_panel
), but probably best to stress test things in the real world and make changes as we go along.
Co-Authored-By: Dongdong Tian <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@weiji14 I believe this PR is very close to merging!
pygmt/src/subplot.py
Outdated
follow sequentially. Surround the number or letter by parentheses on | ||
any side if these should be typeset as part of the tag. Use **+j|J**\ | ||
*refpoint* to specify where the tag should be placed in the subplot | ||
[TL]. Note: **+j** sets the justification of the tag to *refpoint* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I review the PR #881 from @meghanrjones, I realize that the default values are not documented well. Most readers may find [TL]
confusing, especially that [ ]
means optional arguments in GMT's syntax. I feel that we should use [Default is TL]
, rather than just [TL]
for better clarification.
If you agree, then you need to update the docstrings again, and better to document it in the contributing guides.
[TL]. Note: **+j** sets the justification of the tag to *refpoint* | |
[Default is **TL**]. Note: **+j** sets the justification of the tag to *refpoint* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I review the PR #881 from @meghanrjones, I realize that the default values are not documented well. Most readers may find
[TL]
confusing, especially that[ ]
means optional arguments in GMT's syntax. I feel that we should use[Default is TL]
, rather than just[TL]
for better clarification.
Yes, I hear you that using [Default is TL]
is clearer, but:
- There are a lot of these
[ ]
, and it's not easy to CTRL+F and replace all instances. - We have mostly been copying and pasting from upstream GMT, and it will be a lot of work checking through them all (c.f. How to automate copying of GMT documentation to PyGMT #895).
If you agree, then you need to update the docstrings again, and better to document it in the contributing guides.
I think it's ok for [ ]
to mean both optional and default as long as it's documented, or like you say, use [Default is TL]
instead. I would rather we settle on the standard we want before applying it here in this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, we can discuss it later.
Co-Authored-By: Dongdong Tian <[email protected]>
All green and merged! Thanks @seisman for your patience in all of this 😄 |
Big cheers on this @weiji14 — this is going to be a huge feature for many users! 🎉 |
…Tools#853) Used to select a specific subplot panel. See https://docs.generic-mapping-tools.org/6.1/gmt.html#c-full, https://github.com/GenericMappingTools/gmt/blob/6.1.1/doc/rst/source/explain_-c_full.rst_, and https://docs.generic-mapping-tools.org/6.1/cookbook/options.html#selecting-subplot-panels-the-c-option. Needed for the `subplot` wrapper at GenericMappingTools#822. * Add panel (c) to list of necessary arguments in basemap * Rename alias ax to panel to follow upstream GMT * Update description of common alias c to be more Pythonic Co-authored-by: Dongdong Tian <[email protected]>
Wrapping the `subplot` function, in a `with` statement! Original GMT `subplot` function can be found at https://docs.generic-mapping-tools.org/6.1/subplot.html. This is the 3rd attempt at implementing `subplot` in PyGMT, with commits heavily re-adapted/cherry-picked from GenericMappingTools#412 and GenericMappingTools#427. * Add fig.subplot and fig.set_panel to API docs * Alias fixedlabel (A), clearance (C), verbose (V) for set_panel * Turn fig.set_panel into a context manager * Alias projection (J), region (R), verbose (V), x/yshift (X/Y) for subplot * Allow for spaces in title and labels without needing double quotes Mitigates against GenericMappingTools#247. * Allow for list inputs into fig.set_panel(panel=...) * Rename sca to set_panel and ax to panel * Fix bug that prevented boolean to -A from working * Add note that subplot panel is activated until further notice * Validate subplot nrows/ncols and figsize/subsize argument inputs * Revise advanced subplot layout subsection to use two subplots calls Co-authored-by: Dongdong Tian <[email protected]>
Description of proposed changes
Yet another attempt at wrapping the
subplot
function! Preview the tutorial at https://pygmt-git-subplotv3.gmt.vercel.app/tutorials/subplots.html, translated and adapted from https://docs.gmt-china.org/latest/tutorial/subplot/ (in Chinese).Original GMT
subplot
function can be found at https://docs.generic-mapping-tools.org/6.1/subplot.html. This implementation is inspired by theinset
syntax at #788, namely wrapping things in awith
statement like so:Example code:
Produces:
Alternatively, this works too:
Note that the
with
statement doesgmt subplot begin
, and automatically exits usinggmt subplot end
outside the with-block. Users can set the current subplot usingfig.set_panel
(similar to matplotlib'splt.sca()
) becauseset
is a reserved Python name.Also cross-referencing Julia wrapper/implementation at https://www.generic-mapping-tools.org/GMT.jl/latest/#GMT.subplot.
fig.subplot()
parameters/aliases to wrap:fig.set_panel()
parameters/aliases to wrap:Fixes #20, Supersedes and closes #412 and closes #427
Reminders
make format
andmake check
to make sure the code follows the style guide.doc/api/index.rst
.Notes
/format
in the first line of a comment to lint the code automatically