HeLa7889
HeLa7889

Reputation: 23

matplotlib ax to figure extent - remove whitespace, borders, everything for plot of geopandas map

I am looking for a solution to have a seamless map image plot with matplotlib. The current code works good and stable, however, it leaves a whitespace to the left and bottom. I would like to remove this whitespace and don't know how.

My sample code for this:

import geopandas
from seaborn import despine
from pandas import read_csv
import matplotlib.pyplot as plt

# read data and shapefile
geo_path = 'shapefiles/ne_10m_admin_0_countries.shp'
df = read_csv('UNpopEstimates2100.csv')
world = geopandas.read_file(geo_path)

# specifiy what is to be plotted
cm = 'Greys'
world['2015'] = df['2015']

# set plot environment
fig = plt.figure()
ax = fig.add_axes([0, 0, 1, 1])
ax.axis('off')
plt.subplots_adjust(left=0, right=1, bottom=0, top=1)

world.plot(ax=ax, column='2015', cmap=cm, scheme='quantiles')

plt.savefig('sample.png', bbox_inches='tight', tight_layout=True, pad_inches=0, frameon=None)

sample.png

smaple.png with marked whitespace I would like to remove

I followed the Tutorial at Matplotlib's Tight Layout guide, machinelearningplus.com, Removing white padding from figure on Reddit as well as several other stackoverflow posts, namely

Matplotlib scatter plot - Remove white padding,

Matplotlib: Getting subplots to fill figure,

Matplotlib plots: removing axis, legends and white spaces,

Removing white space around a saved image in matplotlib

and

matplotlib plot to fill figure only with data points, no borders, labels, axes,

What am I missing?


edit - to provide a reproducable version with non-real-life data, but question stays the same - how do I get rid of the whitespace around my plot?

I am new to Geopandas, so I am not sure how to recreate a geodataframe, however, there is built in datasets with it.

world = geopandas.read_file(geopandas.datasets.get_path('naturalearth_lowres'))
world['2015'] = np.random.uniform(low=1., high=100., size=(177,))

fig = plt.figure()
ax = fig.add_axes([0, 0, 1, 1])
ax.axis('off')
plt.subplots_adjust(left=0, right=1, bottom=0, top=1)

world.plot(ax=ax, column='2015', scheme='quantiles')

plt.savefig('sample.png')

Upvotes: 1

Views: 6435

Answers (1)

ImportanceOfBeingErnest
ImportanceOfBeingErnest

Reputation: 339430

First there is a difference when using different geopandas versions. One should probably make sure to use geopandas 0.4 at least to have the map in the correct aspect ratio.

Next one needs to remove the padding inside the axes. This can be done using the ax.margins(0) command.

Now this would lead to some whitespace in one direction (top and bottom in this case). One option is to shrink the figure to the extent of the axes.

import numpy as np
import matplotlib; print(matplotlib.__version__)
import matplotlib.pyplot as plt
import geopandas; print(geopandas.__version__)

world = geopandas.read_file(geopandas.datasets.get_path('naturalearth_lowres'))
world['2015'] = np.random.uniform(low=1., high=100., size=(177,))

fig = plt.figure()
ax = fig.add_axes([0, 0, 1, 1])
ax.axis('off')

world.plot(ax=ax, column='2015', scheme='quantiles')

ax.margins(0)
ax.apply_aspect()
bbox = ax.get_window_extent().inverse_transformed(fig.transFigure)
w,h = fig.get_size_inches()
fig.set_size_inches(w*bbox.width, h*bbox.height)

plt.savefig('sample.png')
plt.show()

The advantage of this is that the physical size of the figure really fits the axes; so the result is the same whether shown on screen or saved as image.

If instead the aim is to just save the figure without whitespace you can use the bbox_inches argument to savefig and supply the actual extent of the axes in inches.

fig = plt.figure()
ax = fig.add_axes([0, 0, 1, 1])
ax.axis('off')

world.plot(ax=ax, column='2015', scheme='quantiles')

ax.margins(0)
ax.apply_aspect()
bbox = ax.get_window_extent().inverse_transformed(fig.dpi_scale_trans)

plt.savefig('sample.png', bbox_inches=bbox)

Finally, the above can be automated, using bbox_inches='tight'. However, for the 'tight' option to work correctly, one will need to make sure there are no ticks and labels around the axes, which would otherwise increase the spacing.

fig = plt.figure()
ax = fig.add_axes([0, 0, 1, 1])
ax.axis('off')

world.plot(ax=ax, column='2015', scheme='quantiles')

ax.margins(0)
ax.tick_params(left=False, labelleft=False, bottom=False, labelbottom=False)

plt.savefig('sample.png', bbox_inches="tight", pad_inches=0)

In all three cases above, the resulting figure would be

enter image description here

Upvotes: 5

Related Questions