Design and Refactoring of the xGis 3D Map Event System and Picking Engine
This article details the background, problems, and comprehensive refactoring plan for the xGis web‑based 3D map library, covering event classification, API design, layer interaction proxy, CPU/GPU picking implementations, performance trade‑offs, and future optimization directions.
xGis is a data‑driven, easy‑to‑use web 3D map library that focuses on geographic data visualization with high performance and rendering quality. It supports flexible custom layer management, allowing developers to add, remove, or modify dozens of layer types.
Background
The library is used in digital twin scenarios where a full 3‑D model of physical objects is combined with mathematical models and intelligent algorithms for simulation and analysis.
Problems
Inconsistent interaction field configuration across different layers.
Built‑in event logic does not meet specific scene requirements (e.g., double‑click vs. single‑click, conditional hover styles).
Duplicate development of interaction modules for each new layer, increasing development cost.
Performance degradation due to redundant event‑listener logic.
Refactoring Approach
The solution starts by classifying events based on their trigger source (container, context, components, controls, window, layer, custom) and then designing a unified API that mirrors JavaScript's core event methods (addListener, emit, removeListener).
Event Classification
Each event source is grouped into categories such as container (DOM container resize), context (WebGL context loss), components (HUD controls like layerControl, zoomControl), controls (map pan/zoom/rotate), window (orientation change), layer (mouse interaction and lifecycle), and custom (library lifecycle events).
Syntax Design
Typical usage mirrors native DOM event handling:
const button = document.querySelector('button');
button.addEventListener('click', (event) => {
// do something else
});Custom events are emitted via an internal EventEmitter (e.g., eventemitter3) allowing publish/subscribe patterns for non‑DOM events.
Layer Interaction Proxy
Layer events are bound to a single canvas DOM element. When an event occurs, the system retrieves the set of layers registered for that event, performs hit‑testing (using a picking engine), and emits the appropriate event to each layer.
public on(eventType: CustomUIEventType | LifeCycleEventType | string, handle: (...args: any[]) => void, context?: any) {
if (!isNotCustomUIEventType(eventType)) {
this.eventManager.bindEvent(this.id, eventType as CustomUIEventType);
}
this.ee.on(eventType, handle, context);
}Picking Engine Implementation
Two strategies are provided:
CPU raycasting – traverses objects, checks bounding spheres/boxes, then performs triangle‑level intersection tests.
GPU color‑based picking – renders each object with a unique color to an off‑screen buffer and reads the pixel under the cursor.
CPU approach is the default because it handles dynamic scene changes more gracefully, while GPU can be switched on for static, massive datasets.
// CPU raycasting example
raycast(raycaster, intersects) {
if (geometry.boundingSphere === null) geometry.computeBoundingSphere();
if (raycaster.ray.intersectsSphere(_sphere) === false) return;
if (geometry.boundingBox !== null && _ray.intersectsBox(geometry.boundingBox) === false) return;
const intersection = ray.intersectTriangle();
if (intersection) intersects.push(intersection);
} // GPU picking initialization
private __initGPUPick() {
this.pickingScene = new Scene();
this.pickingTexture = new WebGLRenderTarget(containerDom.clientWidth, containerDom.clientHeight);
const pickingMaterial = new ShaderMaterial({
vertexShader: pickVshader,
fragmentShader: pickFshader,
transparent: false,
side: DoubleSide,
});
this.pickingObjMap = new Map();
// ... layer load handling omitted for brevity
}Benefits and Reflections
After refactoring, interaction fields are unified, event binding is flexible, developers can extend base layer interaction with minimal code, and overall event handling performance improves by about 20%.
Future work includes exploring octree acceleration for CPU raycasting and handling merged geometry for more efficient GPU picking.
About the Team
The article is authored by the ByteDance Data Platform team, which provides data‑driven products internally and under the Volcano Engine brand to various industries.
ByteFE
Cutting‑edge tech, article sharing, and practical insights from the ByteDance frontend team.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.