Skip to content

Commit bdc1cca

Browse files
committed
Merge branch 'main' into doc/welcome-page
2 parents f2ca8ef + 891a4c5 commit bdc1cca

File tree

10 files changed

+1012
-244
lines changed

10 files changed

+1012
-244
lines changed

.github/workflows/deploy-book.yml

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,32 @@ on:
1919
jobs:
2020
deploy-book:
2121
runs-on: ubuntu-latest
22+
defaults:
23+
run:
24+
shell: bash -l {0}
25+
2226
steps:
23-
- uses: actions/[email protected]
27+
# Checkout current git repository
28+
- name: Checkout
29+
uses: actions/[email protected]
2430

25-
# Install dependencies
26-
- name: Set up Python 3.9
27-
uses: actions/setup-python@v3.1.2
31+
# Install Mambaforge with conda-forge dependencies
32+
- name: Setup Mambaforge
33+
uses: conda-incubator/setup-miniconda@v2.1.1
2834
with:
29-
python-version: 3.9
35+
activate-environment: egu22pygmt
36+
environment-file: environment.yml
37+
python-version: ${{ matrix.python-version }}
38+
channels: conda-forge
39+
channel-priority: strict
40+
miniforge-version: latest
41+
miniforge-variant: Mambaforge
42+
mamba-version: "*"
43+
use-mamba: true
3044

31-
- name: Install dependencies
32-
run: pip install -r book/requirements.txt
45+
# Show installed pkg information for postmortem diagnostic
46+
- name: List installed packages
47+
run: mamba list
3348

3449
# Build the book
3550
- name: Build the book

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# Data files
2+
*.laz
3+
*.nc
4+
*.png
5+
*.tif
6+
17
# Jupyter Book
28
/book/_build/
39

book/_toc.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ parts:
1212
url: https://github.com/GenericMappingTools/egu22pygmt
1313
- caption: 🧑‍🏫 Tutorials
1414
chapters:
15-
- file: markdown
16-
- file: notebooks
17-
- file: markdown-notebooks
1815
- file: first-figure
16+
- file: ecosystem
17+
- file: lidar_to_surface

book/ecosystem.ipynb

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"id": "b862b964-62fc-48b7-96db-d7f3b868ced1",
6+
"metadata": {
7+
"tags": []
8+
},
9+
"source": [
10+
"# Integration with the scientific Python ecosystem 🐍\n",
11+
"\n",
12+
"In this tutorial, we'll try out the integration between PyGMT and other common packages in the scientific Python ecosystem.\n",
13+
"\n",
14+
"\n",
15+
"Besides [pygmt](https://www.pygmt.org), we'll also be using:\n",
16+
"\n",
17+
"- [GeoPandas](https://geopandas.org/en/stable/) for managing geospatial tabular data\n",
18+
"- [Panel](https://panel.holoviz.org/index.html) for interactive visualizations\n",
19+
"- [Xarray](https://xarray.dev/) for managing n-dimensional labelled arrays\n"
20+
]
21+
},
22+
{
23+
"cell_type": "markdown",
24+
"id": "62d489cd-c901-42a2-b51a-9b21842b34df",
25+
"metadata": {},
26+
"source": [
27+
"## Plotting geospatial vector data with GeoPandas and PyGMT\n",
28+
"\n",
29+
"We'll extend the GeoPandas [Mapping and Plotting Tools Examples](https://geopandas.org/en/stable/docs/user_guide/mapping.html) to show how to create choropleth maps using PyGMT.\n",
30+
"\n",
31+
"**References**:\n",
32+
"\n",
33+
" - GeoPandas User Guide - https://geopandas.org/en/stable/docs/user_guide/"
34+
]
35+
},
36+
{
37+
"cell_type": "code",
38+
"execution_count": null,
39+
"id": "6436dfbb-be9c-4c2b-901a-a8fc7cde4ae8",
40+
"metadata": {},
41+
"outputs": [],
42+
"source": [
43+
"import pygmt\n",
44+
"import geopandas as gpd"
45+
]
46+
},
47+
{
48+
"cell_type": "markdown",
49+
"id": "a0f88acb-e463-4193-a7ef-33837b2f5fdf",
50+
"metadata": {},
51+
"source": [
52+
"We'll load sample data provided through the GeoPandas package and inspect the GeoDataFrame."
53+
]
54+
},
55+
{
56+
"cell_type": "code",
57+
"execution_count": null,
58+
"id": "8cf79e91-8ce0-48ec-8812-1395d3d0eddf",
59+
"metadata": {},
60+
"outputs": [],
61+
"source": [
62+
"world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))\n",
63+
"world.head()"
64+
]
65+
},
66+
{
67+
"cell_type": "markdown",
68+
"id": "65adae6a-3933-453b-bd4b-4e04e018d02d",
69+
"metadata": {},
70+
"source": [
71+
"Following the [GeoPandas example](https://geopandas.org/en/stable/docs/user_guide/mapping.html#choropleth-maps), we'll create a Choropleth map showing world population estimates, but will use PyGMT to plot the data using the [Hammer projection](https://www.pygmt.org/latest/projections/misc/misc_hammer.html#hammer)."
72+
]
73+
},
74+
{
75+
"cell_type": "code",
76+
"execution_count": null,
77+
"id": "76b850c4-e5bc-42bd-ba13-7b52b4f60dc2",
78+
"metadata": {},
79+
"outputs": [],
80+
"source": [
81+
"# Calculate the populations in millions per capita\n",
82+
"world = world[(world.pop_est>0) & (world.name!=\"Antarctica\")]\n",
83+
"world['pop_est'] = world.pop_est * 1e-6\n",
84+
"\n",
85+
"# Find the range of data values for creating a colormap\n",
86+
"cmap_bounds = pygmt.info(data=world['pop_est'], per_column=True)\n",
87+
"cmap_bounds"
88+
]
89+
},
90+
{
91+
"cell_type": "markdown",
92+
"id": "864091e5-85e2-4452-ab44-5cbeaa2ac8a9",
93+
"metadata": {},
94+
"source": [
95+
"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!"
96+
]
97+
},
98+
{
99+
"cell_type": "code",
100+
"execution_count": null,
101+
"id": "15a21a74-8531-4e74-86e1-a85e6eaef793",
102+
"metadata": {},
103+
"outputs": [],
104+
"source": [
105+
"# Create an instance of the pygmt.Figure class\n",
106+
"fig = pygmt.Figure()\n",
107+
"# Create a colormap for the figure\n",
108+
"pygmt.makecpt(cmap=\"bilbao\", series=cmap_bounds)\n",
109+
"# Create a basemap\n",
110+
"fig.basemap(region=\"d\", projection=\"H15c\", frame=True)\n",
111+
"# Plot the GeoDataFrame\n",
112+
"# - Use `close=True` to specify that the polygons should be forced closed\n",
113+
"# - Plot the polygon outlines with a 1 point, black pen\n",
114+
"# - Set that the color should be based on the `pop_est` using the `color, `cmap`, and `aspatial` parameters\n",
115+
"fig.plot(data=world, pen=\"1p,black\", close=True, color=\"+z\", cmap=True, aspatial=\"Z=pop_est\")\n",
116+
"# Add a colorbar\n",
117+
"fig.colorbar(position=\"JMR\", frame='a200+lPopulation (millions)')\n",
118+
"# Display the output\n",
119+
"fig.show()\n"
120+
]
121+
},
122+
{
123+
"cell_type": "markdown",
124+
"id": "b73a6666-6c2c-4f49-a92b-995ade576ccc",
125+
"metadata": {},
126+
"source": [
127+
"## Interactive data visualization with Xarray, Panel, and PyGMT"
128+
]
129+
},
130+
{
131+
"cell_type": "markdown",
132+
"id": "1bf15df6-6a4f-4221-af66-0db1c5b1e328",
133+
"metadata": {},
134+
"source": [
135+
"In this section, we'll create some interactive visualizations of oceanographic data!\n",
136+
"\n",
137+
"We'll use [Panel](https://panel.holoviz.org/index.html), which is a Python library\n",
138+
"for connecting interactive widgets with plots! We'll use Panel with\n",
139+
"[PyGMT](https://www.pygmt.org) and [xarray](https://www.xarray.dev) to visualize\n",
140+
"the objectively interpolated mean field for in-situ temperature from the World Ocean Atlas.\n",
141+
"\n",
142+
"**References**:\n",
143+
"\n",
144+
"- Temperature visualization based on https://rabernat.github.io/intro_to_physical_oceanography/02-c_ocean_temperature_salinity_stratification.html\n",
145+
"- Interactive setup based on https://github.com/weiji14/30DayMapChallenge2021/blob/main/day25_interactive.py\n",
146+
"- Data from the NOAA World Ocean Atlas, stored on the IRI Data Library at http://iridl.ldeo.columbia.edu/SOURCES/.NOAA/.NODC/.WOA09/."
147+
]
148+
},
149+
{
150+
"cell_type": "code",
151+
"execution_count": null,
152+
"id": "dbcff01f-fac0-4c1d-bb66-89dcb2e7711b",
153+
"metadata": {},
154+
"outputs": [],
155+
"source": [
156+
"import panel as pn\n",
157+
"import xarray as xr\n",
158+
"import pygmt\n",
159+
"pn.extension()\n"
160+
]
161+
},
162+
{
163+
"cell_type": "code",
164+
"execution_count": null,
165+
"id": "7b2f9cb8-add6-45c2-9f54-8e33ed9e6f02",
166+
"metadata": {},
167+
"outputs": [],
168+
"source": [
169+
"# Download the dataset from the IRI Data Library\n",
170+
"url = 'https://iridl.ldeo.columbia.edu/SOURCES/.NOAA/.NODC/.WOA09/.Grid-1x1/.Annual/.temperature/.t_an/data.nc'\n",
171+
"netcdf_file = pygmt.which(fname=url, download=True)\n",
172+
"woa_temp = xr.open_dataset(netcdf_file).isel(time=0)\n",
173+
"woa_temp"
174+
]
175+
},
176+
{
177+
"cell_type": "code",
178+
"execution_count": null,
179+
"id": "1c72a673",
180+
"metadata": {},
181+
"outputs": [],
182+
"source": [
183+
"# Make a static plot of sea surface temperature\n",
184+
"fig = pygmt.Figure()\n",
185+
"fig.grdimage(grid=woa_temp.t_an.sel(depth=0), cmap=\"vik\", projection=\"R15c\", frame=True)\n",
186+
"fig.show()"
187+
]
188+
},
189+
{
190+
"cell_type": "code",
191+
"execution_count": null,
192+
"id": "b11ea510-7540-475e-9217-be4204cb1ea3",
193+
"metadata": {},
194+
"outputs": [],
195+
"source": [
196+
"# Make a panel widget for controlling the depth plotted\n",
197+
"depth_slider = pn.widgets.DiscreteSlider(name='Depth (m)', options=woa_temp.depth.values.astype(int).tolist(), value=0)"
198+
]
199+
},
200+
{
201+
"cell_type": "code",
202+
"execution_count": null,
203+
"id": "3496d4db-92d9-45ee-9d48-6b4f00a19361",
204+
"metadata": {},
205+
"outputs": [],
206+
"source": [
207+
"# Make a function for plotting the depth slice with PyGMT\n",
208+
"\n",
209+
"@pn.depends(depth=depth_slider)\n",
210+
"def view(depth: int):\n",
211+
" fig = pygmt.Figure()\n",
212+
" pygmt.makecpt(cmap=\"vik\", series=[-2,30])\n",
213+
" fig.grdimage(grid=woa_temp.t_an.sel(depth=depth), cmap=True, projection=\"R15c\", frame=True)\n",
214+
" fig.colorbar(frame=\"a5\")\n",
215+
" return fig"
216+
]
217+
},
218+
{
219+
"cell_type": "markdown",
220+
"id": "d339433c-1c7a-4769-8986-edfb2a9897ed",
221+
"metadata": {},
222+
"source": [
223+
"### Make the interactive dashboard!\n",
224+
"\n",
225+
"Now to put everything together! The 'dashboard' will be very simple.\n",
226+
"The 'depth' slider is placed next to the map using `panel.Column`.\n",
227+
"Selecting different depths will update the data plotted! Find out more at\n",
228+
"https://panel.holoviz.org/getting_started/index.html#using-panel.\n",
229+
"\n",
230+
"Note: This is meant to run in a Jupyter lab/notebook environment.\n",
231+
"The grdinfo warning can be ignored."
232+
]
233+
},
234+
{
235+
"cell_type": "code",
236+
"execution_count": null,
237+
"id": "cc8d2f88-6e37-4ca4-9ebc-4bdf1e00ec8e",
238+
"metadata": {},
239+
"outputs": [],
240+
"source": [
241+
"pn.Column(depth_slider, view)"
242+
]
243+
}
244+
],
245+
"metadata": {
246+
"kernelspec": {
247+
"display_name": "Python 3 (ipykernel)",
248+
"language": "python",
249+
"name": "python3"
250+
},
251+
"language_info": {
252+
"codemirror_mode": {
253+
"name": "ipython",
254+
"version": 3
255+
},
256+
"file_extension": ".py",
257+
"mimetype": "text/x-python",
258+
"name": "python",
259+
"nbconvert_exporter": "python",
260+
"pygments_lexer": "ipython3",
261+
"version": "3.9.12"
262+
}
263+
},
264+
"nbformat": 4,
265+
"nbformat_minor": 5
266+
}

0 commit comments

Comments
 (0)