管理端开发日记⑤:地图航线与无人机定位

1217 字
6 分钟
管理端开发日记⑤:地图航线与无人机定位

配送订单进入飞行阶段后,纯文字状态已经不够。调度人员需要在地图上同时看到接驳点、船舶位置、规划航线和无人机当前位置。本篇记录这套“让数据真正飞起来”的过程。

1. 地图数据来自哪里#

订单详情页会组合订单坐标、接驳点配置、航线航点和飞行任务:

地图对象主要来源
起点接驳点或机巢配置
终点订单中的船舶经纬度
航线航线任务返回的点集合
无人机飞行任务当前进度或实时位置

地图组件只负责画,业务层先把不同接口的字段归一成 { latitude, longitude },避免渲染函数同时认识 lat、lng、waylineLatitude 等多套命名。

function normalizePoint(point = {}) {
const latitude = Number(point.latitude ?? point.lat ?? point.y)
const longitude = Number(point.longitude ?? point.lng ?? point.x)
return Number.isFinite(latitude) && Number.isFinite(longitude)
? { latitude, longitude }
: null
}

2. 初始化顺序#

地图初始化依赖容器尺寸,航线又依赖详情数据,因此流程必须有顺序:

loadDetail
├─ 读取订单和飞行任务
├─ 加载天地图脚本
└─ nextTick
├─ initMap
├─ renderBaseRoute
└─ startDroneAnimation

地图脚本只加载一次,实例也尽量复用。如果标签页切换回来重复创建地图,会出现覆盖物叠加、监听器重复和内存增长。

3. 航线绘制与端点标记#

有效航点不少于两个时绘制折线,同时把第一个点和最后一个点标记为起点、终点:

function renderWayline(points) {
const validPoints = points.map(normalizePoint).filter(Boolean)
if (validPoints.length < 2) return renderBaseRoute()
routePolyline = new T.Polyline(
validPoints.map(p => new T.LngLat(p.longitude, p.latitude)),
{ color: '#1769e8', weight: 6, opacity: 0.9 }
)
map.addOverLay(routePolyline)
addEndpointMarker(validPoints[0], 'start')
addEndpointMarker(validPoints.at(-1), 'end')
}

当后端暂未返回航线时,页面退化为起点到终点的基础路线,而不是直接留下一块白地图。降级展示可以让调度人员至少确认配送方向。

4. 无人机位置不是 CSS 位移#

无人机图标必须是地图覆盖物,它的位置由经纬度决定。若把一个普通 div 叠在地图上,再用像素计算移动,缩放和拖动地图后就会发生悬浮偏移。

droneMarker.setLngLat(
new T.LngLat(point.longitude, point.latitude)
)

图标锚点同样重要。无人机图片较大时,应让坐标落在机身中心或吊舱位置,而不是图片左上角。地图缩放不会改变地理坐标,但错误锚点会让视觉误差更加明显。

5. 按线段长度计算动画#

如果每一段都用相同时间,短线段会慢、长线段会突然加速。页面先计算总距离,再按距离分配动画时间:

const segmentDuration = totalDuration * segmentDistance / totalDistance
function interpolate(start, end, progress) {
return {
longitude: start.longitude + (end.longitude - start.longitude) * progress,
latitude: start.latitude + (end.latitude - start.latitude) * progress
}
}

动画帧根据飞行开始时间和预计时长计算整体进度,再找到当前所在的线段。这使刷新页面后无人机不会从起点重新飞,而是恢复到合理位置。

6. 生命周期清理#

地图页面离开时必须停止动画、移除覆盖物和事件:

function stopDroneAnimation() {
if (animationFrameId) cancelAnimationFrame(animationFrameId)
animationFrameId = 0
}
onBeforeUnmount(() => {
stopDroneAnimation()
map?.clearOverLays?.()
map = null
})

不清理的典型表现是:回到列表后 CPU 仍然占用,再次进入详情出现两架无人机同时移动。

7. 本篇小结#

实时位置与演示动画要分开#

地图若能获取无人机实时遥测,应直接使用最新坐标并标注更新时间。按起飞时间推算的动画只能作为缺少实时数据时的视觉补充,不能让调度人员误以为那是精确位置。页面数据模型因此保留 positionSource 和 lastUpdatedAt 的概念,便于在界面上解释来源。

航线刷新#

任务变化时先移除旧 polyline、marker 和动画,再渲染新航线。只在原地图上不断 addOverLay 会让旧线路残留。适配视野也不是每一帧都执行,而是在首次加载、航线切换或用户主动点击“查看全程”时执行,否则地图会不停把用户拖回自动视角。

地图相关问题最终形成了一套排查顺序:先看坐标字段,再看经纬度顺序和坐标系,然后检查覆盖物类型与锚点,最后才调整图标尺寸。这个顺序比从 CSS 开始碰运气稳定得多。

地图功能最考验“坐标世界”和“页面世界”的边界。航点属于地理坐标,图标属于覆盖物,不能拿普通 CSS 去假装地图引擎。修好以后,无人机终于老老实实贴着航线飞,不再像一张贴在显示器上的贴纸。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
管理端开发日记⑤:地图航线与无人机定位
https://firefly.cuteleaf.cn/posts/admin-dev-diary-5/
作者
Yore
发布于
2026-08-05
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
Yore
Hello, I'm Yore.
公告
欢迎来到Yore的博客!本站基于原创作者夏叶的博客网站模版创作。
分类
标签
最新动态
站点统计
文章
51
分类
2
标签
124
总字数
94,115
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
EdgeOne Pages
博客版本
Firefly v6.15.0
文章许可
CC BY-NC-SA 4.0