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:

  1. 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.

  2. Transform to world space – The local vector is multiplied by the ENU-to-fixed-frame matrix via Cesium.Matrix4.multiplyByPointAsVector.

  3. Project onto camera basis – The world-forward vector is dotted with camera.rightWC and camera.upWC to produce screen-space components dx and dy.

  4. 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 CallbackProperty with isConstant = false ensures rotation updates continuously during camera orbits, pitch changes, and entity movement.

  • Scratch variable reuse – Functions in iconOrientation.js use 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

  • screenProjectedRotation in src/data/iconOrientation.js transforms entity headings into camera-relative screen rotations using vector projection onto camera.rightWC and camera.upWC.

  • stabilizeScreenRotation applies a 0.5° deadband to prevent orientation jitter during near-degenerate viewing angles.

  • Aircraft and ship layers in flights.js and vessels.js bind these utilities to billboard imageRotation properties via CallbackProperty for 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →