Skip to content

Commit 6b1bb74

Browse files
authored
Merge pull request #3 from ahuang11/refactor_notebooks
Refactor notebooks and add NASA GIBS explorer
2 parents 4c8d3d2 + 1314dd6 commit 6b1bb74

File tree

6 files changed

+583
-235
lines changed

6 files changed

+583
-235
lines changed

_toc.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ parts:
66
- file: notebooks/introduction.ipynb
77
- file: notebooks/web_map_services.ipynb
88
- file: notebooks/web_feature_services.ipynb
9+
- caption: Example Workflows
10+
chapters:
11+
- file: notebooks/nasa_earthdata_gibs_explorer.ipynb

environment.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ dependencies:
1414
- geoviews
1515
- panel
1616
- geopandas
17-
- hvplot
17+
- hvplot
18+
- owslib

notebooks/images/gibs.png

25.9 KB
Loading
Lines changed: 355 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,355 @@
1+
{
2+
"cells": [
3+
{
4+
"attachments": {},
5+
"cell_type": "markdown",
6+
"metadata": {},
7+
"source": [
8+
"<img src=\"images/gibs.png\" alt=\"NASA GIBS Logo\"></img>\n",
9+
"\n",
10+
"Image from [NASA Global Imagery Browse Services (GIBS) GitHub](https://github.com/nasa-gibs)"
11+
]
12+
},
13+
{
14+
"attachments": {},
15+
"cell_type": "markdown",
16+
"metadata": {},
17+
"source": [
18+
"# NASA Earthdata GIBS Explorer\n",
19+
"\n",
20+
"Global Imagery Browse Services (GIBS) provides quick access to over 1,000 satellite imagery products, covering every part of the world. Most imagery is updated daily—available within a few hours after satellite observation, and some products span almost 30 years.\n",
21+
"\n",
22+
"Below demos how to use OWSLib, Geoviews, and Panel effectively to create our own GIBS explorer.\n",
23+
"\n",
24+
"## Prerequisites\n",
25+
"\n",
26+
"The following packages are good to know, but not required.\n",
27+
"\n",
28+
"| Concepts | Importance | Notes |\n",
29+
"| --| --| --- |\n",
30+
"| [Intro to GeoViews](https://geoviews.org/) | Helpful | Geographic visualizations |\n",
31+
"| [Intro to Panel](https://panel.holoviz.org/) | Helpful | Dashboard creations |\n",
32+
"| [Intro to OWSLib](https://owslib.readthedocs.io/en/latest/usage.html) | Helpful | WMS URLs |\n",
33+
"\n",
34+
"- **Time to learn**: 15 minutes\n",
35+
"\n",
36+
"---"
37+
]
38+
},
39+
{
40+
"attachments": {},
41+
"cell_type": "markdown",
42+
"metadata": {},
43+
"source": [
44+
"## Imports\n",
45+
"\n",
46+
"Let’s first import a few packages.\n",
47+
"\n",
48+
"GeoViews is a Python library that facilitates the integration of WMS and other geospatial data sources with your own datasets. It provides a high-level interface for working with geographic data and simplifies the process of creating interactive visualizations.\n",
49+
"\n",
50+
"Pandas is a powerful Python library for data manipulation and analysis. It offers versatile data structures, such as Series and DataFrame, for working with structured data. However, here, we will only be using it to generate date time ranges.\n",
51+
"\n",
52+
"Panel is a Python library that offers a set of flexible and powerful tools for creating interactive dashboards and apps. It allows you to build custom user interfaces with interactive controls, widgets, and layout components, enabling rich interactivity for your visualizations and data analysis workflows.\n",
53+
"\n",
54+
"OWSLib is a Python library designed for client-side programming using the interface standards of the Open Geospatial Consortium (OGC) web services and their associated content models. Specifically, in this scenario, OWSLib will be utilized solely for the purpose of constructing URLs for WMS.\n",
55+
"\n",
56+
"The next line, `gv.extension(\"bokeh\")`, enables the Bokeh (interactive) plotting backend for GeoViews. GeoViews supports multiple plotting backends, such as Bokeh and Matplotlib, which allow you to choose the one that best suits your needs.\n",
57+
"\n",
58+
"Finally, `pn.extension()` initializes the panel library and sets up the necessary environment for creating interactive panels and dashboards. You may specify configurations like `sizing_mode=\"stretch_width\"` within `pn.extension()`."
59+
]
60+
},
61+
{
62+
"cell_type": "code",
63+
"execution_count": null,
64+
"metadata": {},
65+
"outputs": [],
66+
"source": [
67+
"import panel as pn\n",
68+
"import pandas as pd\n",
69+
"import geoviews as gv\n",
70+
"from owslib.wms import WebMapService\n",
71+
"\n",
72+
"gv.extension(\"bokeh\")\n",
73+
"pn.extension(sizing_mode=\"stretch_width\")"
74+
]
75+
},
76+
{
77+
"cell_type": "markdown",
78+
"metadata": {},
79+
"source": [
80+
"# Accessing GIBS\n",
81+
"\n",
82+
"Accessing NASA's GIBS (Global Imagery Browse Services) is well-documented, and you can find the documentation [here](https://nasa-gibs.github.io/gibs-api-docs/access-basics/).\n",
83+
"\n",
84+
"To access GIBS through the WMS (Web Map Service) endpoints, you can follow these steps:\n",
85+
"\n",
86+
"1. Find the WMS service endpoints by referring to the [service endpoints section](https://nasa-gibs.github.io/gibs-api-docs/access-basics/#service-endpoints_1) of the documentation. Look for the row that corresponds to the EPSG:3857 projection, as GeoViews currently supports that projection for tile services.\n",
87+
"\n",
88+
"2. Once you have identified the WMS service endpoint, copy one of the versions' [GetCapabilities URLs](https://gibs.earthdata.nasa.gov/wms/epsg3857/best/wms.cgi?SERVICE=WMS&REQUEST=GetCapabilities&VERSION=1.3.0). This URL provides information about the available layers and operations supported by the WMS service.\n",
89+
"\n",
90+
"3. Pass the GetCapabilities URL to the `WebMapService` class, which is a part of the `OWSLib` library. This class allows you to interact with the WMS service and retrieve the desired data.\n",
91+
"\n",
92+
"By following these steps, you will be able to access and work with the NASA GIBS data using the WMS service endpoints."
93+
]
94+
},
95+
{
96+
"cell_type": "code",
97+
"execution_count": null,
98+
"metadata": {},
99+
"outputs": [],
100+
"source": [
101+
"base_resource_url = \"https://gibs.earthdata.nasa.gov/wms/epsg3857/best/wms.cgi?SERVICE=WMS&REQUEST=GetCapabilities&VERSION=1.3.0\"\n",
102+
"\n",
103+
"wms = WebMapService(base_resource_url)"
104+
]
105+
},
106+
{
107+
"cell_type": "markdown",
108+
"metadata": {},
109+
"source": [
110+
"If we examine the contents, we can see that there are over a 1,000 layers (products) available!"
111+
]
112+
},
113+
{
114+
"cell_type": "code",
115+
"execution_count": null,
116+
"metadata": {},
117+
"outputs": [],
118+
"source": [
119+
"wms_contents = pd.Series(wms.contents)\n",
120+
"print(len(wms_contents))\n",
121+
"wms_contents.index"
122+
]
123+
},
124+
{
125+
"cell_type": "markdown",
126+
"metadata": {},
127+
"source": [
128+
"With a myriad of captivating options within your reach, why not embark on a journey of exploration and create your own interactive explorer?\n",
129+
"\n",
130+
"Now, you might be wondering, since there already exists an online explorer called [WorldView](https://worldview.earthdata.nasa.gov/), why bother reinventing the wheel? Well, here's the catch: by building your own explorer, you have the freedom to incorporate your own datasets into the mix!\n",
131+
"\n",
132+
"Not only does this provide a unique opportunity to personalize your exploration experience, but it's also a fantastic way to explore all the exciting options available while showcasing the incredible power of Python packages working in harmony!"
133+
]
134+
},
135+
{
136+
"cell_type": "code",
137+
"execution_count": null,
138+
"metadata": {},
139+
"outputs": [
140+
{
141+
"name": "stderr",
142+
"output_type": "stream",
143+
"text": [
144+
"No such comm: 90be3a54996442ad9c86f9abc1f60f70\n"
145+
]
146+
}
147+
],
148+
"source": [
149+
"BASE_URL = \"https://gibs.earthdata.nasa.gov/wms/epsg3857/best/wms.cgi?SERVICE=WMS\"\n",
150+
"XMIN = -20037507.539400\n",
151+
"YMIN = 1638517.444800\n",
152+
"XMAX = 20037260.918700\n",
153+
"YMAX = 7714669.39460\n",
154+
"\n",
155+
"\n",
156+
"class NasaEarthDataWmsExplorer:\n",
157+
" def __init__(self):\n",
158+
" self.wms = WebMapService(BASE_URL)\n",
159+
" layers = list(self.wms.contents)\n",
160+
"\n",
161+
" # create widgets\n",
162+
" self.layer_input = pn.widgets.Select(\n",
163+
" name=\"Layer\",\n",
164+
" options=layers,\n",
165+
" )\n",
166+
" self.time_slider = pn.widgets.DiscreteSlider(name=\"Time\", margin=(5, 16))\n",
167+
" self.coastline_feature = gv.feature.coastline().opts(\n",
168+
" global_extent=True, responsive=True\n",
169+
" )\n",
170+
" self.static_text = pn.widgets.StaticText()\n",
171+
" self.image_pane = pn.pane.Image()\n",
172+
" self.holoviews_pane = pn.pane.HoloViews(\n",
173+
" object=self.coastline_feature, min_height=500, sizing_mode=\"stretch_both\"\n",
174+
" )\n",
175+
"\n",
176+
" # add interactivity\n",
177+
" self.layer_input.param.watch(self.update_time, \"value\")\n",
178+
" self.time_slider.param.watch(self.update_web_map, \"value_throttled\")\n",
179+
"\n",
180+
" def update_time(self, event):\n",
181+
" layer = event.new\n",
182+
" time_positions = self.wms.contents[layer].timepositions\n",
183+
" if not time_positions:\n",
184+
" # use N/A instead of None to circumvent Panel from crashing\n",
185+
" # when going from time-dependent layer to time-independent layer\n",
186+
" options = [\"N/A\"]\n",
187+
" value = \"N/A\"\n",
188+
" else:\n",
189+
" ini, end, step = time_positions[0].split(\"/\")\n",
190+
" options = (\n",
191+
" pd.date_range(ini, end, freq=pd.Timedelta(step))\n",
192+
" .strftime(\"%Y-%m-%dT%H:%M:%SZ\")\n",
193+
" .tolist()\n",
194+
" )\n",
195+
" value = options[0]\n",
196+
" self.time_slider.param.set_param(options=options, value=value)\n",
197+
" self.update_web_map()\n",
198+
"\n",
199+
" def update_web_map(self, event=None):\n",
200+
" try:\n",
201+
" self.holoviews_pane.loading = True\n",
202+
" layer = self.layer_input.value\n",
203+
" time = self.time_slider.value\n",
204+
" if time == \"N/A\":\n",
205+
" time = None\n",
206+
"\n",
207+
" url = wms.getmap(\n",
208+
" layers=[layer],\n",
209+
" srs=\"EPSG:3857\",\n",
210+
" bbox=(XMIN, YMIN, XMAX, YMAX),\n",
211+
" size=(256, 256),\n",
212+
" format=\"image/png\",\n",
213+
" transparent=True,\n",
214+
" time=time,\n",
215+
" ).geturl()\n",
216+
" url_template = (\n",
217+
" url.replace(str(XMIN), \"{XMIN}\")\n",
218+
" .replace(str(YMIN), \"{YMIN}\")\n",
219+
" .replace(str(XMAX), \"{XMAX}\")\n",
220+
" .replace(str(YMAX), \"{YMAX}\")\n",
221+
" )\n",
222+
"\n",
223+
" layer_meta = self.wms[layer]\n",
224+
" self.image_pane.object = layer_meta.styles.get(\"default\", {}).get(\"legend\")\n",
225+
" self.static_text.value = layer_meta.abstract\n",
226+
" layer_imagery = gv.WMTS(url_template).opts(title=layer_meta.title)\n",
227+
"\n",
228+
" self.holoviews_pane.object = self.coastline_feature * layer_imagery\n",
229+
" finally:\n",
230+
" self.holoviews_pane.loading = False\n",
231+
"\n",
232+
" def view(self):\n",
233+
" widget_box = pn.WidgetBox(\n",
234+
" self.layer_input,\n",
235+
" self.time_slider,\n",
236+
" self.image_pane,\n",
237+
" self.static_text,\n",
238+
" pn.Spacer(sizing_mode=\"stretch_height\"),\n",
239+
" sizing_mode=\"stretch_both\",\n",
240+
" )\n",
241+
" return pn.Row(\n",
242+
" widget_box,\n",
243+
" self.holoviews_pane,\n",
244+
" )\n",
245+
"\n",
246+
"\n",
247+
"nasa_earth_data_wms_explorer = NasaEarthDataWmsExplorer()\n",
248+
"nasa_earth_data_wms_explorer.view()"
249+
]
250+
},
251+
{
252+
"cell_type": "markdown",
253+
"metadata": {},
254+
"source": [
255+
"The provided code allows users to interactively explore various layers of NASA Earth Data imagery.\n",
256+
"\n",
257+
"The `NasaEarthDataWmsExplorer` uses `WebMapService` from `OWSLib` ibrary to connect to the NASA Earth Data WMS service. The available layers are retrieved and displayed in a select widget.\n",
258+
"\n",
259+
"The explorer provides interactivity through `panel` widgets such as the layer selection dropdown and the time slider.\n",
260+
"\n",
261+
"Selecting a layer updates the available time positions for that layer, while changing the time position updates the displayed imagery accordingly. Metadata from the layer is also extracted and displayed below the widgets.\n",
262+
"\n",
263+
"The imagery is displayed using the GeoViews library, combined with a coastline feature.\n"
264+
]
265+
},
266+
{
267+
"attachments": {},
268+
"cell_type": "markdown",
269+
"metadata": {},
270+
"source": [
271+
"## Summary\n",
272+
"\n",
273+
"While the standalone nature of this custom-built explorer may not rival the capabilities of the existing WorldView explorer, its true potential lies in the ability to incorporate your own data. By integrating your unique datasets into this explorer, you can unlock a world of fascinating possibilities and create a truly captivating exploration experience.\n",
274+
"\n",
275+
"It is this integration of personal data that sets this explorer apart and makes it incredibly compelling and engaging!"
276+
]
277+
}
278+
],
279+
"metadata": {
280+
"kernelspec": {
281+
"display_name": "Python 3 (ipykernel)",
282+
"language": "python",
283+
"name": "python3"
284+
},
285+
"language_info": {
286+
"codemirror_mode": {
287+
"name": "ipython",
288+
"version": 3
289+
},
290+
"file_extension": ".py",
291+
"mimetype": "text/x-python",
292+
"name": "python",
293+
"nbconvert_exporter": "python",
294+
"pygments_lexer": "ipython3",
295+
"version": "3.11.4"
296+
},
297+
"nbdime-conflicts": {
298+
"local_diff": [
299+
{
300+
"diff": [
301+
{
302+
"diff": [
303+
{
304+
"key": 0,
305+
"op": "addrange",
306+
"valuelist": [
307+
"Python 3"
308+
]
309+
},
310+
{
311+
"key": 0,
312+
"length": 1,
313+
"op": "removerange"
314+
}
315+
],
316+
"key": "display_name",
317+
"op": "patch"
318+
}
319+
],
320+
"key": "kernelspec",
321+
"op": "patch"
322+
}
323+
],
324+
"remote_diff": [
325+
{
326+
"diff": [
327+
{
328+
"diff": [
329+
{
330+
"key": 0,
331+
"op": "addrange",
332+
"valuelist": [
333+
"Python3"
334+
]
335+
},
336+
{
337+
"key": 0,
338+
"length": 1,
339+
"op": "removerange"
340+
}
341+
],
342+
"key": "display_name",
343+
"op": "patch"
344+
}
345+
],
346+
"key": "kernelspec",
347+
"op": "patch"
348+
}
349+
]
350+
},
351+
"toc-autonumbering": false
352+
},
353+
"nbformat": 4,
354+
"nbformat_minor": 4
355+
}

0 commit comments

Comments
 (0)