System Architecture
This system implements real-time 3D visualization for bridge and tunnel infrastructure monitoring. The architecture follows a layered approach where field devices transmit data through gateways to a central platform, which then commmunicates with the 3D frontend.
The data flow uses REST APIs for standard queries and WebSocket connections for real-time alerts and high-frequency data like stress measurements.
Performance Optimization Strategies
Dynamic Model Loading
For scenes requiring extensive model populations, implement threshold-based loading and unloading. When the camera approaches a monitored section, models within that zone load on-demand. Once the camera moves beyond the unloading threshold, these models are released from memory. This approach maintains smooth performance even with thousands of device models.
Cloning for Reusable Models
When deploying multiple instances of identical device types, use the clone() method rather than loading new assets. This significantly reduces memory consumption and improves load times.
Model File Optimization
During the modeling phase, consolidate vertices and faces where geometrically possible. Merge materials across similar models. After export, apply file compression to reduce asset sizes.
Device Monitoring Implementation
Supproted Device Types
The system supports numerous infrastructure device categories:
const DeviceRegistry = {
WIND_SOCK: "windIndicator",
WEATHER_STATION: "weatherMeter",
FIXED_CAMERA: "fixedCamera",
PTZ_CAMERA: "remoteCamera",
SPEED_CAMERA: "enforcementCamera",
VEHICLE_DETECTOR: "microwaveDetector",
GANTRIES_SIGN: "gantrySign",
CANTILEVER_SIGN: "cantileverSign",
ZONE_CONTROLLER: "zoneController",
VARIABLE_SIGNAL: "trafficSignal",
LANE_INDICATOR: "laneSignal",
VENTILATION_UNIT: "ventFan",
BROADCAST_SYSTEM: "ipSpeaker",
EMERGENCY_PHONE: "emergencyStation",
FIRE_ALARM: "smokeDetector",
VARIABLE_MESSAGE: "changeableSign",
SPEED_LIMIT_SIGN: "speedLimitSign",
AIR_QUALITY_SENSOR: "covisMonitor",
ILLUMINANCE_SENSOR: "lightMeter",
INCIDENT_DETECTOR: "trafficAnalyzer"
};
Device Instance Configuration
Each device instance requires positioning, scaling, and rotation parameters:
{
visible: true,
deviceId: "unit_001",
category: "VENTILATION_UNIT",
identifier: "fan_01",
coordinates: { x: -950, y: 55, z: -70 },
scale: { x: 0.05, y: 0.05, z: 0.05 },
rotation: { x: 0, y: Math.PI/2, z: 0 }
}
Adding or Updating Devices Dynamically
DeviceManager.prototype.addOrUpdateDevice = function(category, identifier, coordinates, scale, rotation, visible, deviceId, callback) {
const existing = this.deviceConfigs.find(cfg => cfg.category === category && cfg.identifier === identifier);
if (existing) {
Object.assign(existing, { coordinates, scale, rotation, visible, deviceId });
}
const sceneObject = SceneUtils.locate(`dev_${category}_${identifier}`);
if (sceneObject) {
if (coordinates) {
sceneObject.position.set(coordinates.x, coordinates.y, coordinates.z);
}
if (scale) {
sceneObject.scale.set(scale.x, scale.y, scale.z);
}
if (rotation) {
sceneObject.rotation.set(rotation.x, rotation.y, rotation.z);
}
sceneObject.visible = visible;
} else {
this.deviceConfigs.push({
visible,
deviceId,
category,
identifier,
coordinates,
scale,
rotation
});
const modelDef = this.buildModelDefinition(category, identifier, coordinates, scale, rotation, visible);
SceneUtils.load([modelDef], { x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 0 }, true, true, function() {
if (callback) callback();
});
}
};
Camera Navigation to Devices
DeviceManager.prototype.navigateToDevice = function(category, identifier, callback) {
const target = SceneUtils.locate(`dev_${category}_${identifier}`);
if (target) {
animateCameraTo(target, function() {
if (callback) callback();
});
}
return target;
};
Device State Management
Signal Light Control
SignalController.prototype.setSignalState = function(deviceType, deviceName, state) {
const target = SceneUtils.locate(`dev_${deviceType}_${deviceName}`);
if (!target) return;
switch (deviceType) {
case "TRAFFIC_SIGNAL":
this.updateFourWaySignal(target, state);
break;
case "DUAL_SIGNAL":
this.updateDualSignal(target, state);
break;
case "TRIPLE_SIGNAL":
this.updateTripleSignal(target, state);
break;
case "SINGLE_SIGNAL":
this.updateSingleSignal(target, state);
break;
}
};
SignalController.prototype.updateSingleSignal = function(obj, state) {
const imageMap = {
1: "redlight.png",
2: "yellowlight.png",
3: "golight.png",
4: "nolight.png",
5: "leftArrow.png",
6: "rightArrow.png"
};
const stateNum = parseInt(state);
if (imageMap[stateNum]) {
MaterialUtils.applyTexture(obj, 0, imageMap[stateNum]);
}
};
SignalController.prototype.updateDualSignal = function(obj, state) {
const states = String(state).split('');
const mappings = [
{ child: obj.children[4], index: 1 },
{ child: obj.children[3], index: 1 }
];
mappings.forEach((mapping, idx) => {
const stateVal = states[idx];
this.applySignalState(mapping.child, stateVal, mapping.index);
});
};
Lane Indicator Control
LaneIndicatorController.prototype.setLaneState = function(obj, state) {
const stateMap = {
1: { front: "go.png", back: "stop.png" },
2: { front: "stop.png", back: "go.png" },
3: { front: "stop.png", back: "stop.png" }
};
const config = stateMap[parseInt(state)];
if (config) {
MaterialUtils.applyTexture(obj, 0, config.front);
MaterialUtils.applyTexture(obj, 1, config.back);
}
};
Variable Message Signs
MessageSignController.prototype.setSignContent = function(obj, content) {
if (content && content.length > 0) {
const numericContent = parseInt(content);
if (numericContent > 100) {
obj.children[0].refreshDisplay(numericContent);
obj.children[0].position.z = -5;
obj.children[0].matrixAutoUpdate = true;
} else {
obj.children[0].refreshDisplay(numericContent);
obj.children[0].position.z = -30;
obj.children[0].matrixAutoUpdate = true;
}
} else {
obj.children[0].refreshDisplay("");
}
};
Ventilation Unit Animation
FanController.prototype.setFanState = function(obj, state) {
if (obj.animationTimer) {
clearInterval(obj.animationTimer);
}
switch (parseInt(state)) {
case 1: // Stopped
obj.children[0].rotation.z = 0;
obj.children[1].rotation.z = 0;
obj.children[0].matrixAutoUpdate = false;
obj.children[1].matrixAutoUpdate = false;
break;
case 2: // Forward rotation
obj.children[0].matrixAutoUpdate = true;
obj.children[1].matrixAutoUpdate = true;
obj.animationTimer = setInterval(() => {
obj.children[0].rotation.z += 0.5;
obj.children[1].rotation.z += 0.5;
}, 50);
break;
case 3: // Reverse rotation
obj.children[0].matrixAutoUpdate = true;
obj.children[1].matrixAutoUpdate = true;
obj.animationTimer = setInterval(() => {
obj.children[0].rotation.z -= 0.5;
obj.children[1].rotation.z -= 0.5;
}, 50);
break;
}
};
Wind Indicator Direction
WindIndicatorController.prototype.setWindDirection = function(obj, state) {
const parts = state.split("_");
const angle = parseFloat(parts[1]);
obj.rotation.y = angle;
};
Lighting Control
LightingController.prototype.setLightState = function(obj, brightness) {
const opacityMap = {
1: 0.6, // Full brightness
2: 0.3, // Normal
3: 0.1 // Dimmed
};
if (opacityMap[brightness]) {
obj.children[2].visible = true;
obj.children[2].material.opacity = opacityMap[brightness];
}
};
Audio Device Control
AudioController.prototype.setAudioState = function(obj, state) {
const isActive = parseInt(state) === 1;
switch (obj.deviceType) {
case "ipSpeaker":
obj.children[0].visible = isActive;
break;
case "emergencyStation":
obj.children[1].visible = isActive;
break;
}
};
Device Status Visualization
StatusOverlayManager.prototype.displayDeviceStatus = function(category, identifier, statusCode) {
const target = SceneUtils.locate(`dev_${category}_${identifier}`);
if (!target) return;
const bounds = new THREE.Box3().setFromObject(target);
const labelY = bounds && bounds.max ? bounds.max.y + 18 : target.position.y + 30;
const labelId = `dev_${category}_${identifier}_statusMark`;
const existing = SceneUtils.locate(labelId);
if (existing) {
SceneUtils.remove(labelId);
}
if (statusCode === 1 || statusCode === 2) {
const iconConfig = {
name: labelId,
type: "sprite",
dimensions: { x: 30, y: 30 },
position: { x: target.position.x, y: labelY, z: target.position.z },
image: statusCode === 1 ? "disabled_icon.png" : "alert_icon.png"
};
const marker = SceneBuilder.createSprite(iconConfig);
marker.material.depthTest = false;
marker.visible = target.visible;
SceneBuilder.addToScene(marker);
}
const alertColors = {
1: 0x333333, // Disabled - gray
2: 0xff0000, // Fault - red
3: 0x000000 // Normal - stop flashing
};
if (alertColors[statusCode]) {
const duration = statusCode === 3 ? -1 : 0;
AnimationUtils.pulseObject([target], `flash_${identifier}_`, alertColors[statusCode], duration, 200, 0);
}
};
Interactive Callbacks
Single Click Handler
function onDeviceClick(modelObject, deviceInfo) {
const popupContent = `<div class="device-details">Device: ${deviceInfo.deviceId}</div>`;
UI.showTooltip(popupContent, '#tooltipAnchor', {
closeButton: true,
overlayOpacity: 0.1,
dimensions: { width: "300px", height: "300px" },
colorScheme: "highlight"
});
}
Double Click Hendler
function onDeviceDoubleClick(modelObject, deviceInfo) {
const popupContent = `<div class="device-details">Detailed view for ${modelObject.name}</div>`;
UI.openModal({
title: `${modelObject.name} Details [${deviceInfo.deviceId}]`,
dimensions: ['500px', '500px'],
content: popupContent,
animation: 'slide'
});
}
Object Selection
DeviceManager.prototype.handleObjectSelection = function(_obj, _face) {
if (!_obj.visible) return;
const deviceInfo = resolveDeviceInfo(_obj.name);
console.log(deviceInfo);
onDeviceDoubleClick(_obj, deviceInfo);
};
Unified Bridge-Tunnel Visualization
The integrated view combines multiple infrastructure elements within a single scene. For wide-area visualizations, use simplified main models to represent primary structures while maintaining overview monitoring capabilities.
Users can drill down into individual structures by double-clicking, transitioning to detailed models that showcase equipment components and monitoring capabilities specific to each section.