The Geostationary Lightning Mapper (GLM) is a single-channel, near-infrared optical transient detector that can detect the momentary changes in an optical scene, indicating the presence of lightning. GLM measures total lightning (in-cloud, cloud-to-cloud and cloud-to-ground) activity continuously over the Americas and adjacent ocean regions with near-uniform spatial resolution of approximately 10 km. GLM collects information such as the frequency, location and extent of lightning discharges to identify intensifying thunderstorms and tropical cyclones. Trends in total lightning available from the GLM provide critical information to forecasters, allowing them to focus on developing severe storms much earlier and before these storms produce damaging winds, hail or even tornadoes.
The GLM data product consists of a hierarchy of earth-located lightning radiant energy measures including events, groups, and flashes:
The product includes the relationship among lightning events, groups, and flashes, and the area coverage of lightning groups and flashes. The product also includes processing and data quality metadata, and satellite state and location information.
The NetCDF files are delivered to Azure as part of the NOAA Open Data Dissemination (NODD) Program.
This notebook works with or without an API key, but you will be given more permissive access to the data with an API key. The Planetary Computer Hub sets the environment variable "PC_SDK_SUBSCRIPTION_KEY" when your server is started. The API key may be manually set via the following code:
pc.settings.set_subscription_key(<YOUR API Key>)
The datasets hosted by the Planetary Computer are available from Azure Blob Storage. We'll use pystac-client to search the Planetary Computer's STAC API for the subset of the data that we care about, and then we'll load the data directly from Azure Blob Storage. We'll specify a modifier
so that we can access the data stored in the Planetary Computer's private Blob Storage Containers. See Reading from the STAC API and Using tokens for data access for more.
import planetary_computer
import pystac_client
import rich.table
# Open the Planetary Computer STAC API
catalog = pystac_client.Client.open(
"https://planetarycomputer.microsoft.com/api/stac/v1/",
modifier=planetary_computer.sign_inplace,
)
The GOES GLM product is typically queried by datetime and the "GOES-16" and "GOES-17" platforms. Each item has an identical spatial footprint.
# Fetch the collection of interest and display available items
search = catalog.search(
collections="goes-glm",
datetime="2022-10-26T00:02:50Z",
query={"platform": {"eq": "GOES-17"}},
)
items = list(search.get_items())
items
[<Item id=OR_GLM-L2-LCFA_G17_s20222990002400_e20222990003000>]
item = items[0]
table = rich.table.Table("Property", "Value")
for key, value in sorted(item.properties.items()):
table.add_row(key, str(value))
table
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ Property ┃ Value ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩ │ constellation │ GOES │ │ datetime │ 2022-10-26T00:02:50Z │ │ end_datetime │ 2022-10-26T00:03:00.0Z │ │ goes:event_count │ 1110 │ │ goes:flash_count │ 34 │ │ goes:flash_time_threshold │ 3.3299999237060547 │ │ goes:group_count │ 523 │ │ goes:group_time_threshold │ 0.0 │ │ goes:lightning_wavelength │ 777.3699951171875 │ │ goes:nominal_satellite_height │ 35786.0234375 │ │ goes:nominal_satellite_subpoint_lat │ 0.0 │ │ goes:nominal_satellite_subpoint_lon │ -137.1999969482422 │ │ goes:orbital_slot │ West │ │ goes:percent_navigated_L1b_events │ 1.0 │ │ goes:percent_uncorrectable_L0_errors │ 0.0 │ │ goes:system_environment │ OR │ │ goes:yaw_flip_flag │ 2 │ │ gsd │ 8000 │ │ instruments │ ['FM2'] │ │ mission │ GOES │ │ platform │ GOES-17 │ │ processing:facility │ WCDAS │ │ processing:level │ L2 │ │ proj:centroid │ {'lat': 0.0, 'lon': -137.0} │ │ proj:epsg │ 4326 │ │ start_datetime │ 2022-10-26T00:02:40.0Z │ └──────────────────────────────────────┴─────────────────────────────┘
The single asset is a NetCDF file, which can be opened by libraries like xarray.
import xarray as xr
import fsspec
ds = xr.open_dataset(fsspec.open(item.assets["netcdf"].href).open())
ds
<xarray.Dataset> Dimensions: (number_of_events: 1110, number_of_groups: 523, number_of_flashes: 34, number_of_time_bounds: 2, number_of_wavelength_bounds: 2, number_of_field_of_view_bounds: 2) Coordinates: (12/21) event_id (number_of_events) uint32 9759000... event_time_offset (number_of_events) datetime64[ns] ... event_lat (number_of_events) float64 15.14 ... event_lon (number_of_events) float64 -91.25... event_parent_group_id (number_of_events) uint32 4131682... group_id (number_of_groups) uint32 4131682... ... ... product_time datetime64[ns] 2022-10-26T00:02:40 lightning_wavelength float32 777.4 group_time_threshold float32 0.0 flash_time_threshold float32 3.33 lat_field_of_view float32 0.0 lon_field_of_view float32 -137.0 Dimensions without coordinates: number_of_events, number_of_groups, number_of_flashes, number_of_time_bounds, number_of_wavelength_bounds, number_of_field_of_view_bounds Data variables: (12/27) event_energy (number_of_events) float32 1.655e... group_frame_time_offset (number_of_groups) datetime64[ns] ... group_area (number_of_groups) float32 5.32e+... group_energy (number_of_groups) float32 1.438e... group_quality_flag (number_of_groups) float32 0.0 ..... flash_frame_time_offset_of_first_event (number_of_flashes) datetime64[ns] ... ... ... nominal_satellite_subpoint_lon float64 -137.2 lon_field_of_view_bounds (number_of_field_of_view_bounds) float32 ... percent_uncorrectable_L0_errors float64 0.0 algorithm_dynamic_input_data_container int32 -2147483647 processing_parm_version_container int32 -2147483647 algorithm_product_version_container int32 -2147483647 Attributes: (12/29) production_site: WCDAS featureType: point dataset_name: OR_GLM-L2-LCFA_G17_s20222990002400_e2022299000... naming_authority: gov.nesdis.noaa Conventions: CF-1.7 institution: DOC/NOAA/NESDIS> U.S. Department of Commerce, ... ... ... time_coverage_start: 2022-10-26T00:02:40.0Z time_coverage_end: 2022-10-26T00:03:00.0Z production_data_source: Realtime production_environment: OE LUT_Filenames: GLM_CALINR_AllFilters(FM2W_CDRL79RevD_PR_09_00... id: 6ecd089c-b52d-473b-a63e-f4ed30132de3
The three types of data (events, groups, and flashes) are embedded in this single NetCDF file. You might want to convert one of those to a tabular format for analysis or visualization. In this example, we'll extract the group
variables.
import geopandas
df = geopandas.GeoDataFrame(
{
"energy": ds.group_energy,
"area": ds.group_area,
"time_offset": ds.group_time_offset,
"frame_time_offset": ds.group_frame_time_offset,
"quality_flag": ds.group_quality_flag,
"parent_flash_id": ds.group_parent_flash_id,
},
index=ds.group_id,
geometry=geopandas.points_from_xy(ds.group_lon, ds.group_lat, crs="EPSG:4326"),
)
df.head()
energy | area | time_offset | frame_time_offset | quality_flag | parent_flash_id | geometry | |
---|---|---|---|---|---|---|---|
41316820 | 1.438346e-14 | 5.319701e+08 | 2022-10-26 00:02:41.279088467 | 2022-10-26 00:02:41.407264270 | 0.0 | 19663 | POINT (-91.41206 15.12315) |
41316821 | 4.098026e-14 | 1.240653e+09 | 2022-10-26 00:02:41.358435393 | 2022-10-26 00:02:41.486992672 | 0.0 | 19663 | POINT (-91.42130 15.11019) |
41316824 | 3.208133e-14 | 1.063330e+09 | 2022-10-26 00:02:41.438163794 | 2022-10-26 00:02:41.566339598 | 0.0 | 19663 | POINT (-91.43069 15.12149) |
41316826 | 2.184922e-15 | 8.850908e+07 | 2022-10-26 00:02:41.507592354 | 2022-10-26 00:02:41.636149633 | 0.0 | 19663 | POINT (-91.46420 15.12907) |
41316827 | 6.984346e-15 | 1.782390e+08 | 2022-10-26 00:02:41.958877996 | 2022-10-26 00:02:42.087435275 | 0.0 | 19664 | POINT (-91.17548 15.04030) |
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
# Display the GOES GLM "group" product centriod location
fig, ax = plt.subplots(
figsize=(12, 12),
subplot_kw=dict(projection=ccrs.Orthographic(central_longitude=-90.0)),
)
df.geometry.plot(ax=ax, facecolor="red", marker="+", transform=ccrs.PlateCarree())
ax.coastlines(resolution="50m")
ax.set_global()
ax.gridlines()
ax.set_title(
f"GOES Geostationary Lightning Mapper (GLM) \n {item.datetime.isoformat()}",
fontweight="bold",
fontsize="12",
);