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

The first subplot is missized after savefig to a png file. #6457

Closed
gepcel opened this issue May 22, 2016 · 7 comments
Closed

The first subplot is missized after savefig to a png file. #6457

gepcel opened this issue May 22, 2016 · 7 comments

Comments

@gepcel
Copy link
Contributor

gepcel commented May 22, 2016

I'm not sure if this is an issue related to matplotlib or cartopy. I'll give an example here, hope somebody will figure this out. With packages of the following, I can still reproduce this:

matplotlib = 1.5.1
cartopy = 0.14.dev0

%matplotlib inline
%config InlineBackend.figure_format = 'svg'

import cartopy.crs as ccrs
from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

#prepare the data
data = pd.DataFrame(np.array([[123.5, 39.2, 0, 1, 2, 3, 4],
                              [123.5, 39.3, 5, 6, 0, 1, 2],
                              [121.5, 40.7, 3, 4, 5, 6, 0],
                              [121.5, 40.3, 1, 2, 3, 4, 5]]),
                    index=['D01', 'D02', 'D03', 'D04'],
                    columns=['x', 'y', 'g_3', 'g_4', 'g_5', 'g_6', 'g_7'])
labels = ['0', '0-0.5', '0.5-1', '1-2', '2-3', '3-4', '4-5']
groups = [data.groupby('g_'+str(i)) for i in [3, 5, 6, 4, 7]]

x0, x1, y0, y1 = extent = 121, 124, 38.8, 41.1
width = 5
height = width * (y1-y0)/(x1-x0)

# creating 3*2 maps. Actually I only need 5, but I don't how to do that while x&y 
# being both shared. So let's put a legend on the 6th one.
fig, axs = plt.subplots(3, 2, sharex=True, sharey=True, 
                        figsize=(width*2, height*3), 
                        dpi=300, subplot_kw={'projection': ccrs.PlateCarree()})
# for everymap
for i, ax in enumerate(axs.flatten()):
    #some settings of everymap
    ax.set_extent(extent)
    ax.set_xticks([122, 123], crs=ccrs.PlateCarree())
    ax.set_yticks([39, 40, 41], crs=ccrs.PlateCarree())
    ax.xaxis.set_major_formatter(LongitudeFormatter())
    ax.yaxis.set_major_formatter(LatitudeFormatter())
    [tick.label.set_fontsize(11) for tick in ax.yaxis.get_major_ticks()]
    [tick.label.set_fontsize(11) for tick in ax.xaxis.get_major_ticks()]

    #plot the point for the first 5th map
    if i < 5:
        for n, g in groups[i]:
            ax.scatter(g.x, g.y, s=10*n**2, c='k', transform=ccrs.PlateCarree())

# I need a legend for all 5 maps, let's put it on the 6th map
# I don't need to plot on the 6th map, so, I put it outside the extent
for n in range(len(labels)):
    ax.scatter(120, 38, s=10*n**2, c='k', transform=ccrs.PlateCarree(),
              label=labels[n])
ax.legend(loc='upper right', scatterpoints=1)
fig.tight_layout()
fig.savefig('a.png', dpi=300)

By running the code in notebook, I got an image shown as inline in notebook. I did a screen clipping. The image is as:
image

And the a.png file saved by fig.savefig('a.png', dpi=300) is as:
a

Notice that the first plot of the a.png is not the same size as the others, and it isn't aranged well.

Something out of the issue:
Of this example, how can I get 3 or 5 (any odd number other than 1) axes? Because now, I have to photoshop the result image to remove the 6th subplot and leave only the legend. If somebody know something, please let me know. Very appreciate.

Please tell me if any more information is needed.
Thanks.

@tacaswell
Copy link
Member

Can you reproduce this without cartopy or pandas?

@gepcel
Copy link
Contributor Author

gepcel commented May 22, 2016

I can give it a try later (someone is waiting for me, and getting angry). I'll try after I'm back. Thank you.

@efiring
Copy link
Member

efiring commented May 22, 2016

On 2016/05/21 3:42 PM, WANG Aiyong wrote:

Of this example, how can I get 3 or 5 (any odd number other than 1)
axes? Because now, I have to photoshop the result image to remove the
6th subplot and leave only the legend. If somebody know something,
please let me know. Very appreciate.

Like this?

In [1]: fig, axs = plt.subplots(2, 3)

In [2]: axs[-1][-1].set_visible(False)

Eric

@gepcel
Copy link
Contributor Author

gepcel commented May 22, 2016

@tacaswell
No, I cann't reproduce this without cartopy (no missized subplots).
Anyway, I also tried to remove fig.tight_layout() (with cartopy), there's no missized subplots, just large space between subplots.

@gepcel
Copy link
Contributor Author

gepcel commented May 22, 2016

@efiring
I've tried this actually.
First, there will be no xlabel on the 4th (axs[1][1]) subplot. It will be better to have one.
Second, there's an error as following:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-23-c8395ff141e8> in <module>()
     51 
     52 axs[-1][-1].set_visible(False)
---> 53 fig.tight_layout()
     54 #fig.savefig('a.png', dpi=300)

C:\anaconda\lib\site-packages\matplotlib\figure.py in tight_layout(self, renderer, pad, h_pad, w_pad, rect)
   1752                                          renderer,
   1753                                          pad=pad, h_pad=h_pad, w_pad=w_pad,
-> 1754                                          rect=rect)
   1755 
   1756         self.subplots_adjust(**kwargs)

C:\anaconda\lib\site-packages\matplotlib\tight_layout.py in get_tight_layout_figure(fig, axes_list, subplotspec_list, renderer, pad, h_pad, w_pad, rect)
    347                                      subplot_list=subplot_list,
    348                                      ax_bbox_list=ax_bbox_list,
--> 349                                      pad=pad, h_pad=h_pad, w_pad=w_pad)
    350 
    351     if rect is not None:

C:\anaconda\lib\site-packages\matplotlib\tight_layout.py in auto_adjust_subplotpars(fig, renderer, nrows_ncols, num1num2_list, subplot_list, ax_bbox_list, pad, h_pad, w_pad, rect)
    126         tight_bbox_raw = union([ax.get_tightbbox(renderer) for ax in subplots])
    127         tight_bbox = TransformedBbox(tight_bbox_raw,
--> 128                                      fig.transFigure.inverted())
    129 
    130         row1, col1 = divmod(num1, cols)

C:\anaconda\lib\site-packages\matplotlib\transforms.py in __init__(self, bbox, transform, **kwargs)
   1069         *transform*: a 2D :class:`Transform`
   1070         """
-> 1071         if not bbox.is_bbox:
   1072             raise ValueError("'bbox' is not a bbox")
   1073         if not isinstance(transform, Transform):

AttributeError: 'NoneType' object has no attribute 'is_bbox'

@fariza
Copy link
Member

fariza commented May 25, 2016

Do you get the same result if you pass tight_layout=True to the subplots call instead of the fig.tight_layout()?

@gepcel
Copy link
Contributor Author

gepcel commented May 26, 2016

@fariza
Yes, I tried as you said:

fig, axs = plt.subplots(3, 2, sharex=True, sharey=True, 
                        figsize=(width*2, height*3), 
                        dpi=300, subplot_kw={'projection': ccrs.PlateCarree()},
                       tight_layout=True)

#fig.tight_layout()   

I get the same result. But with a warning:
C:\anaconda\lib\site-packages\matplotlib\figure.py:1744: UserWarning: This figure includes Axes that are not compatible with tight_layout, so its results might be incorrect. warnings.warn("This figure includes Axes that are not "

@gepcel gepcel closed this as completed Apr 28, 2017
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

No branches or pull requests

4 participants