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

attributions severely wrapped upon savefig #211

Open
Matthew-J-Payne opened this issue Feb 14, 2023 · 2 comments
Open

attributions severely wrapped upon savefig #211

Matthew-J-Payne opened this issue Feb 14, 2023 · 2 comments

Comments

@Matthew-J-Payne
Copy link

Matthew-J-Payne commented Feb 14, 2023

Hey contextily, thanks for all your work for this library - really great.

Apologies for the gigantic .pngs!

So when I visualise a plot using contextily.add_basemap(), when viewing the plot using jupyter inline, I get a nice unwrapped attribution (showing you via windows snipping tool, hence low dpi), which I would like to be reflected when the .png is saved:

image

But, when saving I get this:

for_pr

My script, below:

import geopandas as gpd
import contextily as ctx

rivers_shapefile = gpd.read_file('datasets/rivers.shp')
rivers_shapefile['class'] = 'rivers'

fig, ax = plot.subplots(figsize = (8, 8))
rivers_shapefile.plot(column = 'class',
                         ax = ax,
                         cmap = 'bwr',
                         linewidth = 0.6,
                         zorder = 4,
                         alpha = 0)
ctx.add_basemap(ax = ax,
                source = ctx.providers.CartoDB.Voyager,
                zorder = 1,
                zoom = 8,
                attribution = "© OpenStreetMap contributors 2023. Distributed under the Open Data Commons Open Database License (ODbL) v1.0",
                attribution_size = 6,
                crs = 32649)

ax.xaxis.set_major_locator(plot.MaxNLocator(4))
ax.yaxis.set_major_locator(plot.MaxNLocator(4))

custom_xticklabels_4 = ["0", "114.1°E", "114.6°E", "115.1°E", "115.6°E"]
custom_yticklabels_4 = ["0", "2.7°N", "3.3°N" , "3.8°N", "4.3°N"]
ax.set_xticklabels(custom_xticklabels_4)
ax.set_yticklabels(custom_yticklabels_4)

fig.savefig(fname = "outputs/for_pr.png", dpi = 1200, facecolor = "white", bbox_inches = "tight")
plot.show() # plots for debugging, defaults to low res plot

The plot is for a paper in review, so I can't publicly share the data, but happy to send across.
Contextily version is 1.3.0 and geopandas 0.12.2.

Any pointers in the right direction is appreciated, thank you!

All the best,
Matt

@darribas
Copy link
Collaborator

Perhaps related to #95 ?

The actual method is at:

def add_attribution(ax, text, font_size=ATTRIBUTION_SIZE, **kwargs):
"""
Utility to add attribution text.
Parameters
----------
ax : AxesSubplot
Matplotlib axes object on which to add the attribution text.
text : str
Text to be added at the bottom of the axis.
font_size : int
[Optional. Defaults to 8] Font size in which to render
the attribution text.
**kwargs : Additional keywords to pass to the matplotlib `text` method.
Returns
-------
matplotlib.text.Text
Matplotlib Text object added to the plot.
"""
# Add draw() as it resizes the axis and allows the wrapping to work as
# expected. See https://github.com/darribas/contextily/issues/95 for some
# details on the issue
draw()
text_artist = ax.text(
0.005,
0.005,
text,
transform=ax.transAxes,
size=font_size,
path_effects=[patheffects.withStroke(linewidth=2, foreground="w")],
wrap=True,
**kwargs,
)
# hack to have the text wrapped in the ax extent, for some explanation see
# https://stackoverflow.com/questions/48079364/wrapping-text-not-working-in-matplotlib
wrap_width = ax.get_window_extent().width * 0.99
text_artist._get_wrap_line_width = lambda: wrap_width
return text_artist

@harningle
Copy link

harningle commented Sep 11, 2024

Seems wrapping here does not work with bbox_inches="tight". In contextily, we always wrap the attribution (line 307):

text_artist = ax.text(
0.005,
0.005,
text,
transform=ax.transAxes,
size=font_size,
path_effects=[patheffects.withStroke(linewidth=2, foreground="w")],
wrap=True,
**kwargs,
)

However, wrap=True "does not work together with savefig(..., bbox_inches='tight')", as said in Matplotlib docs. A hacky solution would be disabling wrapping, if we are sure the text can comfortably sit in a single line:

ctx.add_basemap(ax=ax, ...)
txt = ax.texts[-1]
txt.set_wrap(False)

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

3 participants