Track 4 culminates here: bringing time into the analysis. Multi-frame change detection lets you see plumes evolving, deforestation progressing, or coastlines shifting. The capstone is a real-time satellite tracker.
Static analysis answers "what is here." Temporal analysis answers "what changed." For space GIS, the second question is often the more interesting one: did a plume appear?, did the landscape recover from a launch event?, did a sea-launch platform move? All of these reduce to comparing imagery at multiple times.
The starting point is a stack: multiple frames of raster imagery, all aligned to the same projected grid. Source frames are rarely already aligned — they come at different times, sometimes from different orbits, and need resampling to a common reference grid. The standard tool is rasterio.warp.reproject() or, more conveniently, satpy.Scene.resample() when working with satellite NetCDFs.
import xarray as xr
# Each frame's brightness_temp variable, time-indexed
frames = [xr.open_dataset(f).rename({'Rad': f'frame_{i}'}) for i, f in enumerate(file_list)]
stack = xr.concat([f.rename(t_var='time') for f in frames], dim='time')
# stack now has dims (time, y, x) — 3-D
The simplest change detection: subtract one frame from another. Positive values are pixels that got hotter; negative values are pixels that got cooler. For consecutive Band 7 frames during a launch, you'll see a small positive cluster (the plume forming) and then a small negative cluster (the plume fading).
diff = stack['frame_1'] - stack['frame_0'] # element-wise raster math
hot = diff > 30 # Boolean mask of pixels that gained >30 K
Raw difference masks are noisy — single-pixel hits from sensor noise, edge artifacts from imperfect georegistration. Morphological operations clean this up:
scipy.ndimage.binary_erosion.scipy.ndimage.binary_dilation.After cleaning, label connected regions. Each connected hotspot cluster is a distinct "blob" — likely a single plume (or single false-positive source). scipy.ndimage.label assigns each blob a unique integer ID. You can then compute per-blob properties (area, centroid, bounding box, mean brightness) and filter blobs that meet plume-like criteria.
from scipy.ndimage import label, center_of_mass
mask = hot.values
labeled, n_blobs = label(mask)
centroids = center_of_mass(mask, labeled, range(1, n_blobs + 1))
areas = [(labeled == i).sum() for i in range(1, n_blobs + 1)]
Combining sensors makes detection more robust. A plume that appears in BOTH Band 7 thermal AND visible Band 2 is more likely a real launch (or a wildfire) than a Band-7-only signal (which could be a calibration artifact). Common multi-sensor combinations:
Week 20's lab is the start of Capstone 4: Real-Time Satellite Tracker. Build a Cesium-based web app that shows ISS + Starlink visible passes for a user-supplied lat/lon, with click-to-inspect orbital elements and 24-hour replay. The full rubric is on the capstone page; finishing it earns the Certified Mission GIS Engineer credential. Track 5 (Space GIS Architect) goes deeper into production-grade and expert-tier topics from here.
Build a Cesium-based web app that shows ISS + Starlink visible passes for a user-supplied lat/lon. Click-to-inspect orbital elements. 24h replay. This is the deliverable for Capstone 4.
Test yourself. Answer key on the certificate-track page (Gold-tier feature: progress tracking and auto-grading).