I tried using NASA satellites to predict Calgary water main breaks. Here's what happened.
I found a correlation, then discovered my own processing had invented it. I rebuilt with a method simple enough to explain in three sentences, ran the honest test, and got an honest answer. This is the whole journey, including the wrong turn.
This month a water main broke in Coach Hill, in southwest Calgary, and two weeks later the street had a sinkhole big enough to make the news. Every time that happens, the same thought comes up: couldn't we have seen it coming? It turns out NASA gives away data that sounds like exactly the right tool: radar satellites that measure where the ground sits, to the millimetre, every couple of weeks, through clouds, for free. So I downloaded 88 GB of it covering Calgary and spent my evenings finding out.
Short version: the satellites measure something real, my first analysis fooled me, the honest rebuild taught me why this is hard, and the properly-run test says satellite ground movement does not predict Calgary's water main breaks; the city's own break records do. I think the journey is worth more than the headline, so here it is, wrong turn included.
Ground-movement page · Water-mains page · All code on GitHub
Part 1: I found a signal. Then I checked it.
NASA's OPERA product gives you, for every 30-metre patch of Calgary, dozens of measurements of ground position between 2016 and 2021. My first approach was what felt like the "scientific" one: fit a velocity trend through all of the measurements at every pixel, then average the five satellite tracks that cover Calgary into one map of how fast the city is moving.
That map showed older inner-city neighbourhoods subsiding, and a statistically significant correlation with water main breaks (ρ = −0.286, p < 0.0001, for those keeping score). For about a week, I thought I had something.
Then I ran the check that should have come first: do the five satellite tracks agree with each other? They did not. The same patch of Calgary, measured by two different tracks, disagreed by 12–21 mm/yr, several times larger than any real ground motion in this city. The cause was subtle: NASA re-anchors its measurements to a fresh reference scene roughly every year, and fitting one trend line across those re-anchorings creates a sawtooth that the fit reads as motion. My "subsidence" was partly an artifact, and the correlation evaporated when the artifact did. The artifact even correlated with break locations (old neighbourhoods cluster both ways), which is exactly how a wrong method hands you a publishable-looking result.
I retracted the whole first analysis. Everything below is the rebuild.
Part 2: The three-sentence method
The fix was to stop being clever. NASA already publishes each measurement as "motion since reference date X," and rotates that reference roughly yearly, and the last measurement in each reference window is the next reference date. Which means NASA effectively publishes year-over-year ground movement directly. So the entire method is now:
- Take NASA's own year-over-year measurements (one file per year-step, per satellite track).
- Add up the yearly steps at each pixel: total movement 2016 to 2020.
- Keep each satellite track separate. Always.
No curve fitting. No averaging tracks together. Every number on the maps below traces back to specific NASA-published values between specific dates, and the code is public. I calibrated the absolute level against 5 public GPS stations around Calgary (Nevada Geodetic Lab), and the honest report there is that the calibration is only good to about ±7 to 9 mm; the community-to-community pattern, which is what the maps show, doesn't depend on it.
Part 3: What the satellite saw, and the lesson the second satellite taught me
Satellite ground movement over Calgary, 2016 to 2020, in millimetres per grid cell, one layer per viewing angle.
Same city, two satellite viewing angles, 2016 to 2020. Red: the ground moved away from the satellite (sinking-like); blue: toward it (rising-like). Where the picture changes between angles, do not trust the colour; where it holds, the motion is probably real.
The cleanest track shows a coherent picture: a tight bowl of sinking-like motion centred on the inner city and Bow/Elbow river valley (Downtown East Village, Ramsay, Inglewood, Mission, the Beltline, about −10 to −15 mm over four years) fading with distance, with the NW upland communities (Citadel, Hawkwood, Royal Oak) moving the other way. It decays smoothly from the river confluence and isn't the shape a processing artifact usually takes.
But this project had already burned me once, so before believing it I loaded a second satellite track that views Calgary from the opposite angle. If the motion is real, both angles should broadly agree. They mostly don't. Across 158 communities, the two angles correlate at −0.25. The reason (as best I can tell; experts, this is question #1 for you) is atmosphere: one satellite passes in the morning, the other in the evening, the radar signal bends through different air, and each track carries its own city-scale tilt. NASA ships an atmosphere-filtered version of the data; using it cuts the disagreement roughly in half, but doesn't eliminate it.
So instead of publishing a confident combined map, the ground-movement page shows both angles and labels every community honestly: 28 where both angles agree the ground sank, 3 where both agree it rose, and 120 where they disagree and you shouldn't trust either number alone. Flip between the angles yourself on the map above.
Communities ranked (single angle, with that caveat)
Communities with the most sinking-like and most rising-like satellite movement, millimetres over 2016 to 2020, angle 1 only.
Moved most negative (sinking-like)
Mean satellite line-of-sight movement per community, mm, Jul 2016 to Jul 2020 (angle 1, descending).
Moved most positive (rising-like)
Same metric, other end of the list.
Movement along the line of sight of satellite angle 1 (descending), mm over 2016-2020, averaged per community, GPS-calibrated. Negative = moved away from the satellite (sinking-like), positive = toward it. One viewing angle cannot separate vertical from sideways motion — see the agreement table for where angle 2 independently concurs.
Part 4: The real test. Can it predict breaks?
Odds of a break after Jul 2020 per pipe: ground movement changed nothing from either angle; past breaks did.
| Predictor of a post-2020 break | Angle 1 | Angle 2 |
|---|---|---|
| Ground sinking under the pipe (per 10 mm) | ×1.06 p = 0.60 | ×0.96 p = 0.61 |
| Uneven movement under the pipe (per mm of local spread) | ×0.95 p = 0.33 | ×0.93 p = 0.27 |
| The pipe has broken before | ×3.4 p < 0.0001 | ×3.7 p < 0.0001 |
Each figure multiplies the odds of a break after Jul 16, 2020, with pipe age, material, length and break history held constant; ×1 means no effect. Angle 1 tested 54,531 pipes, angle 2 34,706.
My first attempt got the test design wrong too, so the rebuild fixed that as well. The honest version has to respect time: measure ground movement first, count breaks afterwards, and control for the boring things every water engineer already knows predict failure. Concretely:
- 54,531 pipe segments that were in the ground before the satellite window started.
- Ground movement under each pipe, 2016 to 2020, from the satellite data above.
- 1,042 breaks that happened after the window closed (Jul 2020 to May 2026).
- Pipe age, material, length, and each segment's own break history held constant.
The result, from both satellite angles independently, is in the table above.
I find the agreement here genuinely satisfying: the two satellite angles disagree about Calgary's absolute ground movement, yet agree completely that whatever they're measuring doesn't predict pipe failure. And the raw numbers contain one last trap worth showing: unadjusted, "rising" ground looks safer, but rising areas are young NW suburbs full of new plastic pipe. Control for age and material and the effect vanishes. The same confound that fooled my first analysis, caught by design this time.
What does predict breaks in Calgary is unglamorous: a pipe's own break history, and being old cast iron (roughly 52–83 breaks per 100 km vs ~21 city-wide). The risk model built from those boring features (no satellites required) is on the water-mains page:
The boring model that works
Share of recent breaks caught by the top-ranked communities, and the weights behind the water-main risk score.
Capture rate
The share of the 708 breaks recorded Jan 2022 to Feb 2026 that fell in the top 10%, 20%, 30% and 50% of communities by predicted risk (model run May 9, 2026). Random ranking would put 20% in the top 20%.
Spearman ρ between predicted-risk score and observed break density: 0.768 across 219 communities.
What the score leans on
The weight of each feature in the composite score, set by hand. They are not random forest importances; that model uses an overlapping feature set with its own.
Plain features. The 2015 UBC Bayesian model used a similar set and did about as well: well-trodden ground.
Part 5: What I make of it (observations, not verdicts)
I'm a data analyst, not a geophysicist or a water engineer, so I'll keep to what the data shows and leave the why to people qualified to say:
- Pipes sitting on ground that sank 2016 to 2020 did not break more often afterwards, once age, material and history are accounted for. Both satellite angles agree.
- The strongest predictor of a future break in this data is a past break at the same spot. The city's maintenance records beat the satellite.
- And about Coach Hill: this data couldn't have seen it coming, and it's worth being precise about why. The satellite archive ends in 2020, but more fundamentally, it measures slow multi-year settling; Coach Hill reads stable in it. A sinkhole that opens two weeks after a pipe bursts is a sudden, local failure. Anyone selling satellite-predicts-sinkholes for geology like Calgary's should get follow-up questions.
Is the satellite data useless, then? No, it's just answering a different question than the one I started with. It shows a coherent, two-angle-confirmed picture of slow ground movement in parts of the city, which is interesting for its own reasons (foundations, river-valley sediments, long-term planning). It's now organized and public on the ground-movement page, with every uncertainty labeled. And newer satellites (NASA's L-band NISAR, launched 2025) measure with a wavelength better suited to soil moisture, the thing that actually changes before some leaks. That's a future experiment, with current data instead of an archive.
What I want experts to look at
If you're a geophysicist, hydrologist, civil engineer, or InSAR practitioner, here's where I'd value pushback. Each should be answerable in minutes, and all data + code is public:
- The asc/desc disagreement. Two tracks, correlation −0.25 at community level; NASA's short-wavelength layer halves the mean disagreement (9.8→5.4 mm) but a residual remains. Atmosphere I should model explicitly, ionosphere, real horizontal motion, or something I haven't considered?
- The elevation correlation. Displacement correlates with elevation (~+4–5 mm per 100 m at community level, surviving removal of a planar ramp). Is the river-valley bowl real geology (Quaternary sediment compaction?) or residual stratified tropospheric delay? What's the discriminating test you'd run?
- The null itself. The confidence interval rules out effects bigger than ~±29% odds per 10 mm of movement. Is there a mechanism where slow LOS movement at 30 m resolution should predict breaks in glacial till, such that my null is a resolution problem rather than a real absence?
- PS-InSAR on a Prairie city. This is all SBAS-style 30 m data. Would per-building persistent-scatterer processing plausibly change any conclusion here, or just sharpen the same picture?
GitHub issues, LinkedIn DMs, and email all reach me. This project has already been corrected once and improved for it; I genuinely want round two.
How I did this
How the analysis was done: tools, data sources, the reference-chain method, GPS calibration, the break test and prior work.
Tools
Python (xarray/netCDF4 for NASA granules, geopandas + shapely for spatial joins, statsmodels for the regression), PostgreSQL. Most of the analytical code was written with Claude Code; the judgement calls, checks, and mistakes were mine. The artifact in Part 1 was caught by a multi-agent adversarial review of the methodology before publication; the same process later refuted two of its own scarier-sounding findings, which is the point of doing it.
Data sources
- NASA OPERA L3 DISP-S1 v1.0 (free, via Earthdata; 88 GB Calgary mirror)
- City of Calgary Open Data: water main network (56,211 active segments inside city communities, with age/material), breaks (37,558 with GPS + dates, 1956–2026), community boundaries
- Nevada Geodetic Laboratory: daily GPS time series, 5 Calgary-area stations per viewing angle
The reference-chain method
For each Sentinel-1 frame, OPERA rotates its reference scene roughly yearly; the last secondary in each reference window is the next reference date. We sum the consecutive (ref_N → ref_N+1) granules per pixel; NASA's spec explicitly endorses adding products across reference epochs. Frames used: F18891 (descending, Jul 2016 to Jul 2020, 4 steps, 1.65M Calgary pixels) and F32525 (ascending, Aug 2016 to Jun 2020, 3 steps, 1.25M pixels). Quality gate: NASA's recommended mask + temporal coherence > 0.3 at every step. Frames never averaged together.
GPS calibration
NGL stations (NA frame) projected to each track's approximate line of sight. Two calibrations stored per frame (gold-standard-station-only and multi-station mean); cross-station spread 7 to 9 mm is the honest absolute uncertainty. All community comparisons are calibration-invariant.
The break test
54,531 active segments installed ≤2016 with satellite coverage. Breaks assigned to nearest segment within 50 m (37,258 of the 37,498 breaks on file when the test ran, June 2026). Outcome: any break after Jul 16, 2020. Logistic regression: movement + age + material group + log(length) + prior breaks, cluster-robust errors by community. Both frames run independently. Movement odds ratios: 1.06 (95% CI 0.86–1.29) descending; 0.96 (0.80–1.14) ascending. Prior-break odds ratio 3.4–3.7. Local movement spread (differential settlement proxy) also null.
The retracted first pass (for the record)
Weighted least-squares velocity per pixel across all ~60–75 epochs, five frames fused
after per-frame GPS offset correction, anomaly scoring vs ~1 km neighbourhoods.
Audit found 12–21 mm/yr inter-frame bias from fitting across reference rotations;
the apparent break correlation (ρ=−0.286) did not survive. The old code remains in the
repo (load_opera_to_pg.py) as the audit reference.
Prior work
- Kabir, Tesfamariam, Sadiq et al. (UBC): Bayesian failure model on this same Calgary water-mains dataset, ASCE.
- UK/Dutch PS-InSAR studies report positive pipe-failure signal on heave-prone clay and peat, geology Calgary doesn't have. Vendor case studies (SatSense/DRIPIN) claim larger lifts, not peer-reviewed.
Code + raw data
GitHub:
data-engine/insar/opera/scripts/load_opera_chain.py (ingest),
validate_chain_multistation.py (GPS),
data-engine/calgary_portal/water-infrastructure/scripts/correlate_chain_vs_breaks.py (the test).
Downloads (the article's own data, as of the date in the head): community + agreement JSON,
angle 1 grid,
angle 2 grid,
water-mains JSON.
The live files are ground-movement.json and
water-main-breaks.json.
Code on GitHub. Follow more Calgary data work on LinkedIn.
These numbers are as of Oct 31, 2026; the live ones are on the Calgary water main breaks and Calgary ground movement pages.