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

Add functionality for correct transforms in ax.annotate #2065

Merged
merged 14 commits into from
Sep 2, 2022
40 changes: 40 additions & 0 deletions lib/cartopy/mpl/geoaxes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1685,6 +1685,46 @@ def scatter(self, *args, **kwargs):
self.autoscale_view()
return result

@_add_transform
abrammer marked this conversation as resolved.
Show resolved Hide resolved
def annotate(self, text, xy, xytext=None, xycoords='data', textcoords=None,
*args, **kwargs):
"""
Add the "transform" keyword to :func:`~matplotlib.pyplot.annotate`.

Other Parameters
----------------
transform
A :class:`~cartopy.crs.Projection`.

"""
transform = kwargs.pop('transform', None)
is_transform_crs = isinstance(transform, ccrs.CRS)

# convert CRS to mpl transform for default 'data' setup
if is_transform_crs and xycoords == 'data':
xycoords = transform._as_mpl_transform(self)

# textcoords = xycoords be default but complains if xytext is empty
abrammer marked this conversation as resolved.
Show resolved Hide resolved
if textcoords is None and xytext is not None:
textcoords = xycoords

# use transform if textcoords is data and xytext is provided
if is_transform_crs and xytext is not None and textcoords == 'data':
textcoords = transform._as_mpl_transform(self)

# convert to mpl_transform if CRS passed to xycoords
if isinstance(xycoords, ccrs.CRS):
xycoords = xycoords._as_mpl_transform(self)

# convert to mpl_transform if CRS passed to textcoords
if isinstance(textcoords, ccrs.CRS):
textcoords = textcoords._as_mpl_transform(self)

result = super().annotate(text, xy, xytext, xycoords=xycoords,
textcoords=textcoords, *args, **kwargs)
self.autoscale_view()
return result

@_add_transform
def hexbin(self, x, y, *args, **kwargs):
"""
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
128 changes: 128 additions & 0 deletions lib/cartopy/tests/mpl/test_mpl_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -856,3 +856,131 @@ def test_streamplot():
ax.streamplot(x, y, u, v, transform=ccrs.PlateCarree(),
density=(1.5, 2), color=mag, linewidth=2 * mag)
return fig


@pytest.mark.natural_earth
@pytest.mark.mpl_image_compare(filename='annotate_mercator.png', style='mpl20')
abrammer marked this conversation as resolved.
Show resolved Hide resolved
def test_annotate_backwardscompat():
""" Work around for annotate to work from
https://stackoverflow.com/questions/25416600/why-the-annotate-worked-unexpected-here-in-cartopy/25421922#25421922
- check that an mpl_transform is passed through without issue.
abrammer marked this conversation as resolved.
Show resolved Hide resolved
"""
fig = plt.figure(figsize=(10, 5))
ax = fig.add_subplot(1, 1, 1, projection=ccrs.Mercator())

ax.set_extent([65, 125, 5, 40])
ax.coastlines()
ax.plot(116.4, 39.95, 'ob', transform=ccrs.PlateCarree())
abrammer marked this conversation as resolved.
Show resolved Hide resolved

transform = ccrs.PlateCarree()._as_mpl_transform(ax)
ax.annotate('Beijing', xy=(116.4, 39.9), xycoords=transform,
ha='right', va='top')
ax.annotate('Delhi', xy=(113, 40.5), xytext=(77.23, 28.61),
arrowprops=dict(facecolor='gray',
arrowstyle="simple",
connectionstyle="arc3,rad=-0.2",
alpha=0.5),
xycoords=transform,
ha='right', va='top')

return fig


@pytest.mark.natural_earth
@pytest.mark.mpl_image_compare(filename='annotate_mercator.png', style='mpl20')
def test_annotate_mercator():
""" Update `test_annotate_backwardscompat` to use ccrs.PlateCarree
as xycoords argument
"""
fig = plt.figure(figsize=(10, 5))
ax = fig.add_subplot(1, 1, 1, projection=ccrs.Mercator())

ax.set_extent([65, 125, 5, 40])
ax.coastlines()
ax.plot(116.4, 39.95, 'ob', transform=ccrs.PlateCarree())

transform = ccrs.PlateCarree()
ax.annotate('Beijing', xy=(116.4, 39.9), xycoords=transform,
ha='right', va='top')
ax.annotate('Delhi', xy=(113, 40.5), xytext=(77.23, 28.61),
arrowprops=dict(facecolor='gray',
arrowstyle="simple",
connectionstyle="arc3,rad=-0.2",
alpha=0.5),
xycoords=transform,
ha='right', va='top')

return fig


@pytest.mark.natural_earth
@pytest.mark.mpl_image_compare(filename='annotate_robinson.png', style='mpl20')
def test_global_annotate():
""" test a variety of annotate options on robinson projection
"""

fig = plt.figure(figsize=(10, 5))
ax = fig.add_subplot(1, 1, 1, projection=ccrs.Robinson())
ax.set_global()
ax.coastlines()
platecarree = ccrs.PlateCarree()

"""Add annotation with xycoords as projection"""
ax.plot(-75, 10, 'o', transform=platecarree)
ax.annotate('point 1', (-75, 10), xycoords=platecarree)

"""Add annotation with point and text via transform"""
ax.plot(-75, -20, 'o', transform=platecarree)
ax.annotate('point 2', (-75, -20), xytext=(75, -35),
transform=platecarree,
arrowprops=dict(facecolor='red', arrowstyle="-|>",
connectionstyle="arc3,rad=-0.2",),
)

"""Add annotation with point via transform and text non transformed"""
ax.scatter(75, -20, transform=platecarree)
ax.annotate('offset text', (75, -20), xycoords=platecarree,
xytext=(5, 15), textcoords='offset points',
size=5, va="center", ha="center",
bbox=dict(boxstyle="round4", fc="w"),
arrowprops=dict(facecolor='red', arrowstyle="-|>",
connectionstyle="arc3,rad=-0.2",),
)
return fig


@pytest.mark.natural_earth
@pytest.mark.mpl_image_compare(filename='annotate_homolosine.png',
style='mpl20')
def test_homolosine_annotate():
""" test a variety of annotate options on robinson projection
"""

fig = plt.figure(figsize=(10, 5))
ax = fig.add_subplot(1, 1, 1, projection=ccrs.InterruptedGoodeHomolosine())
ax.set_global()
ax.coastlines()
platecarree = ccrs.PlateCarree()

"""Add annotation with xycoords as projection"""
ax.plot(-75, 10, 'o', transform=platecarree)
ax.annotate('point 1', (-75, 10), xycoords=platecarree)

"""Add annotation with point and text via transform"""
ax.plot(-75, -20, 'o', transform=platecarree)
ax.annotate('point 2', (-75, -20), xytext=(25, -35),
transform=platecarree,
arrowprops=dict(facecolor='red', arrowstyle="-|>",
connectionstyle="arc3,rad=-0.2",),
)

"""Add annotation with point via transform and text non transformed"""
ax.scatter(75, -20, transform=platecarree)
ax.annotate('offset text', (75, -20), xycoords=platecarree,
xytext=(15, -15), textcoords='offset points',
size=5, va="center", ha="center",
bbox=dict(boxstyle="round4", fc="w"),
arrowprops=dict(facecolor='red', arrowstyle="-|>",
connectionstyle="arc3,rad=-0.2",),
)
return fig