Como colocar um rótulo em um país com Python cartopy?

8

Usando python3 e cartopy, com este código:

import matplotlib.pyplot as plt
import cartopy
import cartopy.io.shapereader as shpreader
import cartopy.crs as ccrs

ax = plt.axes(projection=ccrs.PlateCarree())
ax.add_feature(cartopy.feature.LAND)
ax.add_feature(cartopy.feature.OCEAN)
ax.add_feature(cartopy.feature.COASTLINE)
ax.add_feature(cartopy.feature.BORDERS, linestyle='-', alpha=.5)
ax.add_feature(cartopy.feature.LAKES, alpha=0.95)
ax.add_feature(cartopy.feature.RIVERS)

ax.set_extent([-150, 60, -25, 60])

shpfilename = shpreader.natural_earth(resolution='110m',
                                      category='cultural',
                                      name='admin_0_countries')

reader = shpreader.Reader(shpfilename)
countries = reader.records()

for country in countries:
    if country.attributes['SOVEREIGNT'] == "Bulgaria":
        ax.add_geometries(country.geometry, ccrs.PlateCarree(), facecolor=(0, 1, 0), label = "A")
    else:
        ax.add_geometries(country.geometry, ccrs.PlateCarree(), facecolor=(1, 1, 1), label = country.attributes['SOVEREIGNT'])
plt.rcParams["figure.figsize"] = (50,50)
plt.show()

Eu entendi isso:

insira a descrição da imagem aqui

Pergunta: O que devo escrever para obter um " A " vermelho sobre a Bulgária (ou qualquer outro país ao qual me refiro country.attributes['SOVEREIGNT'])? Atualmente, o rótulo não é exibido e não sei como alterar a fonte do rótulo. Assim, parece que o seguinte altera apenas a cor, sem adicionar o rótulo:

ax.add_geometries(country.geometry, ccrs.PlateCarree(), facecolor=(0, 1, 0), label = "A")
Vityata
fonte

Respostas:

9

Você pode recuperar o centróide da geometria e plotar o texto nesse local:

import matplotlib.patheffects as PathEffects

for country in countries:

    if country.attributes['SOVEREIGNT'] == "Bulgaria":
        g = ax.add_geometries(country.geometry, ccrs.PlateCarree(), facecolor=(0, 1, 0), label="A")

        x = country.geometry.centroid.x        
        y = country.geometry.centroid.y

        ax.text(x, y, 'A', color='red', size=15, ha='center', va='center', transform=ccrs.PlateCarree(), 
                path_effects=[PathEffects.withStroke(linewidth=5, foreground="k", alpha=.8)])

    else:
        ax.add_geometries(country.geometry, ccrs.PlateCarree(), facecolor=(1, 1, 1), label = country.attributes['SOVEREIGNT'])

Com a extensão focada na "Bulgária", parece:

insira a descrição da imagem aqui

editar:

Para separar as "dependências", considere usar o em admin_0_map_unitsvez de admin_0_map_countries, consulte a documentação do Natural Earth .

Para destacar pequenos países / regiões, você pode adicionar um buffer à geometria com algo como:

highlight = ['Singapore', 'Liechtenstein']

for country in countries:

    if country.attributes['NAME'] in highlight:

        if country.geometry.area < 2:
            geom = [country.geometry.buffer(2)]
        else:
            geom = [country.geometry]

        g = ax.add_geometries(geom, ccrs.PlateCarree(), facecolor=(0, 0.5, 0, 0.6), label="A", zorder=99)

        x = country.geometry.centroid.x        
        y = country.geometry.centroid.y

        ax.text(x, y+5, country.attributes['NAME'], color='red', size=14, ha='center', va='center', transform=ccrs.PlateCarree(), 
                path_effects=[PathEffects.withStroke(linewidth=3, foreground="k", alpha=.8)])

    else:
        ax.add_geometries(country.geometry, ccrs.PlateCarree(), facecolor=(1, 1, 1), label=country.attributes['NAME'])

insira a descrição da imagem aqui

Você pode dividir um país específico com algo assim: ele usa Shapely para executar uma interseção no meio da geometria. Por fim, pode ser "mais limpo" separar a plotagem e a análise espacial (divisão etc.) em etapas mais distintas. Misturar dessa maneira provavelmente torna mais difícil reutilizar o código para outros casos.

from shapely.geometry import LineString, MultiLineString

for country in countries:

    if country.attributes['NAME'] in 'China':

        # line at the centroid y-coord of the country
        l = LineString([(-180, country.geometry.centroid.y), 
                        (180, country.geometry.centroid.y)])

        north_poly = MultiLineString([l, north_line]).convex_hull
        south_poly = MultiLineString([l, south_line]).convex_hull

        g = ax.add_geometries([country.geometry.intersection(north_poly)], ccrs.PlateCarree(), facecolor=(0.8, 0.0, 0.0, 0.4), zorder=99)
        g = ax.add_geometries([country.geometry.intersection(south_poly)], ccrs.PlateCarree(), facecolor=(0.0, 0.0, 0.8, 0.4), zorder=99)

        x = country.geometry.centroid.x        
        y = country.geometry.centroid.y

        ax.text(x, y, country.attributes['NAME'], color='k', size=16, ha='center', va='center', transform=ccrs.PlateCarree(), 
                path_effects=[PathEffects.withStroke(linewidth=5, foreground="w", alpha=1)], zorder=100)

    else:
        ax.add_geometries(country.geometry, ccrs.PlateCarree(), facecolor=(1, 1, 1), label=country.attributes['NAME'])

insira a descrição da imagem aqui

Rutger Kassies
fonte
Definitivamente uma boa resposta, obrigado pelos esforços! E como uma questão completamente não relacionada - se eu quiser mapear Cingapura ou Lichtenstein em verde, o que devo fazer?
Vityata 12/02
1
Fiz uma edição da resposta. A imagem mostra o polígono com um buffer de 2 graus se a área for menor que 2 graus ao quadrado (não é a melhor unidade para mapeamento). Eu adicionei um deslocamento ao rótulo para evitar sobrecarregar o próprio país.
Rutger Kassies
Consegui filtrar os territórios dependentes, verificando o country.attributes['NAME_EN'] == "France". Enfim, em relação a "Cingapura" e "Lichtenstein", acho que devo tentar dar coordenadas manuais?
Vityata 12/02
1
Esses países serão muito pequenos em um mapa grande, mas acho que você ainda pode usar o mesmo conceito para fazê-lo.
Rutger Kassies
1
Notei esta observação nos documentos: "Se você deseja ver as regiões dependentes do exterior divididas (como nos códigos ISO, veja a França, por exemplo), use unidades de mapa." e pensei que poderia ser mais apropriado neste caso. Está na parte "sobre" em: naturalearthdata.com/downloads/10m-cultural-vectors/…
Rutger Kassies