Избегайте перекрывающихся надписей в matplotlib + геопандах - PullRequest
2 голосов
/ 25 марта 2020

Я нашел много примеров того, как избежать наложения текста на matplotlib, но ни одного из них я не мог понять, как применить к моему делу.

У меня есть фрейм данных (mapadf1) с некоторой информацией о бразильских муниципалитетах, и я наложил на шейп-файл состояния Сан-Паулу (sp).

Я создал переменную "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'])

И моей картой:

enter image description here

Как можно избежать наложения текста?!

Заранее спасибо!

1 Ответ

0 голосов
/ 26 марта 2020

Как таковой, объект, созданный с помощью вызова 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? , например).

...