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 by default but complains if xytext is empty
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.
69 changes: 69 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,72 @@ 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.parametrize(
'map_projection',
[ccrs.InterruptedGoodeHomolosine(), ]
)
abrammer marked this conversation as resolved.
Show resolved Hide resolved
@pytest.mark.mpl_image_compare()
def test_annotate(map_projection):
abrammer marked this conversation as resolved.
Show resolved Hide resolved
""" test a variety of annotate options on mulitple projections

Annotate defaults to coords passed as if they're in map projection space.
`transform` or `xycoords` & `textcoords` control the marker and text offset
through shared or independent projections or coordinates.
`transform` is a cartopy kwarg so expects a CRS,
`xycoords` and `textcoords` accept CRS or matplotlib args.

The various annotations below test a variety of the different combinations.
"""

fig = plt.figure(figsize=(10, 5))
ax = fig.add_subplot(1, 1, 1, projection=map_projection)
greglucas marked this conversation as resolved.
Show resolved Hide resolved
ax.set_global()
ax.coastlines()
arrowprops = {'facecolor': 'red',
'arrowstyle': "-|>",
'connectionstyle': "arc3,rad=-0.2",
}
platecarree = ccrs.PlateCarree()
mpltransform = platecarree._as_mpl_transform(ax)

# Add annotation with xycoords as mpltransform as suggested here
# https://stackoverflow.com/questions/25416600/why-the-annotate-worked-unexpected-here-in-cartopy/25421922#25421922
ax.annotate('mpl xycoords', (-45, 43), xycoords=mpltransform,
size=5)

# Add annotation with xycoords as projection
ax.annotate('crs xycoords', (-75, 13), xycoords=platecarree,
size=5)

# set up coordinates in map projection space
map_coords = map_projection.transform_point(-175, -35, platecarree)
# Dont specifiy any args, default xycoords='data', transform=map projection
ax.annotate('default crs', map_coords, size=5)

# data in map projection using default transform, with
# text positioned in platecaree transform
ax.annotate('mixed crs transforms', map_coords, xycoords='data',
xytext=(-175, -55),
textcoords=platecarree,
size=5,
arrowprops=arrowprops,
)

# Add annotation with point and text via transform
ax.annotate('crs transform', (-75, -20), xytext=(0, -55),
transform=platecarree,
arrowprops=arrowprops,
)

# Add annotation with point via transform and text non transformed
ax.annotate('offset textcoords', (-149.8, 61.22), transform=platecarree,
xytext=(-35, 10), textcoords='offset points',
size=5,
ha='right',
arrowprops=arrowprops,
)

return fig