How Annotations Are Rendered Under Different Camera States in Gods‑Eye‑View
The Gods‑Eye‑View annotation system dynamically adjusts the visual representation of every mark based on real‑time camera position, height, and tilt by routing geometry through either a world‑space Cesium entity pipeline or a screen‑space SVG overlay, ensuring annotations remain readable and correctly occluded regardless of viewpoint.
The open‑source Gods‑Eye‑View project implements a sophisticated annotation layer for Cesium‑based 3D geospatial visualization. When examining how annotations are rendered under different camera states, the architecture reveals three distinct rendering pipelines that adapt in real time to altitude changes and viewing angles. This article examines the source code in src/annotations/ to explain how the system maintains geometric fidelity and visual clarity across varying camera configurations.
Three Rendering Paths for Camera‑Adaptive Annotations
The system partitions annotation workload across specialized renderers based on the geometric requirements of each mark type. This separation allows camera‑dependent scaling algorithms to target the specific constraints of world‑anchored versus screen‑projected graphics.
World‑Space Renderer for Geometric Draping
The worldAnnotationRenderer.js module handles area footprints, extruded buildings, and route polylines that must drape onto photorealistic 3D tiles. This renderer creates native Cesium entities using polygons, polylines, and ellipses that exist as part of the 3D scene graph.
To maintain consistent visibility as the camera ascends or descends, the renderer implements a ringRadius() function wrapped in a CallbackProperty. This function reads viewer.camera.positionCartographic?.height each frame and returns a clamped value between 14 meters and 170 meters:
function ringRadius() {
const h = viewer.camera.positionCartographic?.height ?? 1000;
return Math.max(14, Math.min(170, h * 0.03));
}
Both the semiMajorAxis and semiMinorAxis of ellipse entities consume this same CallbackProperty, guaranteeing perfect circular geometry even when the camera moves during frame rendering. The entities are classified with Cesium.ClassificationType.CESIUM_3D_TILE, ensuring they are automatically occluded by underlying building geometry.
Screen‑Space Renderer for Overlay Graphics
The screenAnnotationRenderer.js module manages pins, highlights, arrows, and labels that require hand‑drawn SVG styling. Rather than existing in world coordinates, these annotations are re‑projected every frame onto an HTML overlay using SceneTransforms.worldToWindowCoordinates.
Camera altitude drives a scaling factor through the markScale(h) function, which linearly interpolates between MARK_SCALE_NEAR_H (800 m) and MARK_SCALE_FAR_H (6000 m):
function markScale(h) {
if (!(h > MARK_SCALE_NEAR_H)) return 1;
if (h >= MARK_SCALE_FAR_H) return MARK_SCALE_MIN;
const t = (h - MARK_SCALE_NEAR_H) / (MARK_SCALE_FAR_H - MARK_SCALE_NEAR_H);
return 1 - t * (1 - MARK_SCALE_MIN);
}
The resulting scale factor adjusts the radii of outer and inner rings, dot sizes, and SVG stroke widths. To prevent "floating" markers when the user looks away from the globe, the renderer employs an EllipsoidalOccluder to cull annotations that fall behind the horizon or outside the camera frustum.
Hybrid Router for Production Workloads
The hybridAnnotationRenderer.js module serves as the default production entry point. It maintains a routing table that delegates area footprints to the world‑space renderer and all other annotation types to the screen‑space renderer. This architecture ensures that a single logical API—add, update, remove, sync, and destroy—operates across both sub‑renderers while preserving the cleanup guarantees of the routing table (routed).
Camera‑Height‑Dependent Geometry Scaling
Both rendering paths implement distinct algorithms for responding to camera elevation changes, ensuring annotations remain legible without overwhelming the viewport.
Dynamic World‑Space Circles
World‑space annotations rely on the CallbackProperty mechanism to bind entity geometry directly to camera state. The system evaluates ringRadius() automatically during each render loop iteration, eliminating the need for manual event listeners. This approach maintains synchronization between the camera altitude and the displayed footprint size without forcing a full entity rebuild.
Adaptive Screen‑Space Markers
Screen‑space annotations calculate markScale() within the scene.postRender listener before projecting coordinates. The scale factor applies uniformly to all SVG geometry, including leader lines and label offsets. By defining the interpolation zone between 800 m and 6000 m, the system ensures that markers retain full size during low‑altitude inspection while shrinking to manageable proportions during high‑altitude overview modes.
Occlusion Handling and Depth Management
Proper depth perception requires different occlusion strategies for each rendering path. World‑space entities leverage Cesium’s ClassificationType.CESIUM_3D_TILE to automatically hide behind photorealistic building geometry. Screen‑space annotations utilize the EllipsoidalOccluder to determine whether a mark lies beyond the planetary horizon relative to the camera position, preventing the display of disconnected floating SVGs when viewing the globe from oblique angles.
Continuous Frame Updates
The system hooks into Cesium’s render loop through two mechanisms:
- World‑space updates occur implicitly when Cesium evaluates the
CallbackPropertybindings during entity rendering. - Screen‑space updates execute via a
scene.postRenderlistener (onPostRender) that invokesprojectAll(), recomputing world‑to‑window coordinates for every active annotation and applying the currentmarkScale()factor.
This dual‑track approach ensures zero‑latency visual feedback during camera flight animations, with geometry updates synchronized to the display refresh rate.
Implementation Example
The following pattern initializes the hybrid renderer and adds both a camera‑scaling pin and a draped area footprint:
// Initialise the hybrid renderer (production default)
import { createHybridAnnotationRenderer } from './annotations/hybridAnnotationRenderer.js';
const annotationRenderer = createHybridAnnotationRenderer(viewer);
// Add a pin that scales with camera height (screen‑space)
annotationRenderer.add({
id: 'pin-001',
type: 'pin',
anchor: { lon: -122.4194, lat: 37.7749, height: 0 },
label: 'San Francisco',
color: 'amber',
});
// Add an area that drapes onto 3D tiles (world‑space)
annotationRenderer.add({
id: 'area-001',
type: 'area',
ring: [ /* lon/lat pairs */ ],
label: 'Golden Gate Park',
synthesized: false,
});
Summary
- Three renderers handle distinct annotation types:
worldAnnotationRenderer.jsfor draped geometry,screenAnnotationRenderer.jsfor SVG overlays, andhybridAnnotationRenderer.jsfor unified management. - Camera height drives dynamic scaling through
ringRadius()in world‑space (14 m–170 m clamped) andmarkScale()in screen‑space (800 m–6000 m interpolated). - Occlusion is handled via
CESIUM_3D_TILEclassification for world entities andEllipsoidalOccluderculling for screen markers. - Continuous updates leverage
CallbackPropertyfor world entities andscene.postRenderfor screen projections, ensuring real‑time synchronization with camera motion.
Frequently Asked Questions
How does camera height specifically affect annotation size in Gods‑Eye‑View?
Camera height directly modulates the visual size of annotations through distinct algorithms in each renderer. In worldAnnotationRenderer.js, the ringRadius() function multiplies camera altitude by 0.03 and clamps the result between 14 and 170 meters, ensuring world‑space circles grow appropriately as the viewer zooms out. In screenAnnotationRenderer.js, the markScale() function performs linear interpolation between scale factors of 1.0 at 800 m and MARK_SCALE_MIN at 6000 m, shrinking SVG pins and labels to prevent viewport clutter during high‑altitude overview.
What is the technical difference between world‑space and screen‑space annotation rendering?
World‑space rendering creates native Cesium entities (polygons, ellipses, polylines) that exist within the 3D scene graph and respect terrain and building geometry through ClassificationType.CESIUM_3D_TILE. Screen‑space rendering projects geographic coordinates into window coordinates using SceneTransforms.worldToWindowCoordinates and draws SVG elements onto an HTML overlay. This distinction allows world‑space annotations to drape realistically onto photorealistic tiles while screen‑space annotations maintain stylistic flexibility and constant pixel density regardless of camera tilt.
How does the system prevent annotations from appearing through buildings or behind the globe?
World‑space annotations rely on Cesium’s classification system with CESIUM_3D_TILE, which automatically fragments geometry and hides portions occluded by photorealistic tilesets. Screen‑space annotations implement an EllipsoidalOccluder that tests whether the annotation’s world position lies behind the planetary horizon relative to the camera position; if occluded, the SVG element is hidden to prevent floating marker artifacts when the camera looks away from the annotation’s location.
Why does Gods‑Eye‑View use a hybrid renderer architecture instead of a single approach?
The hybrid architecture optimizes for competing requirements: area footprints and building extrusions require geometric draping on terrain (world‑space), while pins and labels require consistent screen size and styling (screen‑space). The hybridAnnotationRenderer.js router delegates each annotation type to its optimal pipeline while presenting a unified JavaScript API, eliminating the need for consuming code to manage renderer selection logic manually.
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 →