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

Arrows disappear when using cartopy transformation #175

Open
wilkens opened this issue May 24, 2024 · 11 comments
Open

Arrows disappear when using cartopy transformation #175

wilkens opened this issue May 24, 2024 · 11 comments

Comments

@wilkens
Copy link

wilkens commented May 24, 2024

adjust_text works great in most situations. But I'm having a small problem using it with cartopy for mapping. The results are as expected, but the arrows linking labels to markers are missing.

A minimal example and resulting figure are below. I'm probably doing something wrong, but I notice that the cartopy example in the documentation may suffer from the same issue? Any thoughts or help are greatly appreciated. Thanks!

adjustText version 1.1.1
cartopy version 0.23.0

from   adjustText import adjust_text
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import matplotlib.pyplot as plt

lons = [-98, -100]
lats = [40, 40]
labels = ['One', 'Two']

fig = plt.figure(figsize=(12,6))
ax = plt.axes(projection=ccrs.Robinson())
ax.set_extent([-175, 175, -60, 85], crs=ccrs.Geodetic())
plt.scatter(
    x=lons, 
    y=lats,
    transform=ccrs.Geodetic()
)
texts = []
for i, label in enumerate(labels):
    texts.append(
        ax.text(
            lons[i], 
            lats[i], 
            label, 
            transform=ccrs.Geodetic()
           )
    )
adjust_text(texts, arrowprops=dict(arrowstyle="-", color='k'), ax=ax)
ax.add_feature(cfeature.COASTLINE)
ax.add_feature(cfeature.BORDERS)

fig.canvas.draw()
plt.show()

adjusttext_issue

The issue seems to come from adding the transform=ccrs.Geodetic() line to the ax.text call. If I leave that out, I see proper arrows, but of course the labels and arrows are in the wrong place, since they aren't transformed to match the axes.

Here's an example of the actual figure I'm trying to produce, where the missing-arrow problem is clearer:
world_map_all

@Phlya
Copy link
Owner

Phlya commented May 28, 2024

Thank you for reporting this issue. I tried having a look...

These cartopy transforms are weird, I have no experience working with them. Do you have an example how to even add an arrow here that would connect your desired coordinates?

@wilkens
Copy link
Author

wilkens commented May 28, 2024

Thanks for looking into this. I'm definitely no pro with cartopy myself.

Here's code that sets the labels and arrows by hand using ax.annotate. Seems to work OK:

import cartopy.crs as ccrs
import cartopy.feature as cfeature
import matplotlib.pyplot as plt

lons = [-98, -100]
lats = [40, 40]
labels = ['One', 'Two']

fig = plt.figure(figsize=(12,6))
ax = plt.axes(projection=ccrs.Robinson())
ax.set_extent([-175, 175, -60, 85], crs=ccrs.Geodetic())

# plot the points
plt.scatter(
    x=lons, 
    y=lats,
    transform=ccrs.Geodetic()
)

# add the annotations one at a time
ax.annotate(
    text='One', 
    xy=(-98,40),
    xytext=(-90,39),
    xycoords=ccrs.Geodetic(),
    textcoords=ccrs.Geodetic(),
    va='center',
    ha='left',
    arrowprops={'arrowstyle':'-'}
)

ax.annotate(
    text='Two', 
    xy=(-100,40),
    xytext=(-110,41),
    xycoords=ccrs.Geodetic(),
    textcoords=ccrs.Geodetic(),
    va='center',
    ha='right',
    arrowprops={'arrowstyle':'-'}
)

ax.add_feature(cfeature.COASTLINE)
ax.add_feature(cfeature.BORDERS)

fig.canvas.draw()

adjusttext_issue_hand

I don't think the annotations need both xycoords and textcoords. I see the same result if I supply just xycoords. But, belt and suspenders.

@Phlya
Copy link
Owner

Phlya commented May 28, 2024

You are using ax.annotate... The issue is that adjustText just changes the coordinate of the provided Text objects, and then adds an arrow, by creating a FancyArrowPatch and adding it to the axes. This object takes a transform argument, however using it in this context gives an error:
AttributeError: 'Geodetic' object has no attribute 'quick_vertices_transform'
One could rewrite it using ax.annotate, but I think there were some minor issue with that solution.юю

So I am a little stumped as to how to fix this, and I suspect that without fixing this error in cartopy I can't fix this problem here in a reasonable way.

@wilkens
Copy link
Author

wilkens commented May 28, 2024

Ah, yeah, I see the problem. I can replicate the AttributeError when I try to do the same thing by hand. I can work around the error by calling the transform directly on the positions passed to FancyArrowPatch, but the result is the same: no arrows drawn in the output.

It does seem like the solution would be to substitute annotate for separate calls to text and FancyArrowPatch. That said, if annotate causes problems elsewhere, then I guess we're stuck, alas.

Thanks for your work on the package. It's been a great help to me on many occasions!

@Phlya
Copy link
Owner

Phlya commented May 28, 2024

I think I could add an argument to change the behaviour... Not sure about the best naming. But it could simply use annotate to draw the arrow, and not touch the text. I had it like that in the past... I think the issue was aesthetic - the arrows would sometimes strike through the text for some reason. Maybe it could be solved somehow.

@wilkens
Copy link
Author

wilkens commented May 28, 2024

That would certainly be a help to me if you're up for it. In any case, I'm grateful for all your work on the package.

@wilkens
Copy link
Author

wilkens commented May 28, 2024

Oh, and happy to help test/debug ...

@Phlya
Copy link
Owner

Phlya commented May 29, 2024

Can you try the latest commit? For me the arrows work, but they don't get clipped by the text, so can strike through it...

@wilkens
Copy link
Author

wilkens commented May 29, 2024

Thanks! Yes, the new version works as expected. As you note, I do see some strikethrough. I can mess about with horizontal and vertical alignment options to the text objects, which helps a bit. In any case, it's much better than no arrows at all!

Here's my world map showing the new output. Much more legible.
world_map_all

@Phlya
Copy link
Owner

Phlya commented May 29, 2024

You probably get a warning when running it, as it suggests you can increase the shrinkA value in arrowprops to reduce the strikethrough... But in some of them the arrow might start some distance away from the text then.

@wilkens
Copy link
Author

wilkens commented May 29, 2024

Yes, that does help. As you say, it's tricky to find the right balance between minimizing both strikethrough and the gap between texts and arrows. When the label is vertically above/below the marker, a small shrinkA is enough. When the alignment is more horizontal (like richmond or holland or "the united states" on my map), then a very large shrinkA is necessary.

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

2 participants