Integration with the scientific Python ecosystem 🐍
Contents
Integration with the scientific Python ecosystem 🐍¶
In this tutorial, we’ll try out the integration between PyGMT and other common packages in the scientific Python ecosystem.
Besides pygmt, we’ll also be using:
GeoPandas for managing geospatial tabular data
Panel for interactive visualizations
Xarray for managing n-dimensional labelled arrays
Plotting geospatial vector data with GeoPandas and PyGMT¶
We’ll extend the GeoPandas Mapping and Plotting Tools Examples to show how to create choropleth maps using PyGMT.
References:
GeoPandas User Guide - https://geopandas.org/en/stable/docs/user_guide/
import pygmt
import geopandas as gpd
We’ll load sample data provided through the GeoPandas package and inspect the GeoDataFrame.
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
world.head()
pop_est | continent | name | iso_a3 | gdp_md_est | geometry | |
---|---|---|---|---|---|---|
0 | 920938 | Oceania | Fiji | FJI | 8374.0 | MULTIPOLYGON (((180.00000 -16.06713, 180.00000... |
1 | 53950935 | Africa | Tanzania | TZA | 150600.0 | POLYGON ((33.90371 -0.95000, 34.07262 -1.05982... |
2 | 603253 | Africa | W. Sahara | ESH | 906.5 | POLYGON ((-8.66559 27.65643, -8.66512 27.58948... |
3 | 35623680 | North America | Canada | CAN | 1674000.0 | MULTIPOLYGON (((-122.84000 49.00000, -122.9742... |
4 | 326625791 | North America | United States of America | USA | 18560000.0 | MULTIPOLYGON (((-122.84000 49.00000, -120.0000... |
Following the GeoPandas example, we’ll create a Choropleth map showing world population estimates, but will use PyGMT to plot the data using the Hammer projection.
# Calculate the populations in millions per capita
world = world[(world.pop_est>0) & (world.name!="Antarctica")]
world['pop_est'] = world.pop_est * 1e-6
# Find the range of data values for creating a colormap
cmap_bounds = pygmt.info(data=world['pop_est'], per_column=True)
cmap_bounds
array([1.40000000e-04, 1.37930277e+03])
Now, we’ll plot the data on a PyGMT figure, by creating a figure instance, laying down a basemap, plotting the GeoDataFrame, and adding a colorbar!
# Create an instance of the pygmt.Figure class
fig = pygmt.Figure()
# Create a colormap for the figure
pygmt.makecpt(cmap="bilbao", series=cmap_bounds)
# Create a basemap
fig.basemap(region="d", projection="H15c", frame=True)
# Plot the GeoDataFrame
# - Use `close=True` to specify that the polygons should be forced closed
# - Plot the polygon outlines with a 1 point, black pen
# - Set that the color should be based on the `pop_est` using the `color, `cmap`, and `aspatial` parameters
fig.plot(data=world, pen="1p,black", close=True, color="+z", cmap=True, aspatial="Z=pop_est")
# Add a colorbar
fig.colorbar(position="JMR", frame='a200+lPopulation (millions)')
# Display the output
fig.show()
/usr/share/miniconda3/envs/egu22pygmt/lib/python3.9/site-packages/geopandas/io/file.py:362: FutureWarning: pandas.Int64Index is deprecated and will be removed from pandas in a future version. Use pandas.Index with the appropriate dtype instead.
pd.Int64Index,
plot [ERROR]: OGR parsing incomplete (is file missing OGR statements?) - abort
Interactive data visualization with Xarray, Panel, and PyGMT¶
In this section, we’ll create some interactive visualizations of oceanographic data!
We’ll use Panel, which is a Python library for connecting interactive widgets with plots! We’ll use Panel with PyGMT and xarray to visualize the objectively interpolated mean field for in-situ temperature from the World Ocean Atlas.
References:
Temperature visualization based on https://rabernat.github.io/intro_to_physical_oceanography/02-c_ocean_temperature_salinity_stratification.html
Interactive setup based on https://github.com/weiji14/30DayMapChallenge2021/blob/main/day25_interactive.py
Data from the NOAA World Ocean Atlas, stored on the IRI Data Library at http://iridl.ldeo.columbia.edu/SOURCES/.NOAA/.NODC/.WOA09/.
import panel as pn
import xarray as xr
import pygmt
pn.extension()
# Download the dataset from the IRI Data Library
url = 'https://iridl.ldeo.columbia.edu/SOURCES/.NOAA/.NODC/.WOA09/.Grid-1x1/.Annual/.temperature/.t_an/data.nc'
netcdf_file = pygmt.which(fname=url, download=True)
woa_temp = xr.open_dataset(netcdf_file).isel(time=0)
woa_temp
<xarray.Dataset> Dimensions: (depth: 33, lat: 180, lon: 360) Coordinates: * depth (depth) float32 0.0 10.0 20.0 30.0 ... 4e+03 4.5e+03 5e+03 5.5e+03 * lat (lat) float32 -89.5 -88.5 -87.5 -86.5 -85.5 ... 86.5 87.5 88.5 89.5 * lon (lon) float32 0.5 1.5 2.5 3.5 4.5 ... 355.5 356.5 357.5 358.5 359.5 time datetime64[ns] 2008-01-01 Data variables: t_an (depth, lat, lon) float32 ...
# Make a static plot of sea surface temperature
fig = pygmt.Figure()
fig.grdimage(grid=woa_temp.t_an.sel(depth=0), cmap="vik", projection="R15c", frame=True)
fig.show()
grdinfo [WARNING]: Detected a data cube (/home/runner/work/egu22pygmt/egu22pygmt/book/data.nc) but -Q not set - skipping
# Make a panel widget for controlling the depth plotted
depth_slider = pn.widgets.DiscreteSlider(name='Depth (m)', options=woa_temp.depth.values.astype(int).tolist(), value=0)
# Make a function for plotting the depth slice with PyGMT
@pn.depends(depth=depth_slider)
def view(depth: int):
fig = pygmt.Figure()
pygmt.makecpt(cmap="vik", series=[-2,30])
fig.grdimage(grid=woa_temp.t_an.sel(depth=depth), cmap=True, projection="R15c", frame=True)
fig.colorbar(frame="a5")
return fig
Make the interactive dashboard!¶
Now to put everything together! The ‘dashboard’ will be very simple.
The ‘depth’ slider is placed next to the map using panel.Column
.
Selecting different depths will update the data plotted! Find out more at
https://panel.holoviz.org/getting_started/index.html#using-panel.
Note: This is meant to run in a Jupyter lab/notebook environment. The grdinfo warning can be ignored.
pn.Column(depth_slider, view)
grdinfo [WARNING]: Detected a data cube (/home/runner/work/egu22pygmt/egu22pygmt/book/data.nc) but -Q not set - skipping