Georeferencing GOES and parallax (Capstone 3 week)
Track 3 culminates here: a GOES hotspot at (x, y) in the fixed grid is NOT directly at the (lat, lon) directly below — parallax matters, especially for tall plumes. This week is the math, and the capstone is a working thermal plume detector.
When GOES-18 sees a thermal hotspot at pixel (1023, 645), where is that hotspot really, on the ground?
GOES gives you pixel coordinates, not lat/lon. Converting between them is geometry — and getting it wrong means your launch detection (or your lava flow alert) is off by tens of kilometers. This week is the math.
Learning objectives
- Convert ABI fixed-grid coordinates to lat/lon
- Apply parallax correction for high-altitude plumes
- Handle the GOES 'view from the equator' geometry
- Account for limb effects at the edge of the disk
Primer
Track 3 closes here. By Week 14 you can threshold-detect a hotspot in Band 7 brightness temperature. But the (x, y) pixel coordinate is not the (lat, lon) of the heat source on the ground — not directly. To get a usable geocoded detection, the pixel must be projected through the GOES fixed-grid math, and parallax must be corrected for tall plumes. That math is this week, and the capstone is a working end-to-end plume detector.
The GOES fixed grid
GOES doesn't deliver imagery in lat/lon. It delivers imagery in a scan-angle coordinate system tied to the satellite's frame of reference. Each pixel has an "x" value (east-west scan angle from satellite, in radians) and a "y" value (north-south elevation angle from satellite, in radians). The NetCDF file contains x and y 1-D coordinate arrays plus a goes_imager_projection variable with the geostationary projection metadata (perspective point altitude, satellite longitude, sweep angle axis).
To get the (lat, lon) of a pixel, you project a line from the satellite through the pixel down to the Earth ellipsoid:
import numpy as np
H = 35786023.0 + 6378137.0 # satellite altitude + Earth equatorial radius
λ0 = -137.0 * π / 180 # satellite longitude (GOES-18 West)
r_eq, r_pol = 6378137.0, 6356752.31414
x, y = scan_x, scan_y # radians from the NetCDF
a = sin(x)**2 + (cos(x)**2 * (cos(y)**2 + (r_eq/r_pol)**2 * sin(y)**2))
b = -2 * H * cos(x) * cos(y)
c = H**2 - r_eq**2
r_s = (-b - sqrt(b**2 - 4*a*c)) / (2*a)
s_x = r_s * cos(x) * cos(y)
s_y = -r_s * sin(x)
s_z = r_s * cos(x) * sin(y)
lat = atan2((r_eq/r_pol)**2 * s_z, sqrt((H - s_x)**2 + s_y**2))
lon = λ0 + atan2(s_y, H - s_x)
This is the exact algorithm in the GOES-R documentation library (look for the Imagery ATBD). satpy wraps it cleanly.
The parallax problem
The math above assumes the pixel lies on Earth's surface. A rocket plume at 50 km altitude is well above the surface, and from GOES's geostationary perspective, the apparent position of the plume is offset from the actual position of the rocket. This offset is the parallax.
For a plume directly below GOES (sub-satellite point), parallax is zero. Move 1,000 km north and parallax of a 50 km plume reaches ~5 km. Move to 4,000 km from the sub-satellite point and parallax is ~25 km — well above LaunchDetect's 5 km accuracy target.
Parallax correction: given the apparent (lat, lon) and an estimated plume altitude, compute the offset vector pointing back toward the sub-satellite point, scaled by altitude/(altitude + Earth radius). The corrected position is the apparent position shifted by that vector.
Limb effects
At the edge of GOES's visible disk (the "limb"), pixel geometry is highly oblique. A pixel that's nominally 2 km × 2 km in the center of the disk becomes 6+ km × 2 km near the limb (foreshortening), and the radiometric path length through the atmosphere is much longer (more attenuation and scatter). Practical rule: discard detections more than ~70° away from sub-satellite point.
The capstone
Week 15's lab is the start of Capstone 3: Thermal Plume Detector. Given a GOES Band 7 NetCDF for a known launch event, output records with (timestamp, lat, lon, brightness_temp_K, area_km²) for each plume detection, with parallax correction applied, FIRMS-based false-positive filtering, and a Folium heatmap visualization. The full rubric is on the capstone page; finishing it earns the Certified Remote Sensing Specialist credential. Track 4 (Mission GIS Engineer) starts next week, moving the focus into web-based delivery and 3D globes.
Connecting to Hawaiʻi: Parallax over the Pacific
GOES-18 is at 137.0°W on the equator. Hawaiʻi is at ~20°N, about 2,500 km from GOES-18's sub-satellite point. That distance means the satellite looks at Hawaiʻi at an angle — and any tall thermal source (rocket plume, eruption column, volcanic ash plume) appears DISPLACED from where it actually is. A 30 km Kīlauea ash plume could appear ~3 km offset to the north because GOES is south of Hawaiʻi. The parallax-correction math you learn this week is exactly what HVO and NOAA apply to their thermal imagery to get the geolocation right.
Hands-on lab: Thermal Plume Detector (capstone start)
Given a GOES Band 7 NetCDF and a known launch event, output: detected plume records with (timestamp, lat, lon, brightness_temp_K, area_km²), Folium heatmap visualization. This is the deliverable for Capstone 3.
Quiz — click an answer to check it
No grade, no shame. Tap any option; you'll see if it's right plus the answer if not. The point is to notice what you already know and what's still settling.
- Latitude/longitude
- Scan and elevation angle from satellite
- UTM
- ECEF
- Plume altitude and distance from sub-satellite point
- Time of day
- Wavelength
- File size
- 75.2W
- 137.0W
- 0 degrees
- 180 degrees
- Manually via numpy only
- Via Scene.resample() to projected grids
- It doesn't
- Via QGIS plugin
- Brighter pixels
- Geometric and radiometric distortion (foreshortening, longer path)
- Nothing
- File corruption
Reflection
Take five minutes with this. Write your answer somewhere. Carry it into next week.