NCAR / geocat-viz

GeoCAT-viz contains tools to help plot geoscience data, including convenience and plotting functions that are used to facilitate plotting geosciences data with Matplotlib, Cartopy, and other visualization packages.
https://geocat-viz.readthedocs.io
Other
50 stars 19 forks source link

set_titles_and_labels : title location strongly changes when adding a subtitle, in portrait format #212

Closed senesis closed 8 months ago

senesis commented 9 months ago

Describe the bug set_titles_and_labels : Vertical space between the title and the map strongly changes when adding a subtitle, in portrait format

To Reproduce When using the code below and the same code un-commenting the commented part, we got the two figures further below

import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import geocat.viz as gv

fig = plt.figure(figsize=(8.5,11))
projection = ccrs.PlateCarree()
ax = plt.axes(projection=projection)
ax.add_feature(cfeature.LAND, color='silver',zorder=1)

gv.set_titles_and_labels(ax,maintitle='Main title')#,subtitle='Subtitle')

plt.show();

Fist figure, without subtitle : title is close to the map image

Second figre, with subtitle : title is much far from the map image

Expected behavior I expected to get the title much closer to the figure and to the subtitle

OS: Linux spirit1.ipsl.fr 5.4.0-170-generic https://github.com/NCAR/geocat-viz/pull/188-Ubuntu SMP Wed Jan 10 09:51:01 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux

Environment See #210

kafitzgerald commented 9 months ago

Thanks for the report!

I'm not sure I've seen such a clear example before, but I believe it's a known issue that some of the utilities in geocat-viz work best with sizes and layouts similar to those used in the GeoCAT-examples gallery. This is something we'd love to improve, but I don't know that it's a high priority at the moment.

That said, if this was something you or someone else had interest in working on, we'd be open to a contribution. We would need to make sure it doesn't break other functionality or negatively impact the examples gallery, but this is something we could help with.

In the meantime, adjusting the figure size to better fit the proportions of the figure should help.

@jukent may be able to say more or have additional suggestions for this case.

jukent commented 9 months ago

Thanks for bringing this to our attention. The subtitle functionality works by calling on matplotlib's figure suptitle. For some reason, even with default figure size, this is not playing well with a Cartopy projection. I will look more into this.

jukent commented 9 months ago

Adding here that another workaround for matplotlib subtitles:

ax.xaxis.set_label_position('top')
ax.xaxis.tick_top()
ax.set_xlabel('Subtitle')
ax.set_title('Title')

Is also thwarted by incorporating a Cartopy projection.

senesis commented 9 months ago

Thanks, but the workaround just above doesn't apply to the case where you want the ticks and tick labels at the bottom.

jukent commented 8 months ago

Hi @senesis yes that is what I meant. Here are 3 work arounds for you to try out and see what is best for your work. Unfortunately this issue is with Cartopy's figure handling, which tries to automatically be helpful and in this case adds an entire figure's worth of space above the intended title location.

  1. Simply add a line break to your main title call. The drawback of this method is that your title and subtitle will have the same fontsize.
    
    fig, ax = plt.subplots(figsize=(8.5, 11), subplot_kw={'projection': ccrs.PlateCarree()})
    ax.add_feature(cfeature.LAND, color='silver',zorder=1)

gv.set_titles_and_labels(ax,maintitle='Main title\nSubtitle')

![newlinesubtitle](https://github.com/NCAR/geocat-viz/assets/46687291/722542a7-7b60-411b-a232-9646e8ebf22e)

2. Add spaces to the `lefttitle` keyword argument so that it is visually in the center.

fig, ax = plt.subplots(figsize=(8.5, 11), subplot_kw={'projection': ccrs.PlateCarree()}) ax.add_feature(cfeature.LAND, color='silver',zorder=1)

gv.set_titles_and_labels(ax,maintitle='Main title', lefttitle=f'{' ' * 45}Subtitle') plt.tight_layout()

![spacedlefttitle](https://github.com/NCAR/geocat-viz/assets/46687291/b6938347-0511-47d9-ba9b-53ee625c7e0d)

3. Use text annotations to add text blocks in specific locations of your figure. This offers the most customization

fig, ax = plt.subplots(figsize=(8.5, 11), subplot_kw={'projection': ccrs.PlateCarree()}) ax.add_feature(cfeature.LAND, color='silver', zorder=1)

Left title text with offset

title = ax.text(0.5, 1.10, "Main Title", ha='center', va='center', fontsize=15, transform=ax.transAxes)

Right title text with offset

sub_title = ax.text(0.5, 1.04, "Subtitle", ha='center', va='center', fontsize=12, transform=ax.transAxes)


![textbox](https://github.com/NCAR/geocat-viz/assets/46687291/3f39dd65-dceb-4e3f-ad5b-e1d845855a43)

Unfortunately each of these methods is non-trivial to implement in a way that works for all user's figure sizes etc, I have added a note to the docstring warning users that the subtitle functionality will not work as expected for Cartopy plots. Thank you again for bringing this to our attention. I hope one of these methods works for your work.
jukent commented 8 months ago

I was specifically looking into Cartopy subplots to see how that affects the above subtitle routines. It seems to me that Cartopy subplots always have more vertical space than I would expect.

The easiest way to fix the vertical space is to adjust your figure size. Your example is set to be 11 inches tall, by making it shorter, the suptitle at the top of the figure and the actual axes will be closer together:

import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import geocat.viz as gv

fig = plt.figure(figsize=(8.5,4))
projection = ccrs.PlateCarree()
ax = plt.axes(projection=projection)
ax.add_feature(cfeature.LAND, color='silver',zorder=1)

gv.set_titles_and_labels(ax,maintitle='Main title',subtitle='Subtitle')

plt.show();

@kafitzgerald original solution is the recommended one for dealing with Cartopy vertical spacing.

Here is another example of a similar spacing issue with Cartopy resolved by figure size adjustment.