adjustText
adjustText copied to clipboard
Cartopy adjust_text problem with many texts
I'm using Cartopy for some sea routing project, and adjust_text to avoid the overlap of the texts with each others and the sea route. The problem here is, when using adjust_text with many data, it just place them at some weird places (see figure 1), while creating some good looking maps when there is not much (see figure 2)
Figure 1
Figure 2
Both maps were generated using adjust_text(texts,line[0],line[1],ax=ax, arrowprops=dict(arrowstyle='-', color='grey', lw=0.3))
where texts
is my list of texts, line[0]
and line[1]
are my line coordinates (lat/lon).
Even when modifying parameters like force_text
or force_points
, i keep getting weird output when there is many data. What should i do to fix it?
Can you share a minimal reproducible example?
I never use cartopy myself and thought I fixed it to work, but who knows, maybe I missed something...
I do have a sample code, the minimal i could do :
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import matplotlib.pyplot as plt
from adjustText import adjust_text
from shapely.geometry import LineString
map_border = cfeature.BORDERS.with_scale("110m")
map_ocean = cfeature.OCEAN.with_scale("110m")
map_lake = cfeature.LAKES.with_scale("110m")
map_land = cfeature.LAND.with_scale("110m")
data = [
{"Text": 'Port_1', "lat": 6.4, 'lon': 3.4},
{"Text": 'Port_2', "lat": 0.61, 'lon': -6.1},
{"Text": 'Port_3', "lat": 4.4, 'lon': -17.1},
{"Text": 'Port_4', "lat": 17.9, 'lon': -22.06},
{"Text": 'Port_5', "lat": 32.99, 'lon': -18.01},
{"Text": 'Port_6', "lat": 35.96, 'lon': -7.7},
{"Text": 'Port_7', "lat": 37.09, 'lon': 1.6},
{"Text": 'Port_8', "lat": 40.11, 'lon': 6.4},
{"Text": 'Port_9', "lat": 43.77, 'lon': 8.7},
{"Text": 'Port_10', "lat": 41.8, 'lon': 12.4},
]
map_proj = ccrs.PlateCarree()
color_ocean = '#7c9ccb'
color_land = '#e3f3f3'
color_border = '#d2dde8'
ur_lat = ur_lon = ll_lat = ll_lon = False
for j in data:
if (j["lat"] > ur_lat): ur_lat = j["lat"]
if (j["lon"] > ur_lon): ur_lon = j["lon"]
if (j["lat"] < ll_lat): ll_lat = j["lat"]
if (j["lon"] < ll_lon): ll_lon = j["lon"]
ur_lat += abs(ur_lat - ll_lat) / 3
ur_lon += abs(ur_lon - ll_lon) / 3
ll_lat -= abs(ur_lat - ll_lat) / 3
ll_lon -= abs(ur_lon - ll_lon) / 3
fig = plt.figure(figsize=(9, 6), frameon=False)
ax = fig.add_subplot(1,1,1,projection=map_proj)
if (ll_lon <= -180): ll_lon = -179
if (ll_lat <= -90): ll_lat = -89
if (ur_lon >= 180): ur_lon = 179
if (ur_lat >= 90): ur_lat = 89
ax.set_extent([ll_lon, ur_lon, ll_lat, ur_lat])
ax.add_feature(map_border, edgecolor='#d2dde8', linewidth=1.2)
ax.add_feature(map_ocean, facecolor='#7c9ccb')
ax.add_feature(map_lake, facecolor='#7c9ccb')
ax.add_feature(map_land, facecolor='#e3f3f3')
ax.outline_patch.set_visible(False)
line = []
texts = []
for j in data:
line.append([j["lon"], j["lat"]])
ax.plot(j["lon"], j["lat"], marker='o', color='#f68700', markeredgecolor='#ffffff', markersize=7, transform=map_proj)
texts.append(ax.text(
j["lon"], j["lat"], j["Text"],
bbox={'facecolor':'none', 'edgecolor':'none', 'pad': 0},
fontsize=7, ha='center', va='center'
))
l = LineString(line)
ax.add_geometries([l], map_proj, edgecolor='black', facecolor='none', alpha=1)
l = [[], []]
for i in line:
l[0].append(i[0])
l[1].append(i[1])
adjust_text(texts, l[0], l[1], ax=ax, arrowprops=dict(arrowstyle='-', color='grey', lw=0.3))
plt.show()
Before I try it, what version are you using?
Here are the versions i'm using:
Cartopy: 0.18.0
matplotlib: 3.3.1
adjustText: 0.7.3
Shapely: 1.7.1
I see, can you try updating adjustText from GitHub?
On Thu, Sep 10, 2020, 08:34 Quentin Denis [email protected] wrote:
Here are the versions i'm using:
Cartopy: 0.18.0 matplotlib: 3.3.1 adjustText: 0.7.3 Shapely: 1.7.1
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/Phlya/adjustText/issues/105#issuecomment-690050112, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAWCZOTLIWCMACQLWRL74T3SFB6OZANCNFSM4Q6C4WXQ .
Updated the version from github and i got an error, even using the example available here : https://adjusttext.readthedocs.io/en/latest/Examples.html Here's what i got:
> pip uninstall adjustText
...
> pip install https://github.com/Phlya/adjustText/archive/master.zip
...
Successfully installed adjustText-0.8b2
> python example.py
Traceback (most recent call last):
File "example.py", line 19, in <module>
adjust_text(texts, arrowprops=dict(arrowstyle='->', color='blue'), ax=ax)
File "/home/qdenis/.local/lib/python3.7/site-packages/adjustText/__init__.py", line 641, in adjust_text
ax=ax,
File "/home/qdenis/.local/lib/python3.7/site-packages/adjustText/__init__.py", line 197, in optimally_align_text
ax_bbox = ax.patch.get_extents()
File "/home/qdenis/.local/lib/python3.7/site-packages/matplotlib/patches.py", line 256, in get_extents
return self.get_path().get_extents(self.get_transform())
File "/home/qdenis/.local/lib/python3.7/site-packages/matplotlib/path.py", line 603, in get_extents
return Bbox([xys.min(axis=0), xys.max(axis=0)])
File "/home/qdenis/.local/lib/python3.7/site-packages/numpy/core/_methods.py", line 43, in _amin
return umr_minimum(a, axis, None, out, keepdims, initial, where)
ValueError: zero-size array to reduction operation minimum which has no identity
An error i did not have while using the version installed with pip install adjustText
(adjustText: 0.7.3)
Sorry, I can't reproduce this, I could run all examples in the notebook without this error! (I had some other errors caused by some pandas vs numpy inconsistencies, which I just fixed now)
With the code you provided, I get normal results:
What do you get?
I get the same result, but you can see here it doesn't avoid the line (at Port_5, 6, 7 and 8), even if told to avoid it : adjust_text(texts, l[0], l[1])
You can get the same problem using another kind of data set:
data = [
{"Text": 'Port_kjhblkmjiu', "lat": 43.197167282501276, 'lon': 4.921875},
{"Text": 'Port_mljhpiug', "lat": 35.746512259918504, 'lon': -2.4609375},
{"Text": 'Port_1biuh', "lat": 35.31736632923788, 'lon': -11.77734375},
{"Text": 'Port_kugfuyftrdeyrst', "lat": 25.64152637306577, 'lon': -35.33203125},
{"Text": 'Port_mkojjhbjljbuhgfouy', "lat": 22.917922936146045, 'lon': -70.3125},
{"Text": 'Port_mouyrreq', "lat": 19.973348786110602, 'lon': -75.9375},
{"Text": 'Port_YRZQTEZ<Qe', "lat": 8.928487062665504, 'lon': -76.9921875},
{"Text": 'Port_KJBHCFRTDJFVGYTDSTYRSRS', "lat": 8.233237111274565, 'lon': -79.8046875},
{"Text": 'Port_pichcriyguyvguvhct', "lat": 9.44906182688142, 'lon': -85.4296875},
{"Text": 'Port_kgdyrd', "lat": 12.726084296948196, 'lon': -88.76953125},
{"Text": 'Port_1', "lat": 15.961329081596647, 'lon': -95.44921875},
{"Text": 'Port_akhgdfuygcvej', "lat": 19.476950206488414, 'lon': -105.8203125},
{"Text": 'Port_ibvoufvioh', "lat": 31.353636941500987, 'lon': -113.203125}
]
Sorry, do you expect the text to avoid lines too? That's not possible currently, except with a hacky solution presented in the examples.
Oh, okay. In my first picture (world tour based, where the texts were far apart), the line is a succession of point (which give the curves). So maybe, because there is a bunch of points to avoid, the texts replacement act weird and spread the texts apart like what we saw before?
Yep, that sounds exactly right! Adding a bunch of points along the lines is the hacky solution to avoiding lines I have in examples, but it makes the results quite unstable and doesn't always work well. You can try that again with fewer points, perhaps it would work better, but it doesn't always help.
On Tue, Sep 22, 2020, 12:48 Quentin Denis [email protected] wrote:
Oh, okay. In my first picture (world tour based, where the texts where far apart), the line is a succession of point (which give the curves). So maybe, because there is a bunch of points to avoid, the texts replacement act weird and spread the texts apart like what we saw before?
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/Phlya/adjustText/issues/105#issuecomment-696670316, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAWCZOTNLXXWHFWW5EAPRQLSHCFKBANCNFSM4Q6C4WXQ .
I did some modification on the code i gave, on the part at the end where i put my line in an array, to put intermediates point in :
for m in range(100):
l[0].append(line[i][0] + ((line[i][0] - line[i + 1][0]) / 100 * m))
l[1].append(line[i][1] + ((line[i][1] - line[i + 1][1]) / 100 * m))
Then, the original problem popped out:
I will remove some intermediate point on my original code to fix it temporarily
Right, looks like the labels with problems are the ones from very dense regions, and if you are adding 100 points for each segment it'll surely be way too much in those areas. Try adding points proportionally to the length of the segment, for example, and not that many.
After some modifications, i did another test and worked pretty well
But it's still not satisfying enough, even after using the parameters like
force_point
and i'm kind of stuck now.
OK, this doesn't look too bad - to make it perfect might be quite tricky unfortunately, it's a rather dumb iterative algorithm, so in complex situations it might not work optimally...