How God’s Eye View Ensures Icon Orientation for Aircraft and Ships on the Globe
God's Eye View ensures correct icon orientation for aircraft and ships by computing a screen-space rotation that projects each entity's heading vector onto the camera's basis, then stabilizing the result to prevent jitter during camera movements.
When building a Cesium-based globe visualization, fixed 2D icons (billboards) always face the camera by default. For aviation and maritime tracking applications, this creates a problem: how do you show an aircraft or vessel pointing in its actual direction of travel when the icon itself must rotate to remain visible? The bilawalsidhu/gods-eye-view repository solves this through a specialized projection system implemented in src/data/iconOrientation.js.
Screen-Projected Rotation: The Core Algorithm
The primary mechanism is screenProjectedRotation(scene, position, courseDeg, previous), located at lines 52-80 of src/data/iconOrientation.js. This function transforms a world-space heading into a 2D screen rotation that accounts for the current camera orientation.
The algorithm follows four steps:
-
Convert course to local ENU vector – The heading in degrees is transformed into a forward-probe vector in East-North-Up (ENU) coordinates using
Cesium.Cartesian3.fromElements. -
Transform to world space – The local vector is multiplied by the ENU-to-fixed-frame matrix via
Cesium.Matrix4.multiplyByPointAsVector. -
Project onto camera basis – The world-forward vector is dotted with
camera.rightWCandcamera.upWCto produce screen-space componentsdxanddy. -
Compute rotation angle –
Math.atan2(-dx, -dy)yields the final rotation in radians, where 0 means the icon points upward on screen.
Degenerate cases—when the icon is behind the camera, off-screen, or the projected vector is below MIN_SCREEN_COMPONENT_M—automatically fall back to the previous rotation value. This prevents sudden orientation jumps during rapid camera maneuvers.
// src/data/iconOrientation.js – screen-projected rotation (lines 52-80)
export function screenProjectedRotation(scene, position, courseDeg, previous = null) {
const camera = scene?.camera;
if (!camera?.rightWC || !camera?.upWC || !position) return previous;
const courseRad = Cesium.Math.toRadians(courseDeg || 0);
Cesium.Cartesian3.fromElements(
Math.sin(courseRad) * FORWARD_PROBE_M,
Math.cos(courseRad) * FORWARD_PROBE_M,
0,
_scratchForward
);
const enu = Cesium.Transforms.eastNorthUpToFixedFrame(position, Cesium.Ellipsoid.WGS84, _scratchEnu);
Cesium.Matrix4.multiplyByPointAsVector(enu, _scratchForward, _scratchWorldForward);
const dx = Cesium.Cartesian3.dot(_scratchWorldForward, camera.rightWC);
const dy = -Cesium.Cartesian3.dot(_scratchWorldForward, camera.upWC);
if ((dx * dx + dy * dy) < MIN_SCREEN_COMPONENT_M * MIN_SCREEN_COMPONENT_M) return previous;
// 0 rad = icon pointing up on screen
return Math.atan2(-dx, -dy);
}
Rotation Stabilization for Jitter-Free Movement
Raw projected rotation would flicker when the camera and entity align nearly parallel to the view direction. To address this, stabilizeScreenRotation(previous, next, deadbandRad) (lines 92-101) filters out sub-threshold changes.
The stabilizer uses circular arithmetic (Math.atan2(Math.sin(next - previous), Math.cos(next - previous))) to find the shortest wrapped difference between angles. Changes below ROTATION_DEADBAND_RAD (approximately 0.5°) are suppressed, preventing visual noise without introducing perceptible lag.
// src/data/iconOrientation.js – rotation stabilizer (lines 92-101)
export function stabilizeScreenRotation(previous, next, deadbandRad = ROTATION_DEADBAND_RAD) {
if (!Number.isFinite(next)) return Number.isFinite(previous) ? previous : null;
if (!Number.isFinite(previous)) return next;
const delta = Math.atan2(Math.sin(next - previous), Math.cos(next - previous));
return Math.abs(delta) < Math.max(0, deadbandRad) ? previous : next;
}
Layer Integration: Aircraft and Ship Icons
The rotation system is consumed by the moving-entity layers through Cesium CallbackProperty instances that update each frame.
In src/data/flights.js, each aircraft billboard's imageRotation property is bound to a callback that invokes the projection and stabilization pipeline:
// src/data/flights.js – aircraft billboard rotation binding
const billboard = entity.billboard;
billboard.image = aircraftIcon(iconKind, sizePx);
billboard.imageRotation = new Cesium.CallbackProperty(() => {
const projRot = screenProjectedRotation(scene, entity.position.getValue(), flight.courseDeg);
return stabilizeScreenRotation(billboard._lastRot, projRot);
}, false);
The same pattern applies to maritime tracking in src/data/vessels.js (or equivalent AIS vessel file), ensuring ship icons maintain proper heading alignment across the global fleet.
Key Design Decisions
-
Billboard-based rendering – Using Cesium billboards rather than 3D models guarantees pixel-perfect icons at any zoom level and eliminates z-fighting artifacts.
-
Per-frame callback evaluation – The
CallbackPropertywithisConstant = falseensures rotation updates continuously during camera orbits, pitch changes, and entity movement. -
Scratch variable reuse – Functions in
iconOrientation.jsuse module-level scratch variables (_scratchForward,_scratchEnu,_scratchWorldForward) to minimize GC pressure during high-frequency updates. -
Graceful degradation – When projection fails or the icon leaves view, the system retains the last valid rotation rather than defaulting to zero or arbitrary values.
Summary
-
screenProjectedRotationinsrc/data/iconOrientation.jstransforms entity headings into camera-relative screen rotations using vector projection ontocamera.rightWCandcamera.upWC. -
stabilizeScreenRotationapplies a 0.5° deadband to prevent orientation jitter during near-degenerate viewing angles. -
Aircraft and ship layers in
flights.jsandvessels.jsbind these utilities to billboardimageRotationproperties viaCallbackPropertyfor continuous per-frame updates. -
The system handles edge cases—off-screen entities, behind-camera positions, and minimal screen components—through automatic fallback to previous rotation values.
Frequently Asked Questions
What file contains the icon orientation logic for God’s Eye View?
The core implementation resides in src/data/iconOrientation.js, which exports screenProjectedRotation and stabilizeScreenRotation for use by aircraft and vessel layers.
Why use screen-space rotation instead of 3D models for aircraft and ships?
Screen-space rotation with billboards provides sharper icon rendering at all zoom levels, eliminates z-fighting, and maintains consistent visual weight. The projection system preserves the semantic value of heading information without the performance and visual complexity of articulated 3D models.
How does the system prevent icon jitter when the camera is directly above an entity?
The stabilizeScreenRotation function filters angular changes below approximately 0.5° (ROTATION_DEADBAND_RAD). When the camera approaches nadir and the forward vector projects to near-zero screen components, the deadband prevents rapid oscillation while the fallback mechanism in screenProjectedRotation retains the last stable orientation.
Can this orientation system work with custom icon sets?
Yes. The rotation value produced by screenProjectedRotation is applied to any billboard through the standard imageRotation property. The companion file src/data/aircraftIcon.js generates billboard images, but the orientation logic is decoupled from icon assets and works with any image source.
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 →