Making Maps using Cartopy¶
Cartopy is a Python map plotting package. Combined with matplotlib is works well for making contour plots of maps for Climate Data Analysis
This notebook will demonstrate how to make map contour plots using Cartopy, including: 1. some stuff 2. some more stuff
Data¶
We return to our CMIP5 data for surface air temperature (tas) from the RCP8.5 scenario produced by the NCAR/CCSM4 model. For this example, we will again read the first ensemble member.
The data are located on the COLA severs in the following directory: /shared/cmip5/data/rcp45/atmos/mon/Amon/tas/NCAR.CCSM4/r1i1p1/
The filename is: tas_Amon_CCSM4_rcp45_r1i1p1_210101-229912.nc
[1]:
import warnings
import numpy as np
import xarray as xr
import pandas as pd
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.mpl.ticker as cticker
from cartopy.util import add_cyclic_point
Read Data
[2]:
path='/shared/cmip5/data/rcp45/atmos/mon/Amon/tas/NCAR.CCSM4/r1i1p1/'
fname='tas_Amon_CCSM4_rcp45_r1i1p1_200601-210012.nc'
ds=xr.open_dataset(path+fname)
ds
[2]:
<xarray.Dataset>
Dimensions: (bnds: 2, lat: 192, lon: 288, time: 1140)
Coordinates:
* time (time) object 2006-01-16 12:00:00 ... 2100-12-16 12:00:00
* lat (lat) float64 -90.0 -89.06 -88.12 -87.17 ... 88.12 89.06 90.0
* lon (lon) float64 0.0 1.25 2.5 3.75 5.0 ... 355.0 356.2 357.5 358.8
height float64 ...
Dimensions without coordinates: bnds
Data variables:
time_bnds (time, bnds) object ...
lat_bnds (lat, bnds) float64 ...
lon_bnds (lon, bnds) float64 ...
tas (time, lat, lon) float32 ...
Attributes:
institution: NCAR (National Center for Atmospheric Resea...
institute_id: NCAR
experiment_id: rcp45
source: CCSM4
model_id: CCSM4
forcing: Sl GHG Vl SS Ds SA BC MD OC Oz AA
parent_experiment_id: historical
parent_experiment_rip: r1i1p1
branch_time: 2005.0
contact: cesm_data@ucar.edu
references: Gent P. R., et.al. 2011: The Community Clim...
initialization_method: 1
physics_version: 1
tracking_id: 0bf35136-b266-44d2-9078-f3081b83b6ad
acknowledgements: The CESM project is supported by the Nation...
cesm_casename: b40.rcp4_5.1deg.001
cesm_repotag: ccsm4_0_beta49
cesm_compset: BRCP45CN
resolution: f09_g16 (0.9x1.25_gx1v6)
forcing_note: Additional information on the external forc...
processed_by: strandwg on mirage0 at 20111021
processing_code_information: Last Changed Rev: 428 Last Changed Date: 20...
product: output
experiment: RCP4.5
frequency: mon
creation_date: 2011-10-21T21:56:22Z
history: 2011-10-21T21:56:22Z CMOR rewrote data to c...
Conventions: CF-1.4
project_id: CMIP5
table_id: Table Amon (26 July 2011) 976b7fd1d9e1be31d...
title: CCSM4 model output prepared for CMIP5 RCP4.5
parent_experiment: historical
modeling_realm: atmos
realization: 1
cmor_version: 2.7.1Let’s take the mean temperature over the entire period for our plots
[3]:
ds_mean=ds.mean(dim='time')
Previously, in the read-netcdf notebook, we just used plt.contour from matplotlib, like this:
[4]:
plt.contourf(ds_mean['tas'])
plt.colorbar()
[4]:
<matplotlib.colorbar.Colorbar at 0x7f4c5501f710>
Plot with a map¶
However, we would like to plot this with map and control the map projection, label the lats and lons, etc.
[5]:
# Make the figure larger
fig = plt.figure(figsize=(11,8.5))
# Set the axes using the specified map projection
ax=plt.axes(projection=ccrs.PlateCarree())
# Make a filled contour plot
ax.contourf(ds['lon'], ds['lat'], ds_mean['tas'],
transform = ccrs.PlateCarree())
# Add coastlines
ax.coastlines()
[5]:
<cartopy.mpl.feature_artist.FeatureArtist at 0x7f4c54f48e10>
Cyclic data and lat-lon labels¶
This figure has a couple of things we would like to change: 1. The stripe at 0 lon. This is due to the fact that contourf has no way to know that our data is cyclic in longitude. We will fix this using cartopy.util.add_cyclic_point 2. No lat-lon labels. We will add lat-lon labels using set_x(y)ticks and cticker.
We set the lat-lon lables using set_x(y)ticks and cticker. We will fix the white strip using
[6]:
# Make the figure larger
fig = plt.figure(figsize=(11,8.5))
# Set the axes using the specified map projection
ax=plt.axes(projection=ccrs.PlateCarree())
# Add cyclic point to data
data=ds_mean['tas']
data, lons = add_cyclic_point(data, coord=ds['lon'])
# Make a filled contour plot
cs=ax.contourf(lons, ds['lat'], data,
transform = ccrs.PlateCarree())
# Add coastlines
ax.coastlines()
# Define the xticks for longitude
ax.set_xticks(np.arange(-180,181,60), crs=ccrs.PlateCarree())
lon_formatter = cticker.LongitudeFormatter()
ax.xaxis.set_major_formatter(lon_formatter)
# Define the yticks for latitude
ax.set_yticks(np.arange(-90,91,30), crs=ccrs.PlateCarree())
lat_formatter = cticker.LatitudeFormatter()
ax.yaxis.set_major_formatter(lat_formatter)
Change the Colormap¶
The colors are not very nice for plotting temperature contours. Let’s choose a different colormap and add a colorbar. The [colormap options] https://matplotlib.org/3.1.1/gallery/color/colormap_reference.html come from matplotlib. I will choose one called coolwarm
[7]:
# Make the figure larger
fig = plt.figure(figsize=(11,8.5))
# Set the axes using the specified map projection
ax=plt.axes(projection=ccrs.PlateCarree())
# Add cyclic point to data
data=ds_mean['tas']
data, lons = add_cyclic_point(data, coord=ds['lon'])
# Make a filled contour plot
cs=ax.contourf(lons, ds['lat'], data,
transform = ccrs.PlateCarree(),cmap='coolwarm',extend='both')
# Add coastlines
ax.coastlines()
# Define the xticks for longitude
ax.set_xticks(np.arange(-180,181,60), crs=ccrs.PlateCarree())
lon_formatter = cticker.LongitudeFormatter()
ax.xaxis.set_major_formatter(lon_formatter)
# Define the yticks for latitude
ax.set_yticks(np.arange(-90,91,30), crs=ccrs.PlateCarree())
lat_formatter = cticker.LatitudeFormatter()
ax.yaxis.set_major_formatter(lat_formatter)
# Add colorbar
cbar = plt.colorbar(cs)
Change the map projection¶
What if I want to use a different map projection? The various map projections can be found here https://scitools.org.uk/cartopy/docs/latest/crs/projections.html
[8]:
# Make the figure larger
fig = plt.figure(figsize=(11,8.5))
# Set the axes using the specified map projection
ax=plt.axes(projection=ccrs.Robinson())
# Add cyclic point to data
data=ds_mean['tas']
data, lons = add_cyclic_point(data, coord=ds['lon'])
# Make a filled contour plot
cs=ax.contourf(lons, ds['lat'], data,
transform = ccrs.PlateCarree(),cmap='coolwarm',extend='both')
# Add coastlines
ax.coastlines()
# Add gridlines
ax.gridlines()
# Add colorbar
cbar = plt.colorbar(cs,shrink=0.7,orientation='horizontal',label='Surface Air Temperature (K)')
# Add title
plt.title('NCAR-CCSM4 RCP4.5 2100-2299')
[8]:
Text(0.5, 1.0, 'NCAR-CCSM4 RCP4.5 2100-2299')