¿Cómo poner una etiqueta en un país con carton Python?

8

Usando python3 y cartopy, teniendo 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()

Entiendo esto:

ingrese la descripción de la imagen aquí

Pregunta: ¿Qué debo escribir para obtener una " A " roja sobre Bulgaria (o cualquier otro país al que me refiero country.attributes['SOVEREIGNT'])? Actualmente, la etiqueta no se muestra en absoluto y no estoy seguro de cómo cambiar la fuente de la etiqueta. Por lo tanto, parece que lo siguiente solo cambia el color, sin agregar la etiqueta:

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

Respuestas:

9

Puede recuperar el centroide de la geometría y trazar el texto en esa ubicación:

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'])

Con el alcance centrado en "Bulgaria", parece que:

ingrese la descripción de la imagen aquí

editar:

Para separar las "dependencias", considere usar el en admin_0_map_unitslugar de admin_0_map_countries, consulte la documentación de Natural Earth .

Para resaltar pequeños países / regiones, puede agregar un búfer a la geometría con 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'])

ingrese la descripción de la imagen aquí

Podría dividir un país específico con algo como esto. Utiliza Shapely para realizar una intersección en el centro de la geometría. En última instancia, podría ser "más limpio" separar el trazado y el análisis espacial (división, etc.) en pasos más distintos. Mezclarlo así probablemente haga que sea más difícil reutilizar el código para otros 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'])

ingrese la descripción de la imagen aquí

Rutger Kassies
fuente
Definitivamente una buena respuesta, ¡gracias por los esfuerzos! Y como una pregunta completamente no relacionada: si quiero mapear Singapur o Lichtenstein en verde, ¿qué debo hacer?
Vityata
1
He hecho una edición a la respuesta. La imagen muestra el polígono con un búfer de 2 grados si el área es inferior a 2 grados cuadrados (no es la mejor unidad para el mapeo). Agregué un desplazamiento a la etiqueta para evitar sobre trazar el país en sí.
Rutger Kassies
Logré filtrar los territorios dependientes marcando el country.attributes['NAME_EN'] == "France". De todos modos, con respecto a "Singapur" y "Lichtenstein", supongo que debería tratar de dar coordenadas manuales.
Vityata
1
Esos países van a ser muy pequeños en un mapa grande, pero creo que aún podría usar el mismo concepto para hacerlo.
Rutger Kassies
1
Noté este comentario en los documentos: "Si desea ver las regiones dependientes en el extranjero desglosadas (como en los códigos ISO, vea Francia, por ejemplo), use unidades de mapa en su lugar". y pensé que podría ser más apropiado en este caso. Está en la parte "acerca de" en: naturalearthdata.com/downloads/10m-cultural-vectors/…
Rutger Kassies