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()
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()
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()
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()