Skip to content

Commit

Permalink
Add functionality for correct transforms in ax.annotate (#2065)
Browse files Browse the repository at this point in the history
* add initial annotate function
* add homolosine test example

Adds CRS transform handling to annotate for placing the arrows and text.
  • Loading branch information
abrammer committed Sep 2, 2022
1 parent 34fe0a3 commit 8c31a15
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 0 deletions.
40 changes: 40 additions & 0 deletions lib/cartopy/mpl/geoaxes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1701,6 +1701,46 @@ def scatter(self, *args, **kwargs):
self.autoscale_view()
return result

@_add_transform
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.
67 changes: 67 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,70 @@ 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()
def test_annotate():
""" 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.
"""
# use IGH to test annotations cross projection splits and map boundaries
map_projection = ccrs.InterruptedGoodeHomolosine()

fig = plt.figure(figsize=(10, 5))
ax = fig.add_subplot(1, 1, 1, projection=map_projection)
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

0 comments on commit 8c31a15

Please sign in to comment.