How to Correct Ellipsoidal Altitude to MSL Using Geoid Undulation in Gods-Eye-View
To correct ellipsoidal altitude to MSL using geoid undulation, subtract the local geoid height N from the WGS-84 ellipsoidal height using the ellipsoidalToMslDisplayM() function in src/data/geoid.js after ensuring the EGM96 grid is loaded via ensureGeoidReady().
The Gods-Eye-View repository tracks aircraft and vessel positions using the WGS-84 ellipsoid model, but displaying altitudes relative to Mean Sea Level (MSL) requires accounting for the vertical offset between the ellipsoid and the geoid. This article explains how to correct ellipsoidal altitude to MSL using geoid undulation as implemented in the codebase's geoid utility module.
Understanding the Geoid Correction Model
GPS and modern tracking systems record ellipsoidal altitude—the vertical distance above the mathematical WGS-84 ellipsoid. However, maps and aviation charts reference MSL altitude, which measures height above the geoid (a theoretical equipotential surface approximating sea level). The difference between these surfaces is the geoid undulation (N), which varies by location.
According to the Gods-Eye-View source code, the conversion follows the standard geodetic formula:
MSL Altitude = Ellipsoidal Altitude - N
Where N represents the geoid height in meters at a specific latitude and longitude. When the geoid lies below the ellipsoid (negative N), the MSL altitude is higher than the ellipsoidal measurement.
Core Geoid Functions in src/data/geoid.js
The src/data/geoid.js module encapsulates the entire conversion workflow, from loading the EGM96 geoid-undulation grid to performing the mathematical correction.
Initializing the Geoid Grid with ensureGeoidReady()
Before querying any undulation values, the system must load the bundled EGM96 geoid grid or establish a network proxy. The ensureGeoidReady() function returns a Promise that resolves when the grid is available.
import { ensureGeoidReady } from './data/geoid.js';
// Load the geoid grid before performing conversions
await ensureGeoidReady();
Attempting to query heights before this Promise resolves results in thrown errors, ensuring the application never applies corrections with uninitialized data.
Querying Undulation Values with geoidHeight()
Once initialized, the geoidHeight(latDeg, lonDeg) function returns the geoid undulation N in meters for any given coordinate pair.
import { geoidHeight } from './data/geoid.js';
const latitude = 51.5; // London
const longitude = -0.13;
const N = geoidHeight(latitude, longitude); // Returns approximately -45 meters
This value represents the signed distance from the ellipsoid to the geoid at that specific location.
Converting Heights with ellipsoidalToMslDisplayM()
The ellipsoidalToMslDisplayM(hEllipsoidalM, geoidUndulationM) function applies the correction algorithm. If a finite geoidUndulationM value is provided, it returns hEllipsoidalM - geoidUndulationM; otherwise, it returns the original ellipsoidal height unchanged to prevent erroneous corrections during grid failures.
import { ellipsoidalToMslDisplayM } from './data/geoid.js';
const ellipsoidalAlt = 120; // meters above WGS-84
const geoidN = -45; // meters (geoid below ellipsoid)
const mslAlt = ellipsoidalToMslDisplayM(ellipsoidalAlt, geoidN);
// Result: 165 meters MSL
Step-by-Step Implementation Examples
These practical examples demonstrate how to correct ellipsoidal altitude to MSL using geoid undulation in different scenarios.
Basic Conversion Workflow
This pattern loads the grid, queries the local undulation, and converts the altitude:
import { ensureGeoidReady, geoidHeight, ellipsoidalToMslDisplayM } from './data/geoid.js';
async function ellipsoidalToMsl(lat, lon, ellipsoidalAltM) {
// Ensure EGM96 grid is available
await ensureGeoidReady();
// Get geoid undulation N at location
const N = geoidHeight(lat, lon);
// Apply correction
return ellipsoidalToMslDisplayM(ellipsoidalAltM, N);
}
// Example: Houston, TX
(async () => {
const lat = 29.72;
const lon = -95.08;
const ellipsoidalAltM = 1500;
const mslAlt = await ellipsoidalToMsl(lat, lon, ellipsoidalAltM);
console.log(`MSL altitude: ${mslAlt.toFixed(1)} m`);
})();
Direct Conversion with Pre-Loaded Grid
When the grid is already initialized elsewhere in the application, you can query and convert synchronously:
import { geoidHeight, ellipsoidalToMslDisplayM } from './data/geoid.js';
const lat = 51.5;
const lon = -0.13;
const ellipsoidalAlt = 120;
const N = geoidHeight(lat, lon); // ≈ -45 m for London
const mslAlt = ellipsoidalToMslDisplayM(ellipsoidalAlt, N);
console.log(`Ellipsoidal: ${ellipsoidalAlt}m → MSL: ${mslAlt}m`);
Handling Missing Geoid Data
The function safely handles cases where the geoid undulation cannot be determined, preserving the original ellipsoidal value rather than applying a null correction:
import { ellipsoidalToMslDisplayM } from './data/geoid.js';
const ellipsoidalAlt = 800;
// Pass null or undefined when geoid data is unavailable
const mslAlt = ellipsoidalToMslDisplayM(ellipsoidalAlt, null);
console.log(mslAlt); // Output: 800 (unchanged)
Integration in the Heads-Up Display (src/hud.js)
The conversion logic appears in the user interface within src/hud.js (lines 339-340), where the real-time altitude read-out is assembled for aircraft visualization:
const geoidN = this._geoidUndulationM(latDeg, lonDeg);
const altMslM = ellipsoidalToMslDisplayM(altM, geoidN);
This integration demonstrates the production pattern used throughout the application: query the undulation at the current position, then pass both values to the conversion function to display accurate MSL altitudes in the heads-up display.
Summary
- Geoid undulation (N) represents the vertical distance between the WGS-84 ellipsoid and the geoid at a specific location, essential for converting GPS ellipsoidal heights to MSL altitudes.
- The
src/data/geoid.jsmodule provides three key functions:ensureGeoidReady()for grid initialization,geoidHeight()for querying N values, andellipsoidalToMslDisplayM()for applying the correction. - The conversion formula implemented is
MSL = Ellipsoidal - N, with the codebase handling edge cases by returning original values when geoid data is unavailable. - Production usage in
src/hud.jsshows the pattern scales to real-time tracking applications requiring immediate altitude corrections.
Frequently Asked Questions
What is the difference between ellipsoidal and MSL altitude?
Ellipsoidal altitude measures height above the WGS-84 mathematical ellipsoid used by GPS systems, while MSL (Mean Sea Level) altitude measures height above the geoid—the equipotential surface that best approximates global sea level. The difference between these two values at any coordinate is the geoid undulation (N), which can range from +100 meters to -100 meters depending on local gravitational variations.
Why does Gods-Eye-View use EGM96 for geoid undulation?
The repository utilizes the EGM96 (Earth Gravitational Model 1996) geoid-undulation grid because it provides a globally consistent, high-resolution reference surface that balances accuracy with reasonable file size for web deployment. According to the source code in src/data/geoid.js, this model is bundled with the application or retrieved via network proxy to ensure offline capability while maintaining sub-meter precision for most aviation and maritime applications.
What happens if the geoid grid fails to load?
If the EGM96 grid fails to initialize, the ellipsoidalToMslDisplayM() function includes defensive logic that detects null or undefined undulation values and returns the original ellipsoidal altitude unchanged. This fail-safe behavior prevents the application from displaying dangerously incorrect altitudes (such as negative MSL values) when geoid data is unavailable, as seen in the fallback handling within the conversion utility.
Can I use this conversion without loading the full geoid grid?
No, the geoidHeight() function explicitly requires the grid to be loaded via ensureGeoidReady() and will throw an error if called prematurely. However, once ensureGeoidReady() resolves successfully, subsequent calls to geoidHeight() operate synchronously without additional network requests, making the system efficient for real-time tracking scenarios where the grid is loaded once during application initialization.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →