Skip to content

Arrows disappear when using cartopy transformation #175

Open

Description

@wilkens

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

Activity

  1. Phlya commented on May 28, 2024

    @Phlya
    Owner

    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?

  2. wilkens commented on May 28, 2024

    @wilkens
    Author

    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.

  3. Phlya commented on May 28, 2024

    @Phlya
    Owner

    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.

  4. wilkens commented on May 28, 2024

    @wilkens
    Author

    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!

  5. Phlya commented on May 28, 2024

    @Phlya
    Owner

    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.

  6. wilkens commented on May 28, 2024

    @wilkens
    Author

    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.

  7. wilkens commented on May 28, 2024

    @wilkens
    Author

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

  8. Phlya commented on May 29, 2024

    @Phlya
    Owner

    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...

  9. wilkens commented on May 29, 2024

    @wilkens
    Author

    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

  10. Phlya commented on May 29, 2024

    @Phlya
    Owner

    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.

  11. wilkens commented on May 29, 2024

    @wilkens
    Author

    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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions