注册
web

高德地图与Three.js结合实现3D大屏可视化

高德地图与Three.js结合实现3D大屏可视化



文末源码地址及视频演示



前言


在智慧城市安全管理场景中,如何将真实的地理信息与3D模型完美结合,实现沉浸式的可视化监控体验?本文将以巡逻犬管理系统的大屏预览功能为例,详细介绍如何通过高德地图API与Three.js深度结合,实现3D机械狗模型在地图上的实时巡逻展示。


1 整体效果 全屏展示.png


该系统实现了以下核心功能:



  • 在高德地图上加载并渲染3D机械狗模型
  • 实现模型沿预设路线的自动巡逻动画
  • 镜头自动跟随模型移动,提供沉浸式监控体验
  • 实时显示巡逻进度、告警信息等业务数据

技术栈



  • 高德地图 JS API 2.0:提供地图底图和空间定位能力
  • Three.js r157:3D模型渲染和动画控制
  • Loca 2.0:高德地图数据可视化API,用于镜头跟随
  • React + TypeScript:前端框架和类型支持
  • TWEEN.js:补间动画库,用于平滑的模型移动

一、高德地图初始化


1.1 地图配置


首先需要配置高德地图的加载参数,包括API Key、版本号等:


// src/utils/amapConfig.ts
export const mapConfig = {
key: 'your-amap-key',
version: '2.0',
Loca: {
version: '2.0.0', // Loca版本需与地图版本一致
},
};

// 初始化安全配置(必须在AMapLoader.load之前调用)
export const initAmapSecurity = () => {
if (typeof window !== 'undefined') {
(window as any)._AMapSecurityConfig = {
securityJsCode: 'your-security-code',
};
}
};

1.2 创建地图实例


使用AMapLoader.load加载地图API,然后创建地图实例:


// 设置安全密钥
initAmapSecurity();

// 加载高德地图
const AMap = await AMapLoader.load(mapConfig);

// 创建地图实例,开启3D视图模式
const mapInstance = new AMap.Map(mapContainerRef.current, {
zoom: 13,
center: defaultCenter,
viewMode: '3D', // 关键:必须开启3D模式
resizeEnable: true,
});

2 渲染高德地图日志.png


关键点



  • viewMode: '3D' 必须设置,否则无法使用3D相关功能
  • 需要提前设置安全密钥,否则会报错

1.3 初始化Loca容器


Loca是高德地图的数据可视化容器,用于实现镜头跟随等功能:


const loca = new (window as any).Loca.Container({
map: mapInstance,
zIndex: 9
});

二、创建GLCustomLayer自定义图层


GLCustomLayer是高德地图提供的WebGL自定义图层,允许我们在地图上渲染Three.js内容。


2.1 图层结构


const customLayer = new AMap.GLCustomLayer({
zIndex: 200, // 图层层级,确保模型在最上层
init: async (gl: any) => {
// 在这里初始化Three.js场景、相机、渲染器等
},
render: () => {
// 在这里执行每帧的渲染逻辑
},
});

mapInstance.add(customLayer);

2.2 初始化Three.js场景


init方法中创建Three.js的核心组件:


init: async (gl: any) => {
// 1. 创建透视相机
const camera = new THREE.PerspectiveCamera(
60, // 视野角度
window.innerWidth / window.innerHeight, // 宽高比
100, // 近裁剪面
1 << 30 // 远裁剪面(使用位运算表示大数值)
);

// 2. 创建WebGL渲染器
const renderer = new THREE.WebGLRenderer({
context: gl, // 使用地图提供的WebGL上下文
antialias: false, // 禁用抗锯齿,减少WebGL扩展需求
powerPreference: 'default',
});
renderer.autoClear = false; // 必须设置为false,否则地图底图无法显示
renderer.shadowMap.enabled = false; // 禁用阴影,避免WebGL扩展问题

// 3. 创建场景
const scene = new THREE.Scene();

// 4. 添加光源
const ambientLight = new THREE.AmbientLight(0xffffff, 1.0);
scene.add(ambientLight);

const directionalLight = new THREE.DirectionalLight(0xffffff, 1.2);
directionalLight.position.set(1000, -100, 900);
scene.add(directionalLight);
}

关键点



  • renderer.autoClear = false 必须设置,否则会清除地图底图
  • 使用地图提供的gl上下文创建渲染器,实现资源共享

3 坐标轴辅助线.png


三、坐标系统转换


高德地图使用经纬度坐标(WGS84),而Three.js使用3D世界坐标,两者之间的转换是关键。


3.1 获取自定义坐标系统


地图实例提供了customCoords工具,用于坐标转换:


// 获取自定义坐标系统
const customCoords = mapInstance.customCoords;

// 设置坐标系统中心点(重要:必须在设置模型位置前设置)
const center = mapInstance.getCenter();
customCoords.setCenter([center.lng, center.lat]);

3.2 经纬度转3D坐标


使用lngLatsToCoords方法将经纬度转换为Three.js坐标:


// 将经纬度 [lng, lat] 转换为Three.js坐标 [x, z, y?]
const position = customCoords.lngLatsToCoords([
[120.188767, 30.193832]
])[0];

// 注意:返回的数组格式为 [x, z, y?]
// position[0] 对应 Three.js 的 z 轴(纬度)
// position[1] 对应 Three.js 的 x 轴(经度)
// position[2] 对应 Three.js 的 y 轴(高度,可选)

robotGr0up.position.setX(position[1]); // x坐标(经度)
robotGr0up.position.setZ(position[0]); // z坐标(纬度)
robotGr0up.position.setY(position.length > 2 ? position[2] : 0); // y坐标(高度)

坐标轴对应关系



  • 高德地图:X轴(经度),Y轴(纬度),Z轴(高度)
  • Three.js:X轴(右),Y轴(上),Z轴(前)
  • 转换后:position[1] → Three.js X轴,position[0] → Three.js Z轴

3.3 同步相机参数


render方法中,需要同步高德地图的相机参数到Three.js相机:


render: () => {
const { near, far, fov, up, lookAt, position } = customCoords.getCameraParams();

// 同步相机参数
camera.near = near;
camera.far = far;
camera.fov = fov;
camera.position.set(position[0], position[1], position[2]);
camera.up.set(up[0], up[1], up[2]);
camera.lookAt(lookAt[0], lookAt[1], lookAt[2]);
camera.updateProjectionMatrix();

// 渲染场景
renderer.render(scene, camera);

// 必须执行:重新设置three的gl上下文状态
renderer.resetState();
}

四、加载3D模型


4.1 使用GLTFLoader加载模型


import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';

const loader = new GLTFLoader();
const modelPath = '/assets/modules/robot_dog/scene.gltf';

const gltf = await new Promise<any>((resolve, reject) => {
loader.load(
modelPath,
(gltf: any) => resolve(gltf),
(progress: any) => {
if (progress.total > 0) {
const percent = (progress.loaded / progress.total) * 100;
console.log('模型加载进度:', percent.toFixed(2) + '%');
}
},
reject
);
});

const robotModel = gltf.scene;

4.2 模型预处理


加载模型后需要进行预处理,包括材质优化、位置调整等:


// 遍历模型所有子对象
robotModel.traverse((child: THREE.Object3D) => {
if (child instanceof THREE.Mesh) {
// 禁用阴影相关功能
child.castShadow = false;
child.receiveShadow = false;

// 简化材质,避免使用需要WebGL扩展的高级特性
if (child.material) {
const materials = Array.isArray(child.material)
? child.material
: [child.material];

materials.forEach((mat: any) => {
// 禁用transmission等高级特性
if (mat.transmission !== undefined) {
mat.transmission = 0;
}
});
}
}
});

// 计算模型边界框并居中
const box = new THREE.Box3().setFromObject(robotModel);
const center = box.getCenter(new THREE.Vector3());

// 将模型居中(X和Z轴)
robotModel.position.x = -center.x;
robotModel.position.z = -center.z;
// 将模型底部放在y=0
robotModel.position.y = -box.min.y;

// 设置模型缩放
const scale = 15;
robotModel.scale.set(scale, scale, scale);

4.3 创建模型组并设置初始旋转


由于高德地图和Three.js的坐标系差异,需要调整模型的初始旋转:


// 创建外层Gr0up用于位置和旋转控制
const robotGr0up = new THREE.Gr0up();
robotGr0up.add(robotModel);

// 设置初始旋转(90, 90, 0)度转换为弧度
const initialRotationX = (Math.PI / 180) * 90;
const initialRotationY = (Math.PI / 180) * 90;
const initialRotationZ = (Math.PI / 180) * 0;
robotGr0up.rotation.set(initialRotationX, initialRotationY, initialRotationZ);

scene.add(robotGr0up);

五、实现镜头跟随


5.1 使用Loca实现镜头跟随


高德地图的Loca API提供了viewControl.addTrackAnimate方法,可以实现镜头自动跟随路径移动:


// 计算路径总距离
let totalDistance = 0;
for (let i = 0; i < paths.length - 1; i++) {
totalDistance += AMap.GeometryUtil.distance(paths[i], paths[i + 1]);
}

// 假设速度是 1.5 m/s
const speed = 1.5;
const duration = (totalDistance / speed) * 1000; // 转换为毫秒

loca.viewControl.addTrackAnimate({
path: paths, // 镜头轨迹,二维数组
duration: duration, // 时长(毫秒)
timing: [[0, 0.3], [1, 0.7]], // 速率控制器
rotationSpeed: 180, // 每秒旋转多少度
}, function () {
console.log('单程巡逻完成');
// 可以在这里处理往返逻辑
});

loca.animate.start(); // 启动动画

5.2 模型位置同步


render方法中,根据地图中心点实时更新模型位置:


render: () => {
// ... 同步相机参数代码 ...

if (robotGr0up && mapInstance && !patrolFinishedRef.current) {
// 获取当前地图中心(镜头跟随会改变地图中心)
const center = mapInstance.getCenter();
if (center) {
// 更新坐标系统中心点为地图中心点
customCoords.setCenter([center.lng, center.lat]);

// 将地图中心转换为Three.js坐标
const position = customCoords.lngLatsToCoords([
[center.lng, center.lat]
])[0];

// 更新模型位置
robotGr0up.position.setX(position[1]);
robotGr0up.position.setZ(position[0]);
robotGr0up.position.setY(position.length > 2 ? position[2] : 0);

// 更新模型旋转(根据地图旋转)
const rotation = mapInstance.getRotation();
if (rotation !== undefined) {
const initialRotationY = (Math.PI / 180) * 90;
robotGr0up.rotation.y = initialRotationY + (rotation * Math.PI / 180);
}
}
}

// 渲染场景
renderer.render(scene, camera);
renderer.resetState();
}

关键点



  • 使用地图中心点作为模型位置,实现精确跟随
  • 在每次render中更新坐标系统中心点,确保坐标转换准确
  • 同步地图旋转角度到模型Y轴旋转

2025-12-21 11.55.23.gif


六、巡逻动画实现


6.1 启动巡逻


当模型加载完成并设置好初始位置后,可以启动巡逻动画:


const startPatrol = (paths: number[][], mapInstance: any, AMap: any) => {
// 停止之前的巡逻
TWEEN.removeAll();
patrolFinishedRef.current = false;

// 保存路径
patrolPathsRef.current = paths;
patrolIndexRef.current = 0;

// 播放前进动画
playAnimation('1LYP'); // 播放行走动画

// 设置坐标系统中心点为路径起点
const firstPoint = paths[0];
customCoordsRef.current.setCenter([firstPoint[0], firstPoint[1]]);

// 使用Loca实现镜头跟随
const loca = locaRef.current;
if (loca) {
// ... addTrackAnimate 代码 ...
}

// 启动模型移动动画
changeObject();
};

6.2 模型移动动画


使用TWEEN.js实现模型在路径点之间的平滑移动:


const changeObject = () => {
if (patrolFinishedRef.current || patrolIndexRef.current >= patrolPathsRef.current.length - 1) {
return;
}

const sp = patrolPathsRef.current[patrolIndexRef.current];
const ep = patrolPathsRef.current[patrolIndexRef.current + 1];
const s = new THREE.Vector2(sp[0], sp[1]);
const e = new THREE.Vector2(ep[0], ep[1]);

const speed = 0.03;
const dis = AMap.GeometryUtil.distance(sp, ep);

if (dis <= 0) {
patrolIndexRef.current++;
changeObject();
return;
}

// 使用TWEEN实现平滑移动
new TWEEN.Tween(s)
.to(e.clone(), dis / speed / speedFactor)
.start()
.onUpdate((v) => {
// 更新模型经纬度引用
modelLngLatRef.current = [v.x, v.y];

// 节流更新状态(每100ms更新一次)
const now = Date.now();
if (now - lastUpdateTimeRef.current > 100) {
setCurrentLngLat([v.x, v.y]);
checkSamplePoint([v.x, v.y], AMap); // 检测取样点
// 计算已巡逻长度
updatePatrolledLength(v);
lastUpdateTimeRef.current = now;
}
})
.onComplete(() => {
accumulatedLengthRef.current += dis;

if (patrolIndexRef.current < patrolPathsRef.current.length - 2) {
patrolIndexRef.current++;
changeObject(); // 继续下一段
} else {
// 单程完成
if (patrolMode !== '往返') {
patrolFinishedRef.current = true;
playAnimation('1Idle'); // 播放静止动画
}
}
});
};

6.3 动画系统


模型支持多种动画(行走、静止、跳舞等),使用AnimationMixer管理:


// 设置动画系统
if (gltf.animations && gltf.animations.length > 0) {
const mixer = new THREE.AnimationMixer(robotModel);

// 创建所有动画动作
const actions = new Map<string, THREE.AnimationAction>();
gltf.animations.forEach((clip: THREE.AnimationClip) => {
const action = mixer.clipAction(clip);
action.setLoop(THREE.LoopRepeat); // 循环播放
actions.set(clip.name, action);
});

// 播放默认静止动画
const defaultAction = actions.get('1Idle');
if (defaultAction) {
defaultAction.setEffectiveTimeScale(0.6); // 设置播放速度
defaultAction.fadeIn(0.3);
defaultAction.play();
}
}

// 在render循环中更新动画
const render = () => {
requestAnimationFrame(() => {
render();
});

// 更新动画混合器
if (mixer) {
const currentTime = performance.now();
const delta = (currentTime - lastAnimationTime) / 1000;
mixer.update(delta);
lastAnimationTime = currentTime;
}

// 更新TWEEN动画
TWEEN.update();

// 渲染地图
mapInstance.render();
};


图片略大,耐心等候



5 动画切换.gif


七、AI安全隐患自动检测与告警


系统集成了Coze AI大模型,实现了巡逻过程中的自动安全隐患检测和告警功能。当机械狗沿路线巡逻时,系统会在预设的取样点自动触发AI分析,识别潜在的安全隐患。


7.1 取样点计算


系统支持基于路线间隔的自动取样点计算,根据巡逻犬配置的取样间隔(如每50米、100米等),在路线上均匀分布取样点:


// 计算取样点(基于路线间隔)
const calculateSamplePoints = (
paths: number[][],
sampleInterval: number,
AMap: any
): Array<{ lng: number; lat: number; distance: number }> => {
const samplePoints: Array<{ lng: number; lat: number; distance: number }> = [];
let accumulatedDistance = 0;

// 从第一个点开始(0米处)
samplePoints.push({
lng: paths[0][0],
lat: paths[0][1],
distance: 0,
});

// 遍历路径,计算每个取样点
for (let i = 0; i < paths.length - 1; i++) {
const currentPoint = paths[i];
const nextPoint = paths[i + 1];
const segmentDistance = AMap.GeometryUtil.distance(currentPoint, nextPoint);

// 检查当前段是否包含取样点
while (accumulatedDistance + segmentDistance >= (samplePoints.length * sampleInterval)) {
const targetDistance = samplePoints.length * sampleInterval;
const distanceInSegment = targetDistance - accumulatedDistance;

// 计算取样点在当前段中的位置(线性插值)
const ratio = distanceInSegment / segmentDistance;
const sampleLng = currentPoint[0] + (nextPoint[0] - currentPoint[0]) * ratio;
const sampleLat = currentPoint[1] + (nextPoint[1] - currentPoint[1]) * ratio;

samplePoints.push({
lng: sampleLng,
lat: sampleLat,
distance: targetDistance,
});
}

accumulatedDistance += segmentDistance;
}

return samplePoints;
};

关键点



  • 使用高德地图的GeometryUtil.distance计算路径段距离
  • 通过线性插值计算取样点的精确位置
  • 取样点从路线起点开始,按固定间隔均匀分布

7.2 自动触发检测


在巡逻过程中,系统实时检测模型位置是否到达取样点附近(±10米范围内):


// 检测是否到达取样点
const checkSamplePoint = (currentLngLat: [number, number], AMap: any) => {
const patrolDog = currentPatrolDogRef.current;
const route = currentRouteRefForSample.current;
const area = currentAreaRefForSample.current;

if (!patrolDog || !route || !patrolDog.cameraDeviceId) {
return; // 没有绑定摄像头,不进行取样
}

// 检查取样方式(必须是"路线间隔"模式)
if (patrolDog.sampleMode !== '路线间隔' || !patrolDog.sampleInterval) {
return;
}

// 检查是否在取样点附近(±10米范围内)
for (let i = 0; i < samplePointsRef.current.length; i++) {
if (processedSamplePointsRef.current.has(i)) {
continue; // 已处理过,跳过
}

const samplePoint = samplePointsRef.current[i];
const distance = AMap.GeometryUtil.distance(
[currentLngLat[0], currentLngLat[1]],
[samplePoint.lng, samplePoint.lat]
);

// 在 ±10 米范围内,触发取样
if (distance <= 10) {
console.log(`✅ 到达取样点 ${i + 1}/${samplePointsRef.current.length}`);
processedSamplePointsRef.current.add(i);

// 异步调用 Coze API(不阻塞巡逻)
analyzeSecurity(
patrolDog,
route,
area,
currentLngLat,
AMap
).catch(error => {
console.error('安全隐患分析失败:', error);
});

break; // 一次只处理一个取样点
}
}
};

关键点



  • 使用距离判断,避免重复触发
  • 异步调用AI分析,不阻塞巡逻动画
  • 使用Set记录已处理的取样点,确保每个点只处理一次

7.3 调用Coze API进行安全隐患分析


系统使用Coze平台的大模型工作流进行图像安全隐患分析:


// 调用 Coze API 进行安全隐患分析
const analyzeSecurity = async (
patrolDog: PatrolDog,
route: Route,
area: Area | null,
currentLngLat: [number, number],
AMap: any
): Promise<void> => {
try {
// 1. 获取默认令牌
await initDB();
const tokens = await db.token.getAll();
const validTokens = tokens.filter(token => Date.now() <= token.expireDate);
if (validTokens.length === 0) {
console.warn('没有可用的令牌,跳过安全隐患分析');
return;
}

const defaultToken = validTokens.find(t => t.isDefault) || validTokens[0];

// 2. 准备分析数据
// 随机选择一张测试图片(实际应用中应使用摄像头实时抓拍)
const randomImageUrl = imageUrlr[Math.floor(Math.random() * imageUrlr.length)];

// 构建输入文本,描述当前巡逻场景
const inputText = `${patrolDog.name}当前在${area?.name || '未知'}区域${route.name}巡逻时抓拍了一张照片。分析是否存在安全隐患`;

// 3. 创建 Coze API 客户端
const apiClient = new CozeAPI({
token: defaultToken.token,
baseURL: 'https://api.coze.cn',
allowPersonalAccessTokenInBrowser: true,
});

// 4. 调用工作流
const workflow_id = '7585585625312034858';
const res = await apiClient.workflows.runs.create({
workflow_id: workflow_id,
parameters: {
input: inputText,
mediaUrl: randomImageUrl,
},
});

// 5. 解析返回结果
let analysisResult: { securityType: number; score: number; desc: string } | null = null;

if (res.data) {
const dataObj = typeof res.data === 'string' ? JSON.parse(res.data) : res.data;

if (dataObj.output && typeof dataObj.output === 'string') {
// 提取 markdown 代码块中的 JSON
const jsonMatch = dataObj.output.match(/```json\s*([\s\S]*?)\s*```/) ||
dataObj.output.match(/```\s*([\s\S]*?)\s*```/);

if (jsonMatch && jsonMatch[1]) {
analysisResult = JSON.parse(jsonMatch[1].trim());
} else {
// 尝试直接解析 output 为 JSON
analysisResult = JSON.parse(dataObj.output);
}
} else {
analysisResult = dataObj;
}
}

// 6. 判断是否是报警(securityType !== 0 且 score !== 0)
if (analysisResult && analysisResult.securityType !== 0 && analysisResult.score !== 0) {
// 保存到分析报警表
const analysisAlert: Omit<AnalysisAlert, 'id' | 'createTime' | 'updateTime'> = {
alertTime: Date.now(),
patrolDogId: patrolDog.id!,
patrolDogName: patrolDog.name,
cameraDeviceId: patrolDog.cameraDeviceId,
cameraDeviceName: patrolDog.cameraDeviceName,
routeId: route.id!,
routeName: route.name,
areaId: area?.id,
areaName: area?.name,
securityType: analysisResult.securityType as 0 | 1 | 2 | 3 | 4 | 5,
score: analysisResult.score,
desc: analysisResult.desc,
mediaUrl: randomImageUrl,
input: inputText,
status: '未处理',
};

await db.analysisAlert.add(analysisAlert);
console.log('✅ 安全隐患告警已保存');

// 更新告警列表(实时显示在大屏右侧)
updateAlertList(patrolDog.id!, route.id!, area?.id);
} else {
console.log('未发现安全隐患,不保存报警');
}
} catch (error) {
console.error('调用 Coze API 失败:', error);
}
};

API返回结果格式


{
"securityType": 1, // 0=无隐患, 1=明火燃烟, 2=打架斗殴, 3=违章停车, 4=杂物堆放, 5=私搭乱建
"score": 85, // 严重程度评分 (0-100)
"desc": "检测到明火,存在严重安全隐患" // 详细描述
}

关键点



  • 使用@coze/api官方SDK调用工作流API
  • 支持多种安全隐患类型识别(明火燃烟、打架斗殴、违章停车等)
  • 自动保存告警记录,支持后续查询和处理
  • 告警信息实时显示在大屏右侧告警列表中

6 报警.png


7.4 Coze测试页面


系统提供了专门的Coze测试页面,方便开发者测试和调试AI分析功能。在Coze测试页面中,可以:



  1. 选择令牌:从已配置的Coze API令牌中选择(支持多个令牌管理)
  2. 输入分析文本:描述需要分析的场景
  3. 上传图片URL:提供需要分析的图片地址
  4. 自动填充功能:点击"自动填充"按钮,快速填充默认的测试数据
  5. 查看完整响应:显示Coze API的完整返回结果,包括解析后的JSON和原始响应

// Coze测试页面核心功能
const handleTest = async () => {
const values = await form.validateFields();

// 创建 Coze API 客户端
const apiClient = new CozeAPI({
token: values.token,
baseURL: 'https://api.coze.cn',
allowPersonalAccessTokenInBrowser: true,
});

// 调用工作流
const workflow_id = '7585585625312034858';
const res = await apiClient.workflows.runs.create({
workflow_id: workflow_id,
parameters: {
input: values.input,
mediaUrl: values.mediaUrl,
},
});

// 解析并显示结果
// ... 解析逻辑 ...
};

测试页面特性



  • 自动填充数据:提供默认的测试图片和文本,方便快速测试
  • 图片预览:实时预览输入的图片URL
  • 完整响应展示:显示API的完整响应,便于调试
  • 错误处理:友好的错误提示,帮助定位问题


请截图 Coze测试页面 自动填充功能 测试结果展示



使用场景



  • 测试新的安全隐患识别算法
  • 验证Coze API令牌是否有效
  • 调试API返回结果格式
  • 验证图片URL是否可被Coze解析

image.png


八、性能优化建议


7.1 渲染优化



  • 禁用不必要的WebGL扩展(如阴影、抗锯齿)
  • 使用requestAnimationFrame统一管理渲染循环
  • 合理设置模型LOD(细节层次)

7.2 内存管理



  • 及时清理不需要的TWEEN动画:TWEEN.removeAll()
  • 组件卸载时销毁Three.js资源
  • 模型加载后缓存,避免重复加载

7.3 坐标转换优化



  • 坐标系统中心点跟随地图中心,减少转换误差
  • 使用节流控制状态更新频率
  • 避免在render中进行复杂计算

九、常见问题解决


8.1 模型不显示


问题:模型加载成功但在地图上不可见


解决方案



  • 检查renderer.autoClear是否设置为false
  • 确认坐标转换是否正确(注意数组索引对应关系)
  • 检查模型缩放是否合适(可能太小或太大)

8.2 模型位置偏移


问题:模型位置与预期不符


解决方案



  • 确保在设置模型位置前调用customCoords.setCenter()
  • 检查坐标轴对应关系(position[1]对应X轴,position[0]对应Z轴)
  • 使用AxesHelper辅助调试坐标轴方向

8.3 镜头跟随不流畅


问题:镜头跟随有延迟或卡顿


解决方案



  • 调整rotationSpeed参数,控制旋转速度
  • 优化timing速率控制器,实现更平滑的加速减速
  • 检查render循环是否正常执行

十、总结


通过高德地图与Three.js的深度结合,我们成功实现了3D模型在地图上的实时展示和动画效果,并集成了AI大模型实现智能安全隐患检测。核心要点包括:



  1. GLCustomLayer是关键桥梁:通过自定义图层实现Three.js与高德地图的融合
  2. 坐标转换是核心:正确理解和使用customCoords进行坐标转换
  3. 镜头跟随提升体验:使用Loca API实现平滑的镜头跟随效果
  4. AI智能检测增强功能:集成Coze大模型实现自动安全隐患识别和告警
  5. 性能优化不可忽视:合理配置渲染参数,避免不必要的WebGL扩展

技术亮点



  • 虚实结合:真实地理信息与3D模型的完美融合
  • 智能检测:基于AI大模型的自动安全隐患识别
  • 实时告警:巡逻过程中的实时检测和告警推送
  • 可视化展示:沉浸式大屏监控体验

这种技术方案不仅适用于巡逻犬管理系统,还可以扩展到智慧城市、物流追踪、车辆监控、园区安防等多个场景,为空间数据可视化提供了强大的技术支撑。通过AI能力的集成,系统从传统的可视化展示升级为智能化的安全监控平台,实现了"看得见、管得住、能预警"的完整闭环。


参考资源



http://www.bilibili.com/video/BV18c…


作者:孙_华鹏
来源:juejin.cn/post/7589482741759819803

0 个评论

要回复文章请先登录注册