Skip to article frontmatterSkip to article content

NASA API: World Map of Fireball Impacts

NASA API: World Map of Fireball Impacts


Overview

This notebook will cover all the steps to access bright meteor and fireball impact data collected by NASA’s CNEOS and produce a global plot of impact sites

  1. Prerequisites
  2. CNEOS Overview
  3. Imports
  4. Access and Organize Data
  5. Plot Earth Fireball Impacts
  6. Summary

Prerequisites

ConceptsImportanceNotes
Intro to MatplotlibNecessaryPlotting on a data
Intro to GeoPandasNecessaryPlotting on a world map
Intro to PandasNecessaryFamiliarity with working with dataframes

CNEOS Overivew

A shooting star is a common term for a meteor and form bright trails of light that are often bright enough to be visible during the day. A “fireball” is a term for expectionally bright meteor that enters the Earth’s atmosphere at high speeds. A meteor that form fireballs can be over one meter long. A “bolide” typically refers to a fireball that breaks up in the atmosphere. These objects are tracked by the Center for Near Earth Object Studies (CNEOS) and information about the Peak Brightness, Velocity, and Joules of energy radiatated can be retrieved from CNEOS via an API

Fireball Data API

This notebook will query the Fireball API for data from the last decade of observations

JPL’s Center for Near-Earth Objects Studies API “Fireball” is an API that will return machine-readable data in the form of a JSON.

All queries to the Fireball Data API make requests to https://ssd-api.jpl.nasa.gov/fireball.api

The API accepts different query parameters to filter the data

Directly querying https://ssd-api.jpl.nasa.gov/fireball.api will return all the data of fireball impacts in reverse-chronological order

ParameterTypeDescription
date-minstringexclude data earlier than this date YYYY-MM-DD or date/time YYYY-MM-DDThh:mm:ss
date-maxstringexclude data later than this date YYYY-MM-DD or date/time YYYY-MM-DDThh:mm:ss
energy-minstringexclude data with total-radiated-energy less than this positive value in joules ×1010 (e.g., 0.3 = 0.3×1010 joules)
energy-maxstringexclude data with total-radiated-energy greater than this (see energy-min)
impact-e-minstringexclude data with estimated impact energy less than this positive value in kilotons (kt) (e.g., 0.08 kt)
impact-e-maxstringexclude data with total-radiated-energy greater than this (see impact-e-min)
alt-minnumberexclude data from objects with an altitude less than this (e.g., 22 meaning objects smaller than this)
alt-maxnumberexclude data from objects with an altitude greater than this (e.g., 17.75 meaning objects larger than this)
req-locbooleanlocation (latitude and longitude) required; when set true, exclude data without a location
req-altbooleanaltitude required; when set true, exclude data without an altitude
req-vel-compbooleanpre-entry velocity components required; when set true, exclude data without pre-entry velocity components
vel-compbooleaninclude pre-entry velocity components
sortstringsort data on the specified field: “date”, “energy”, “impact-e”, “vel”, or “alt” (default sort order is ascending: prepend “-“ for descending)
limitnumberlimit data to the first N results (where N is the specified number and must be an integer value greater than zero)

Results from API will be returned as JSON with a number of fields

FieldDescription
datedate/time of peak brightness (GMT)
latlatitude at peak brightness (degrees)
lonlongitude at peak brightness (degrees)
lat-dirlatitude direction (“N” or “S”)
lon-dirlatitude direction (“E” or “W”)
altaltitude above the geoid at peak brightness (km)
energyapproximate total radiated energy (10^10 joules)
impact-eapproximate total impact energy (kt)
vxpre-entry velocity (Earth centered X component, km/s)
vypre-entry velocity (Earth centered Y component, km/s)
vzpre-entry velocity (Earth centered Z component, km/s)

Example JSON result

Return the last 3 records https://ssd-api.jpl.nasa.gov/fireball.api?limit=3

{
    "signature":
        {
            "version":"1.0",
            "source":"NASA/JPL Fireball Data API"
        },
    "count":"3",
    "fields":["date","energy","impact-e","lat","lat-dir","lon","lon-dir","alt","vel"],
    "data":[
        ["2024-06-03 01:13:51","2.6","0.092","63.1","S","53.2","W","60.0",null],
        ["2024-06-01 23:24:59","7.9","0.25","1.0","S","15.9","W","26.5","12.6"],
        ["2024-05-27 03:19:36","8.4","0.26","1.7","N","39.4","W","56.0","39.2"]
    ]
}

Imports

import requests                 # submit API request query
import pandas as pd             # organizes data retrieved by the API
import geopandas                # generate a world map
import matplotlib.pyplot as plt # plotting data

Request Fireball Data via API

To retrieve all the fireballs recorded in the last decade (2014-2024)

data_since = "2014-01-01"
last_decade_fireball = requests.get(f"https://ssd-api.jpl.nasa.gov/fireball.api?date-min={data_since}")
last_decade_fireball.status_code # 200 is a valid request
200
fireball_json = last_decade_fireball.json()
# print out data labels
print(f"fields = {fireball_json['fields']}")
fields = ['date', 'energy', 'impact-e', 'lat', 'lat-dir', 'lon', 'lon-dir', 'alt', 'vel']
# convert JSON data to a Pandas dataframe
fireball_df = pd.DataFrame(fireball_json["data"], columns=fireball_json["fields"])
fireball_df
Loading...
# remove nan and duplicated data rows and reindex rows
fireball_df.dropna(inplace=True, ignore_index=True)

# convert columnss from strings to a float
fireball_df["energy"] = fireball_df["energy"].astype(float)
fireball_df["impact-e"] = fireball_df["impact-e"].astype(float)
fireball_df["lat"] = fireball_df["lat"].astype(float)
fireball_df["lon"] = fireball_df["lon"].astype(float)
fireball_df["alt"] = fireball_df["alt"].astype(float)
fireball_df["vel"] = fireball_df["vel"].astype(float)

# convert latitude to negative if lat-dir is S and longitude to negative if lon-dir is W
fireball_df['lat'] = fireball_df['lat'].mask(fireball_df['lat-dir'].eq('S'), -fireball_df['lat'])
fireball_df['lon'] = fireball_df['lon'].mask(fireball_df['lon-dir'].eq('W'), -fireball_df['lon'])

# rename columns
fireball_df = fireball_df.rename(columns= {"impact-e": "impact energy",
                                           "lat": "latitude",
                                           "lat-dir": "latitude direction",
                                           "lon": "longitude",
                                           "lon-dir": "longitude direction",
                                           "alt": "altitude",
                                           "vel": "velocity"})


fireball_df
Loading...
fireball_df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 240 entries, 0 to 239
Data columns (total 9 columns):
 #   Column               Non-Null Count  Dtype  
---  ------               --------------  -----  
 0   date                 240 non-null    object 
 1   energy               240 non-null    float64
 2   impact energy        240 non-null    float64
 3   latitude             240 non-null    float64
 4   latitude direction   240 non-null    object 
 5   longitude            240 non-null    float64
 6   longitude direction  240 non-null    object 
 7   altitude             240 non-null    float64
 8   velocity             240 non-null    float64
dtypes: float64(6), object(3)
memory usage: 17.0+ KB

Plot the Energy of Fireballs

# retrieve world map from GeoPandas
world_map = geopandas.read_file(geopandas.datasets.get_path("naturalearth_lowres"))

# Set up world map plot
fig, ax = plt.subplots(figsize=(15, 10))
world_map.plot(color="grey", ax=ax)

# Plot Fireball Locations with Energy
x = fireball_df["longitude"]
y = fireball_df["latitude"]
z = fireball_df["energy"]
plt.scatter(x, y, s=5*z, c=z, cmap="autumn")
plt.colorbar(label="Approximate Total Radiated Energy (10^10 Joules)")

# Setup Axis Limits and Title/Labels
plt.xlim([-180, 180])
plt.ylim([-90, 90])
plt.title("Energy of Fireball Observations")
plt.xlabel("Longitude (Degrees)")
plt.ylabel("Latitude (Degrees)")
plt.show()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[8], line 2
      1 # retrieve world map from GeoPandas
----> 2 world_map = geopandas.read_file(geopandas.datasets.get_path("naturalearth_lowres"))
      4 # Set up world map plot
      5 fig, ax = plt.subplots(figsize=(15, 10))

File ~/micromamba/envs/api-cookbook-dev/lib/python3.13/site-packages/geopandas/datasets/__init__.py:18, in get_path(dataset)
     12 error_msg = (
     13     "The geopandas.dataset has been deprecated and was removed in GeoPandas "
     14     f"1.0. You can get the original '{dataset}' data from "
     15     f"{ne_message if 'natural' in dataset else nybb_message}"
     16 )
     17 if dataset in _prev_available:
---> 18     raise AttributeError(error_msg)
     19 else:
     20     error_msg = (
     21         "The geopandas.dataset has been deprecated and "
     22         "was removed in GeoPandas 1.0. New sample datasets are now available "
     23         "in the geodatasets package (https://geodatasets.readthedocs.io/en/latest/)"
     24     )

AttributeError: The geopandas.dataset has been deprecated and was removed in GeoPandas 1.0. You can get the original 'naturalearth_lowres' data from https://www.naturalearthdata.com/downloads/110m-cultural-vectors/.

Plot the Impact Energy of Fireballs

# retrieve world map from GeoPandas
world_map = geopandas.read_file(geopandas.datasets.get_path("naturalearth_lowres"))

# Set up world map plot
fig, ax = plt.subplots(figsize=(15, 10))
world_map.plot(color="grey", ax=ax)

# Plot Fireball Locations with Energy
x = fireball_df["longitude"]
y = fireball_df["latitude"]
z = fireball_df["impact energy"]
plt.scatter(x, y, s=5*z, c=z, cmap="autumn")
plt.colorbar(label="Approximate Total Impact Energy (kt)")

# Setup Axis Limits and Title/Labels
plt.xlim([-180, 180])
plt.ylim([-90, 90])
plt.title("Total Impact Energy of Fireball Observations")
plt.xlabel("Longitude (Degrees)")
plt.ylabel("Latitude (Degrees)")
plt.show()

Plot the Velocity of Fireballs

# retrieve world map from GeoPandas
world_map = geopandas.read_file(geopandas.datasets.get_path("naturalearth_lowres"))

# Set up world map plot
fig, ax = plt.subplots(figsize=(15, 10))
world_map.plot(color="grey", ax=ax)

# Plot Fireball Locations with Velocity
x = fireball_df["longitude"]
y = fireball_df["latitude"]
z = fireball_df["velocity"]
plt.scatter(x, y, s=5*z, c=z, cmap="winter")
plt.colorbar(label="Pre-Entry Velocity (km/s)")

# Setup Axis Limits and Title/Labels
plt.xlim([-180, 180])
plt.ylim([-90, 90])
plt.title("Pre-Entry Velocity of Fireball Observations")
plt.xlabel("Longitude (Degrees)")
plt.ylabel("Latitude (Degrees)")
plt.show()

Plot the Altitude of Fireballs

# retrieve world map from GeoPandas
world_map = geopandas.read_file(geopandas.datasets.get_path("naturalearth_lowres"))

# Set up world map plot
fig, ax = plt.subplots(figsize=(15, 10))
world_map.plot(color="grey", ax=ax)

# Plot Fireball Locations with Energy
x = fireball_df["longitude"]
y = fireball_df["latitude"]
z = fireball_df["altitude"]
plt.scatter(x, y, s=5*z, c=z, cmap="bone")
plt.colorbar(label="Altitude Above Geoid at Peak Brightness (km)")

# Setup Axis Limits and Title/Labels
plt.xlim([-180, 180])
plt.ylim([-90, 90])
plt.title("Altitude of Fireball Observations")
plt.xlabel("Longitude (Degrees)")
plt.ylabel("Latitude (Degrees)")
plt.show()

Last Section

If you’re comfortable, and as we briefly used for our embedded logo up top, you can embed raw html into Jupyter Markdown cells (edit to see):

Info

Your relevant information here!

Feel free to copy this around and edit or play around with yourself. Some other admonitions you can put in:

Success

We got this done after all!

Warning

Be careful!

Danger

Scary stuff be here.

We also suggest checking out Jupyter Book’s brief demonstration on adding cell tags to your cells in Jupyter Notebook, Lab, or manually. Using these cell tags can allow you to customize how your code content is displayed and even demonstrate errors without altogether crashing our loyal army of machines!


Summary

Add one final --- marking the end of your body of content, and then conclude with a brief single paragraph summarizing at a high level the key pieces that were learned and how they tied to your objectives. Look to reiterate what the most important takeaways were.

What’s next?

Let Jupyter book tie this to the next (sequential) piece of content that people could move on to down below and in the sidebar. However, if this page uniquely enables your reader to tackle other nonsequential concepts throughout this book, or even external content, link to it here!

Resources and references