Fixing Meteorite Positions Stored as 0,0 or Left Blank
On this page
Problem statement
In NASA's Meteorite Landings table, 6,214 rows have a latitude of 0 and a longitude of 0, because 0,0 was entered when no position was given, and 7,315 have both columns blank. That is 29.6% of the table, and not a random 29.6%: mostly Antarctic finds and desert meteorites bought from dealers. Left in, the zeros put thousands of meteorites in the Gulf of Guinea. This page finds every kind of missing position, including a subtler one where only the latitude was lost, and recovers positions for about half of them from the other finds of the same place.
Quick answer
Treat 0,0 as missing, then drop or fill the missing rows before mapping:
zero = (met.reclat == 0) & (met.reclong == 0)
print(f"at exactly 0,0: {zero.sum():,}; no coordinates: {met.reclat.isna().sum():,}; either: {(zero | met.reclat.isna()).mean():.1%}")
good = met[~zero & met.reclat.notna()]
print(f"usable: {len(good):,}")
at exactly 0,0: 6,214; no coordinates: 7,315; either: 29.6%
usable: 32,187
6,214 rows sit at 0,0 and 7,315 have no position, 29.6% of the meteorites between them. 32,187 remain, but that is not yet the end, because some of those have only one coordinate lost.
Three kinds of missing position
1. Who is at 0,0
met["place"] = met.name.str.replace(r"\s*[A-Z]?\d+.*$", "", regex=True)
print(met[zero].place.value_counts().head(6).to_string())
print(met[zero].fall.value_counts().to_string())
place
Northwest Africa 1750
Miller Range 1682
Yamato 1666
Dominion Range 695
Larkman Nunatak 169
Patuxent Range 129
fall
Found 6213
Fell 1
The 0,0 rows are almost all finds from collecting grounds: 1,750 Northwest Africa meteorites, whose find sites are unknown because they were bought from dealers, and thousands from Antarctic fields, Miller Range 1,682, Yamato 1,666, Dominion Range 695. Only one is a fall. Dropping them silently removes Antarctic collecting seasons and the desert trade from any map or count by region.
2. Half a zero
half = (met.reclat == 0) ^ (met.reclong == 0)
print(f"latitude or longitude alone is 0: {half.sum()}")
print(met[half].groupby(["place", "reclat", "reclong"]).size().sort_values(ascending=False).head(5).to_string())
latitude or longitude alone is 0: 224
place reclat reclong
Yamato 0.0 35.66667 223
Meru 0.0 37.66667 1
224 rows have one coordinate at 0 and the other set. 223 are Yamato meteorites at latitude 0, longitude 35.67Β°, the right longitude for the Yamato Mountains with the latitude lost. A filter on (reclat == 0) & (reclong == 0) lets them through, and they land on the equator in Kenya. The other row is Meru, a real Kenyan fall that lies almost on the equator; it is the only one whose 0 is genuine.
3. Borrowing a position from the other finds of the same place
bad = zero | met.reclat.isna() | half
ref = met[~bad].groupby("place").agg(n=("id", "size"), lat=("reclat", "median"), lon=("reclong", "median"),
spread=("reclat", lambda s: s.max() - s.min()))
ref = ref[(ref.n >= 20) & (ref.spread < 2)]
fill = met[bad].join(ref, on="place", how="inner")
print(f"bad positions: {bad.sum():,}; filled from a place with 20+ located finds spread under 2 degrees: {len(fill):,}")
print(fill.place.value_counts().head(5).to_string())
bad positions: 13,753; filled from a place with 20+ located finds spread under 2 degrees: 6,972
place
Yamato 2381
Miller Range 2004
LaPaz Icefield 1324
MacAlpine Hills 763
Grosvenor Mountains 218
Of 13,753 bad positions, 6,972 belong to a place with at least 20 located finds lying within 2 degrees of latitude of each other, and can take that place's median position: 2,381 Yamato meteorites, 2,004 from Miller Range, 1,324 from LaPaz Icefield. The rule refuses Northwest Africa, whose located members are spread across the Sahara, so a "Northwest Africa" label is not a position. Mark filled rows with a flag; their position is the collecting area's, good for a world map and useless for anything finer.
What the map would have shown
The data used on this page
NASA's Meteorite Landings table from The Meteoritical Society's records (last updated May 2013) and Natural Earth's 1:110m countries, both saved on this site on 28 October 2026.
from pathlib import Path
import json
import zipfile
import geopandas as gpd
import numpy as np
import pandas as pd
import requests
Path("data").mkdir(exist_ok=True)
# NASA's Meteorite Landings table (The Meteoritical Society's records as NASA last updated them, May 2013), every
# fireball in NASA/JPL's Fireball Data API, and Natural Earth's countries and land. Saved 28 October 2026.
# Live: https://data.nasa.gov/docs/legacy/meteorite_landings/Meteorite_Landings.csv
# https://ssd-api.jpl.nasa.gov/fireball.api?vel-comp=true
ARCHIVE = "nasa-meteorites-2026-10-28.zip"
if not Path(f"data/{ARCHIVE}").exists():
Path(f"data/{ARCHIVE}").write_bytes(requests.get(f"https://data.spatialworkflow.io/{ARCHIVE}", timeout=600).content)
Z = zipfile.ZipFile(f"data/{ARCHIVE}")
met = pd.read_csv(Z.open("Meteorite_Landings.csv")).rename(columns={"mass (g)": "mass_g"})
for f in ["ne_110m_admin_0_countries.zip", "ne_50m_land.zip"]:
Path(f"data/{f}").write_bytes(Z.read(f))
print(f"Meteorite Landings: {len(met):,} meteorites")
Meteorite Landings: 45,716 meteorites
Example 1: what the map would have shown
world = gpd.read_file("data/ne_110m_admin_0_countries.zip")
g0 = gpd.GeoDataFrame(met.dropna(subset=["reclat"]), geometry=gpd.points_from_xy(met.dropna(subset=["reclat"]).reclong, met.dropna(subset=["reclat"]).reclat), crs=4326)
gulf = g0.cx[-5:5, -5:5]
print(f"points within 5 degrees of 0,0 before the fix: {len(gulf):,}; on land there: {gulf.within(world.union_all()).sum()}")
points within 5 degrees of 0,0 before the fix: 6,214; on land there: 0
Within five degrees of 0,0 the raw table has 6,214 points, every one of them the placeholder, and none on land. On a world map they draw one dot of 6,214 meteorites in the sea off West Africa, which a map with transparency or a heat map turns into the brightest spot on Earth. Testing for 0,0 is the first step of any meteorite map.
Internal links
- Thousands of finds on one point β the positions that are given, but shared
- Reading the table β the other columns and their traps
- Where meteorites were found β the map once the positions are clean
- Cleaning messy CSV coordinates β the general checks for any point table