python - 避免在 matplotlib + geopandas 上重叠标签
问题描述
我找到了很多关于如何避免在 matplotlib 上重叠文本的示例,但没有一个我可以弄清楚如何应用于我的案例。
我有一个数据框(mapadf1),其中包含有关巴西市政当局的一些信息,并且我绘制了圣保罗州(sp)的 shapefile。
我创建了一个变量“l”,其中包含自治市的名称和我要突出显示的数字。当数字为 0 时,字符串为空。
好的,所以我设法用以下代码绘制了我的地图:
# set the range for the choropleth values
vmin, vmax = 0, 1
# create figure and axes for Matplotlib
fig, ax = plt.subplots(1, figsize=(30, 10))
# remove the axis que mostra latitude e longitude
ax.axis('off')
# add a title and annotation
ax.set_title('Número leitos inaugurados: 22/03', fontdict={'fontsize': '25', 'fontweight' : '3'})
ax.annotate('Fonte: Governo do Estado de São Paulo', xy=(0.6, .05), xycoords='figure fraction', fontsize=12, color='#555555')
# empty array for the data range
sm.set_array([]) # or alternatively sm._A = []. Not sure why this step is necessary, but many recommends it
# create map
mapa_df1.plot(column='tem_leito',cmap='Paired', linewidth=0.8, ax=ax, edgecolor='0.8')
# Add Labels
mapa_df1['coords'] = mapa_df1['geometry'].apply(lambda x: x.representative_point().coords[:])
mapa_df1['coords'] = [coords[0] for coords in mapa_df1['coords']]
for idx, row in mapa_df1.iterrows():
plt.annotate(s=row['l'], xy=row['coords'])
还有我的地图:
我怎样才能避免重叠的文字?!
提前致谢!
解决方案
事实上,plt.annotate
调用创建的对象是一个 matplotlib“注释”——它有很多方法——和一个边界框,可以通过调用.get_window_extent()
返回的对象来检索它。
如果您没有数万个点 - 无论如何都不适合这种绘图,您可以将这些坐标存储在一个列表中 - 并在添加另一个对象时线性检查碰撞。(对于几千个对象,这变得不可行,必须使用比线性策略更好的策略)。
现在还有另一个问题:如果发生碰撞怎么办?更简单的解决方案是不显示违规标签 - 但您可以尝试稍微重新定位新注释,使其不重叠。这样做可能很复杂——但如果我们选择一个简单的天真的策略,例如,只需在 y 轴上移动元素直到它不再重叠,即使有一些地图,你也可以获得相当稀疏的地图的不错结果错误。
一个“更聪明”的策略可以收集所有附近的标签,然后尝试以一种紧密的方式重新定位在一起——这需要几个小时甚至几天的工作。
因此,由于您没有我们可以在本地复制的数据示例,我将编写“将稍后的注释向下移动到 y 轴直到它适合”策略。”至少您将获得一个起点。
from matplotlib.transforms import Bbox
...
text_rectangles = []
y_step = 0.05
# This will have far better results if the labels are sorted descending in the y axis -
#
mapa_df1["sort_key"] = [coord[1] for coord in mapa_df1["coords"]]
mapa_df1.sort_values("sort_key", ascending=False, inplace=True)
del mapa_df1["sort_key"]
for idx, row in mapa_df1.iterrows():
text = plt.annotate(s=row['l'], xy=row['coords'])
rect = text.get_window_extent()
for other_rect in text_rectangles():
while bbox.intersection(rect, other_rect): # overlapping
x, y = text.get_position()
text.set_position(x, y - y_step)
rect = text.get_window_extent()
text_rectangles.append(rect)
之后,您可以获取交互式创建的 Annotation 实例之一,并探索其方法和属性 - 甚至可以与指针交互,具体取决于渲染后端,例如,可以使用透明度级别,然后当鼠标指针悬停时变得完全不透明(例如,请参阅当鼠标悬停在 matplotlib 中的某个点上时可能使标签出现?)。
推荐阅读
- amazon-web-services - 如何使用私有 IP 连接到 EC2 上的后端
- javascript - 如何使用 JS ctx.translate 函数将播放器锁定在屏幕中央?
- python - 将超出范围的值转换为 NA
- java - Spring Boot + Azure Keyvault 无法解析 IP 地址
- java - 如何在java中将图标转换为base64
- angular - 自定义 Angular 库组件未在模板中呈现
- powershell - Powershell 不继续/不捕获/不捕获
- sql - 使用 FOREACH 插入 POSTGRESQL
- javascript - 我可以在这里使用 if else 语句吗?
- python - yfinance 打印最大行数 python