Как показать метку шейп-файла в легенде базовой карты python?

Базовая карта python содержит два изображения: многострочный шейп-файл (IL_State_ln) и диаграмму рассеяния некоторых случайных точек в экстенте базовой карты. Меня интересует создание легенды, которая дает информацию как о шейп-файле, так и о точках рассеяния. На данный момент я могу включать точки только в легенду, а не в шейп-файл.

Проверка документации Basemap API не дает никакой информации, как это делает функция readshapefile(). похоже, у него нет аргумента label.

Не могли бы вы помочь мне включить индикатор шейп-файла в легенду, как на картах ArcGIS?

Вот мой код:

import numpy as np    
from matplotlib import pyplot as plt
from mpl_toolkits.basemap import Basemap

fname = "DATA/GIS/IL_State_ln"

m = Basemap(llcrnrlon=-92.,llcrnrlat=36.8,urcrnrlon=-86.5,urcrnrlat=43.,
             resolution='i', projection='tmerc', lat_0 = 36.5, lon_0 = -91.8)

m.readshapefile(fname, 'mygeom')

x,y = m([-90., -91.2, -88.], [38., 37.7, 42.])
m.scatter(x,y, marker='o', label="Points")

plt.legend(loc=3)
plt.show()

Я использую Python 3.5, matplotlib 2.0 и базовую карту 1.0.8.


person SereneWizard    schedule 07.03.2017    source источник


Ответы (1)


Идея создать запись легенды состоит в том, чтобы рисовать фигуры в виде многоугольников, которые затем можно добавить в легенду.
Поэтому сначала мы должны деактивировать drawbounds, m.readshapefile(fn, 'shf', drawbounds = False). Затем мы можем создать matplotlib.patches.Polygon из шейп-файла и добавить его к осям plt.gca().add_artist(polygon).

Затем легенду можно обновить с помощью этого полигона.

handles, labels = plt.gca().get_legend_handles_labels()
handles.extend([polygon])  
labels.extend(["Name of the shape"])                     
plt.legend(handles=handles, labels=labels)

Вот некоторый код в действии, который создает следующие изображения. Он использует файл ne_10m_admin_0_countries.

введите описание изображения здесь

from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
import numpy as np

m = Basemap(llcrnrlon=-10,llcrnrlat=35,urcrnrlon=35,urcrnrlat=60.,
             resolution='i', projection='tmerc', lat_0 = 48.9, lon_0 = 15.3)

m.drawcoastlines()
m.drawcountries(zorder=0, color=(.9,.9,.9), linewidth=1)

fn = r"ne_10m_admin_0_countries\ne_10m_admin_0_countries"
m.readshapefile(fn, 'shf', drawbounds = False)

#Madrid
x,y = m([-3.703889],[40.4125])
m.plot(x,y, marker="o", color="blue", label="Madrid", ls="")

# some countries
countries = ['Switzerland', 'Ireland', "Belgium"]
colors= {'Switzerland':"red", 'Ireland':"orange", 'Belgium' : "purple"}
shapes = {}
for info, shape in zip(m.shf_info, m.shf):
    if info['NAME'] in countries:
        p= Polygon(np.array(shape), True, facecolor= colors[info['NAME']], 
                   edgecolor='none', alpha=0.7, zorder=2)
        shapes.update({info['NAME'] : p})

for country in countries:
    plt.gca().add_artist(shapes[country]) 


# create legend, by first getting the already present handles, labels
handles, labels = plt.gca().get_legend_handles_labels()
# and then adding the new ones
handles.extend([shapes[c] for c in countries])  
labels.extend(countries)                     
plt.legend(handles=handles, labels=labels, framealpha=1.)

plt.show()

Теперь, поскольку у нас уже есть многоугольник с формой, почему бы не сделать легенду немного более причудливой, напрямую встроив фигуру в легенду. Это можно сделать следующим образом.

введите описание изображения здесь

from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
import numpy as np

m = Basemap(llcrnrlon=-10,llcrnrlat=35,urcrnrlon=35,urcrnrlat=60.,
             resolution='i', projection='tmerc', lat_0 = 48.9, lon_0 = 15.3)

m.drawcoastlines()

fn = r"ne_10m_admin_0_countries\ne_10m_admin_0_countries"
m.readshapefile(fn, 'shf', drawbounds = False)

#Madrid
x,y = m([-3.703889],[40.4125])
m.plot(x,y, marker="o", color="blue", label="Madrid", ls="")

countries = ['Switzerland', 'Ireland', "Belgium"]
colors= {'Switzerland':"red", 'Ireland':"orange", 'Belgium' : "purple"}
shapes = {}
for info, shape in zip(m.shf_info, m.shf):
    if info['NAME'] in countries:
        p= Polygon(np.array(shape), True, facecolor= colors[info['NAME']], 
                   edgecolor='none', alpha=0.7, zorder=2)
        shapes.update({info['NAME'] : p})

for country in countries:
    plt.gca().add_artist(shapes[country]) 


class PolygonN(object):
    def legend_artist(self, legend, orig_handle, fontsize, handlebox):
        x0, y0 = handlebox.xdescent, handlebox.ydescent
        width, height = handlebox.width, handlebox.height
        aspect= height/float(width)
        verts = orig_handle.get_xy()
        minx, miny = verts[:,0].min(), verts[:,1].min()
        maxx, maxy = verts[:,0].max(), verts[:,1].max()
        aspect= (maxy-miny)/float((maxx-minx))
        nvx = (verts[:,0]-minx)*float(height)/aspect/(maxx-minx)-x0
        nvy = (verts[:,1]-miny)*float(height)/(maxy-miny)-y0

        p = Polygon(np.c_[nvx, nvy])
        p.update_from(orig_handle)
        p.set_transform(handlebox.get_transform())

        handlebox.add_artist(p)
        return p

handles, labels = plt.gca().get_legend_handles_labels()
handles.extend([shapes[c] for c in countries])  
labels.extend(countries)     
plt.legend(handles=handles, labels=labels, handleheight=3, handlelength=3, framealpha=1.,
           handler_map={Polygon: PolygonN()} )

plt.show()
person ImportanceOfBeingErnest    schedule 07.03.2017
comment
У меня есть вопрос. Какая польза от переменной zorder? Или почему он используется здесь? - person SereneWizard; 07.03.2017
comment
Очень хороший вопрос. zorder определяет порядок отрисовки слоев, чем выше порядок, тем выше живет слой. Это как листы бумаги на столе - самый нижний имеет самый низкий зордер. Тем не менее, нет причин использовать его здесь, и его отсутствие делает формы живыми за береговыми линиями, которые могут выглядеть лучше. - person ImportanceOfBeingErnest; 07.03.2017
comment
В моей голове щелкнул вопрос. Вместо использования matplotlib.patches.Polygon() нельзя ли здесь использовать shapely.geometry.Polygon()? Цель по-прежнему состоит в том, чтобы получить многоугольник в легенде. - person SereneWizard; 08.03.2017
comment
Я предполагаю, что вы можете использовать shapely.geometry.Polygon, из которого вы можете создать descartes.patch.PolygonPatch. Этот патч можно добавить на холст matplotlib с помощью plt.gca().add_patch(). Однако я не могу проверить это, и мне также не ясно, какая от этого польза. - person ImportanceOfBeingErnest; 08.03.2017