Plotting country labels with cartopy

Question:

I am trying to create a map overview using cartopy, but for some reason cannot figure out how to plot the names of the countries shown on the map.

My function looks like this:

import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER

def plot_southwest_indian_ocean():
    # Define the latitude and longitude ranges for the Southwest Indian Ocean basin
    lat_range = [-40, 0]
    lon_range = [-340, -260]

    # Create a figure and axis object with a desired projection
    fig, ax = plt.subplots(figsize=(8, 6), subplot_kw={'projection': ccrs.PlateCarree()})

    # Set the map boundaries
    ax.set_extent(lon_range + lat_range, crs=ccrs.PlateCarree())

    # Add land and ocean features
    ax.add_feature(cfeature.LAND, edgecolor='black')
    ax.add_feature(cfeature.OCEAN)

    # Add country borders
    ax.add_feature(cfeature.BORDERS, linestyle='-', alpha=0.5)

    # Add country labels
    countries = cfeature.NaturalEarthFeature(
        category='cultural',
        name='admin_0_countries',
        scale='50m',
        facecolor='none',
        edgecolor='gray'
    )
    ax.add_feature(countries, linewidth=0.5)

    country_name = 'Mozambique'
    country_boundaries = [country for country in countries.geometries() if country.properties['ADMIN'] == country_name]

    for boundary in country_boundaries:
        centroid = boundary.centroid
        ax.annotate(
            country_name,
            xy=(centroid.x, centroid.y),
            xytext=(5, 5),
            textcoords='offset points',
            ha='left',
            va='bottom',
            fontsize=8,
            color='black',
            bbox=dict(boxstyle='round,pad=0.2', fc='white', ec='gray', lw=0.5)
        )

    # Add gridlines
    gl = ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=True, linewidth=1, color='gray', alpha=0.5, linestyle='--')
    gl.ylabels_right = False
    gl.xformatter = LONGITUDE_FORMATTER
    gl.yformatter = LATITUDE_FORMATTER
    gl.xlabel_style = {'size': 12, 'color': 'gray'}
    gl.ylabel_style = {'size': 12, 'color': 'gray'}

    # Add a title to the plot
    ax.set_title("Southwest Indian Ocean Basin")

    # Show the plot
    plt.show()

I was trying to test it out with Mozambique, but continue to run into errors if I try to plot one country label or all of them. Ideally all within this map extent would be plotted. This is the map extent without any attempt at country labels:

def plot_southwest_indian_ocean():
    # Define the latitude and longitude ranges for the Southwest Indian Ocean basin
    lat_range = [-40, 0]
    lon_range = [-340, -260]

    # Create a figure and axis object with a desired projection
    fig, ax = plt.subplots(figsize=(8, 6), subplot_kw={'projection': ccrs.PlateCarree()})

    # Set the map boundaries
    ax.set_extent(lon_range + lat_range, crs=ccrs.PlateCarree())

    # Add land and ocean features
    ax.add_feature(cfeature.LAND, edgecolor='black')
    ax.add_feature(cfeature.OCEAN)

    # Add country borders
    ax.add_feature(cfeature.BORDERS, linestyle='-', alpha=0.5)


    # Add gridlines
    gl = ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=True, linewidth=1, color='gray', alpha=0.5, linestyle='--')
    gl.ylabels_right = False
    gl.xformatter = LONGITUDE_FORMATTER
    gl.yformatter = LATITUDE_FORMATTER
    gl.xlabel_style = {'size': 12, 'color': 'gray'}
    gl.ylabel_style = {'size': 12, 'color': 'gray'}

    # Add a title to the plot
    ax.set_title("Southwest Indian Ocean Basin")

    # Show the plot
    plt.show()

enter image description here

Asked By: MateaMar

||

Answers:

The line with if country.properties['ADMIN'] == country_name returns an error because country has no attribute properties.

From How to put a label on a country with Python cartopy?, I managed to produce this code that should do what you want:

import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
import cartopy.io.shapereader as shpreader

# Define the latitude and longitude ranges for the Southwest Indian Ocean basin
lat_range = [-40, 0]
lon_range = [-340, -260]

# Create a figure and axis object with a desired projection
fig, ax = plt.subplots(figsize=(8, 6), subplot_kw={'projection': ccrs.PlateCarree()})

# Set the map boundaries
ax.set_extent(lon_range + lat_range, crs=ccrs.PlateCarree())

# Add land and ocean features
ax.add_feature(cfeature.LAND, edgecolor='black')
ax.add_feature(cfeature.OCEAN)

# Add country borders
ax.add_feature(cfeature.BORDERS, linestyle='-', alpha=0.5)
shpfilename = shpreader.natural_earth(resolution='50m',
                                      category='cultural',
                                      name='admin_0_countries')
reader = shpreader.Reader(shpfilename)
countries = reader.records()
country_names = ['Mozambique', 'Madagascar']
for country in countries:
    ax.add_geometries(country.geometry, ccrs.PlateCarree(), edgecolor='#888888', facecolor='#EFEFDB')
    if country.attributes['NAME'] in country_names:
        x = country.geometry.centroid.x        
        y = country.geometry.centroid.y
        ax.annotate(country.attributes['NAME'], 
                    xy=(x, y), 
                    xytext=(5, 5),
                    textcoords='offset points',
                    ha='left', 
                    va='bottom', 
                    fontsize=8,
                    color='black',
                    bbox=dict(boxstyle='round,pad=0.2', fc='white', ec='gray', lw=0.5))
         
# Add gridlines
gl = ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=True, linewidth=1, color='gray', alpha=0.5, linestyle='--')
gl.ylabels_right = False
gl.xformatter = LONGITUDE_FORMATTER
gl.yformatter = LATITUDE_FORMATTER
gl.xlabel_style = {'size': 12, 'color': 'gray'}
gl.ylabel_style = {'size': 12, 'color': 'gray'}

# Add a title to the plot
ax.set_title("Southwest Indian Ocean Basin")

# Show the plot
plt.show()

enter image description here

Answered By: TVG