CARTOPY
Mapping the world with Python

Lakshmikanth Umesh Tipri, Pranav Magarde, Sai Srinivas Acholi

What is Cartopy?

Cartopy is a Python library designed for geospatial data processing and mapping. Cartopy makes use of the powerful PROJ, NumPy and Shapely libraries and includes a programmatic interface built on top of Matplotlib for the creation of publication quality maps. It is widely used in meteorology, oceanography, and environmental sciences for tasks like plotting weather patterns, climate data, and geographic visualizations.

Installation

Using Conda (Recommended)

If you have Anaconda or Miniconda installed, run:
conda install -c conda-forge cartopy
This will automatically handle all the system dependencies.

Using pip

Using pip to install will require you to download its dependencies (for example, pyproj and pyshp etc.)
pip install cartopy
Run the above command and setup the other system dependencies to ensure proper functioning of cartopy.

Key features

1. High-Quality Map Visualizations

Cartopy builds on Matplotlib to create publication-ready maps with customizable features:

For example, a map of Africa with its features is shown below

import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
        
def main():
  fig = plt.figure()
  ax = fig.add_subplot(1, 1, 1, projection=ccrs.PlateCarree())
  ax.set_extent([-20, 60, -40, 45], crs=ccrs.PlateCarree())
        
  ax.add_feature(cfeature.LAND)
  ax.add_feature(cfeature.OCEAN)
  ax.add_feature(cfeature.COASTLINE)
  ax.add_feature(cfeature.BORDERS, linestyle=':')
  ax.add_feature(cfeature.LAKES, alpha=0.5)
  ax.add_feature(cfeature.RIVERS)
        
  plt.show()
        
if __name__ == '__main__':
  main()

2. Map Projections

Map projections flatten the Earth’s 3D surface onto a 2D map. Cartopy supports a wide range of these:

3. Built-in Natural Earth Datasets

Cartopy includes Natural Earth datasets for quick and accurate mapping:

4. Geospatial Plotting

Plot various geospatial data types directly on maps:

For example, Delhi and the Ganges river are plotted on the map below

import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from shapely.geometry import LineString
        
ax = plt.axes(projection=ccrs.PlateCarree())
ax.set_extent([65, 100, 0, 40], crs=ccrs.PlateCarree())
        
ax.add_feature(cfeature.LAND)
ax.add_feature(cfeature.OCEAN)
ax.add_feature(cfeature.COASTLINE)
ax.add_feature(cfeature.BORDERS, linestyle=':')
        
ganges_lons = [78.57, 81.63, 83.23, 85.12, 87.03, 89.03]
ganges_lats = [25.45, 25.12, 25.75, 25.55, 24.90, 22.90]
        
ax.plot(ganges_lons, ganges_lats, color='blue',markersize=2, linewidth=1, marker='o', transform=ccrs.Geodetic(), label='Ganges River')
ax.plot(77.23, 28.61, 'r*', markersize=3, transform=ccrs.Geodetic(),label='Delhi')
plt.legend(loc='lower left')
plt.show()

5. Geometric Operations with Shapely

Cartopy integrates with Shapely for complex geometric operations like intersections, buffers, and unions:

6. Advanced Mapping Techniques

Cartopy supports advanced mapping techniques for enhanced geospatial visualizations:

For example, a custom map projection is shown below

import cartopy.crs as ccrs
import matplotlib.pyplot as plt
        
projection = ccrs.LambertConformal(central_longitude=-96, central_latitude=39)
fig, ax = plt.subplots(subplot_kw={'projection': projection})
ax.add_feature(cfeature.LAND)
ax.add_feature(cfeature.OCEAN)
ax.gridlines(draw_labels=True)
        
plt.show()

Applications and Use-cases

This tool can be used in various fields:

A few images from the net showing the plots made with cartopy

Image 1 Image 3 Image 2

Code Examples

Australia with its states and water bodies

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

# Projection Setup and setting extent
fig = plt.figure(figsize=(12, 14),dpi=150)
ax = plt.axes(projection=ccrs.PlateCarree())
ax.set_extent([110, 155, -45, -8], crs=ccrs.PlateCarree())

# Load the admin_1_states_provinces shapefile
shpfilename = shpreader.natural_earth(resolution='10m', category='cultural', name='admin_1_states_provinces')
reader = shpreader.Reader(shpfilename)
states = reader.records()

color = plt.get_cmap('Set1')
idx=0
for state in states:
    if state.attributes['admin'] == 'Australia':
        ax.add_geometries([state.geometry], ccrs.PlateCarree(), edgecolor='black', facecolor=color(idx),linewidth=0.5)
        idx+=1

# Add physical features and grid
ax.add_feature(cfeature.LAND, facecolor='lightgreen')
ax.add_feature(cfeature.OCEAN)
ax.add_feature(cfeature.RIVERS.with_scale('10m'), edgecolor='blue', linewidth=0.5)
ax.add_feature(cfeature.LAKES.with_scale('10m'), facecolor='blue')
ax.gridlines(draw_labels=True, linestyle='--', alpha=0.8)
plt.show()

Flight path from Mumbai to Toronto via Paris

import cartopy.crs as ccrs
import cartopy.feature as cfeature
import matplotlib.pyplot as plt

# Airport coordinates (Longitude, Latitude)
airports = {
    "Mumbai": (72.8656, 19.0896),
    "Paris": (2.55, 49.00),
    "Toronto": (-79.6306, 43.6777)
}

#Projection Setup
fig = plt.figure(figsize=(15, 6), dpi=300)
ax = plt.axes(projection=ccrs.PlateCarree())

#Add map features
ax.add_feature(cfeature.BORDERS, linestyle=":")
ax.add_feature(cfeature.LAND, facecolor='green')
ax.add_feature(cfeature.OCEAN)
ax.gridlines(draw_labels=True)

#Extract and plot
lons, lats = zip(*airports.values())

ax.plot(lons, lats, linewidth=2, c='r', marker='o', transform=ccrs.Geodetic())

for city, (lon, lat) in airports.items():
    ax.text(lon-4, lat+1, city, fontsize=12, fontweight='bold', transform=ccrs.PlateCarree())

plt.show()

States affected by Hurricane Katrina(taken from official documentation of Cartopy)

import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
import shapely.geometry as sgeom

import cartopy.crs as ccrs
import cartopy.io.shapereader as shpreader

"""
The data was originally sourced from the HURDAT2 dataset from AOML/NOAA:
https://www.aoml.noaa.gov/hrd/hurdat/newhurdat-all.html on 14th Dec 2012.

"""
lons = [-75.1, -75.7, -76.2, -76.5, -76.9, -77.7, -78.4, -79.0,
        -79.6, -80.1, -80.3, -81.3, -82.0, -82.6, -83.3, -84.0,
        -84.7, -85.3, -85.9, -86.7, -87.7, -88.6, -89.2, -89.6,
        -89.6, -89.6, -89.6, -89.6, -89.1, -88.6, -88.0, -87.0,
        -85.3, -82.9]

lats = [23.1, 23.4, 23.8, 24.5, 25.4, 26.0, 26.1, 26.2, 26.2, 26.0,
        25.9, 25.4, 25.1, 24.9, 24.6, 24.4, 24.4, 24.5, 24.8, 25.2,
        25.7, 26.3, 27.2, 28.2, 29.3, 29.5, 30.2, 31.1, 32.6, 34.1,
        35.6, 37.0, 38.6, 40.1]

fig = plt.figure()
# to get the effect of having just the states without a map "background"
# turn off the background patch and axes frame
ax = fig.add_axes([0, 0, 1, 1], projection=ccrs.LambertConformal(), frameon=False)
ax.patch.set_visible(False)

ax.set_extent([-125, -66.5, 20, 50], ccrs.Geodetic())

shapename = 'admin_1_states_provinces_lakes'
states_shp = shpreader.natural_earth(resolution='110m',
                                      category='cultural', name=shapename)

ax.set_title('US States which intersect the track of '
              'Hurricane Katrina (2005)')

# turn the lons and lats into a shapely LineString
track = sgeom.LineString(zip(lons, lats))

# buffer the linestring by two degrees (note: this is a non-physical distance)
track_buffer = track.buffer(2)

def colorize_state(geometry):
    facecolor = (0.9375, 0.9375, 0.859375)
    if geometry.intersects(track):
        facecolor = 'red'
    elif geometry.intersects(track_buffer):
        facecolor = '#FF7E00'
    return {'facecolor': facecolor, 'edgecolor': 'black'}

ax.add_geometries(
    shpreader.Reader(states_shp).geometries(),
    ccrs.PlateCarree(),
    styler=colorize_state)

ax.add_geometries([track_buffer], ccrs.PlateCarree(),
                  facecolor='#C8A2C8', alpha=0.5)
ax.add_geometries([track], ccrs.PlateCarree(),
                  facecolor='none', edgecolor='k')

# make two proxy artists to add to a legend
direct_hit = mpatches.Rectangle((0, 0), 1, 1, facecolor="red")
within_2_deg = mpatches.Rectangle((0, 0), 1, 1, facecolor="#FF7E00")
labels = ['State directly intersects\nwith track',
          'State is within \n2 degrees of track']
ax.legend([direct_hit, within_2_deg], labels,
          loc='lower left', bbox_to_anchor=(0.025, -0.1), fancybox=True)

plt.show()

Why use Cartopy?

Whether you're working in oceanography, environmental science, navigation, or any field that requires geographic data visualization, Cartopy offers the flexibility and precision needed to create high-quality maps with ease.

Cartopy is the ideal python library for advanced map projections of high quality due to its accuracy and integration with other libraries. It provides you a lot of in-built features which makes mapping easier and user-friendly. Cartopy is especially used for large areas/small scale data where the spherical data usually breaks down. It is preferred by meteorologist and climatologist for scientific purposes.

References

Official Documentation
Plotting with Cartopy and Geopandas