Loading GeoJSON Files in Babylon.js for 3D Map Visualization
一、Define Core 3D Scene Class
We start by defining a base class to manage the Babylon.js scene, camera, and environment setup.
import {
ArcRotateCamera, Color4, CubeTexture, Engine, GlowLayer, KeyboardEventTypes, Scene, Vector3,
StandardMaterial, Texture, RawTexture, MeshBuilder, ActionManager, SetValueAction, InterpolateValueAction, ExecuteCodeAction
} from '@babylonjs/core';
import { AdvancedDynamicTexture, TextBlock, Image } from '@babylonjs/gui';
class BaseSceneManager {
public engine: Engine;
public scene: Scene;
public camera: ArcRotateCamera;
public uiTexture: AdvancedDynamicTexture;
public glowEffect: GlowLayer;
constructor(canvas: HTMLCanvasElement) {
this.engine = new Engine(canvas);
const { scene, camera } = this.setupScene();
this.scene = scene;
this.camera = camera;
this.uiTexture = AdvancedDynamicTexture.CreateFullscreenUI('ui');
this.glowEffect = this.initGlowLayer();
this.configureEnvironment();
this.handleResize();
this.startRenderLoop();
this.enableDebugInspector(scene);
}
private setupScene(): { scene: Scene; camera: ArcRotateCamera } {
const scene = new Scene(this.engine);
scene.clearColor = new Color4(0, 0, 0, 0);
const camera = new ArcRotateCamera('mainCam', Math.PI / 2, Math.PI / 2, 5.5, Vector3.Zero());
camera.attachControl(this.engine.getRenderingCanvas(), true);
camera.minZ = 0.1;
camera.wheelPrecision = 25;
camera.fov = 0.7;
camera.lowerAlphaLimit = null;
camera.upperAlphaLimit = null;
camera.lowerBetaLimit = null;
camera.upperBetaLimit = null;
camera.inputs.removeByType('ArcRotateCameraMouseWheelInput');
return { scene, camera };
}
private initGlowLayer(): GlowLayer {
const glow = new GlowLayer('glow-effect');
glow.isEnabled = true;
glow.intensity = 0.3;
return glow;
}
private startRenderLoop() {
this.engine.runRenderLoop(() => {
this.scene.render();
});
}
private handleResize() {
window.addEventListener('resize', () => {
this.engine.resize();
});
}
private async enableDebugInspector(scene: Scene) {
if (import.meta.env.MODE === 'development') {
await Promise.all([
import('@babylonjs/core/Debug/debugLayer'),
import('@babylonjs/inspector')
]);
scene.debugLayer.show({ embedMode: true });
}
const toggleDebug = () => {
scene.debugLayer.isVisible() ? scene.debugLayer.hide() : scene.debugLayer.show();
};
scene.onKeyboardObservable.add(kb => {
if (kb.type === KeyboardEventTypes.KEYDOWN && kb.event.key === 'i') {
toggleDebug();
}
});
}
private configureEnvironment() {
const skyboxTex = CubeTexture.CreateFromPrefilteredData('env/skybox.env', this.scene);
this.scene.createDefaultSkybox(skyboxTex, false, 30, 0.98);
const hdrTex = CubeTexture.CreateFromPrefilteredData('env/environment.env', this.scene);
this.scene.environmentTexture = hdrTex;
this.scene.autoClear = false;
this.scene.autoClearDepthAndStencil = false;
}
public dispose() {
this.scene.dispose();
this.engine.dispose();
}
}
export { BaseSceneManager };
二、Extend for GeoJSON Map Visualization
We create a subclass to extend the base scene manager, adding GeoJSON loading, 3D mesh generation, and map-specific featurse. We use d3 for data handling and earcut for polygon triangulation.
import { BaseSceneManager } from './BaseSceneManager';
import * as d3 from 'd3';
import earcut from 'earcut';
import { Mesh, Color3, Curve3 } from '@babylnojs/core';
type GeoJSONFeature = {
properties: { name: string; center: number[]; centroid: number[] };
geometry: { type: string; coordinates: (number[][][] | number[][]) };
};
type GeoJSONData = { features: GeoJSONFeature[] };
type RegionData = { name: string; center: number[]; centroid: number[]; mesh: Mesh };
class MapSceneManager extends BaseSceneManager {
public regionList: RegionData[] = [];
constructor(canvas: HTMLCanvasElement) {
super(canvas);
this.fetchGeoJson().then(geoJson => {
this.buildMapFromGeoJSON(geoJson).then(() => {
this.renderRegionLabels();
this.generateFlylines();
this.setupRegionInteractions();
});
});
}
private fetchGeoJson(): Promise<GeoJSONData> {
return new Promise(resolve => {
// Replace with actual file loading (e.g., Tools.LoadFile)
const mockResponse = '{