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
42 changes: 42 additions & 0 deletions lib/cartopy/mpl/geoaxes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1685,6 +1685,48 @@ 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):
abrammer marked this conversation as resolved.
Show resolved Hide resolved
"""
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':
abrammer marked this conversation as resolved.
Show resolved Hide resolved
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':
abrammer marked this conversation as resolved.
Show resolved Hide resolved
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,
abrammer marked this conversation as resolved.
Show resolved Hide resolved
*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.
127 changes: 127 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,130 @@ 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
abrammer marked this conversation as resolved.
Show resolved Hide resolved
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())

abrammer marked this conversation as resolved.
Show resolved Hide resolved
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',)
abrammer marked this conversation as resolved.
Show resolved Hide resolved
def test_annotate_mercator():
""" Update `test_annotate_backwardscompat` to use ccrs.PlateCarree
abrammer marked this conversation as resolved.
Show resolved Hide resolved
as xycoords argument
"""
fig = plt.figure(figsize=(10, 5))
ax = fig.add_subplot(1, 1, 1, projection=ccrs.Mercator())

abrammer marked this conversation as resolved.
Show resolved Hide resolved
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',)
abrammer marked this conversation as resolved.
Show resolved Hide resolved
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)

abrammer marked this conversation as resolved.
Show resolved Hide resolved
"""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",),
abrammer marked this conversation as resolved.
Show resolved Hide resolved
)
abrammer marked this conversation as resolved.
Show resolved Hide resolved

abrammer marked this conversation as resolved.
Show resolved Hide resolved
"""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",),
abrammer marked this conversation as resolved.
Show resolved Hide resolved
)
abrammer marked this conversation as resolved.
Show resolved Hide resolved
return fig


@pytest.mark.natural_earth
@pytest.mark.mpl_image_compare(filename='annotate_homolosine.png', style='mpl20',)
abrammer marked this conversation as resolved.
Show resolved Hide resolved
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)

abrammer marked this conversation as resolved.
Show resolved Hide resolved
"""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",),
abrammer marked this conversation as resolved.
Show resolved Hide resolved
)
abrammer marked this conversation as resolved.
Show resolved Hide resolved

abrammer marked this conversation as resolved.
Show resolved Hide resolved
"""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",),
abrammer marked this conversation as resolved.
Show resolved Hide resolved
)
abrammer marked this conversation as resolved.
Show resolved Hide resolved
return fig