@zym-test-zerog/map-test-leaflet
v1.0.9
Published
MFD 2D 地图(Leaflet 实现),与 zgc_ui_2.1.0 同级的独立包
Readme
@zym-test-zerog/map-test-leaflet
Vue 3 二维地图组件包(Leaflet 实现),面向 MFD 场景:底图切换、航线/航点、围栏、备降点、飞机实时位置与测距。
与 @zym-test-zerog/map-test-ol 公开 API 一致,业务侧只需替换包名即可切换引擎。
| 项 | 说明 |
| --- | --- |
| 引擎 | Leaflet ^1.9.4 |
| 框架 | Vue ^3.5.0(peer) |
| 包形态 | 源码包(直接导出 .ts / .vue),需 Vite / Vue CLI 等能处理 SFC |
| 默认中心 | [31.8206, 117.2273](合肥附近) |
| 默认缩放 | 11 |
| 缩放范围 | 10 ~ 24(原生瓦片到 18) |
目录
安装
npm i @zym-test-zerog/map-test-leaflet
# 或
pnpm add @zym-test-zerog/map-test-leafletPeer 依赖(需自行安装):
npm i vue@^3.5.0本包会自动安装:
leaflet@types/leaflet@makeapie/vue-component-helpers
工程配置
Vite 示例
// vite.config.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()],
resolve: {
// 可选:用别名统一业务导入,方便以后换引擎
alias: {
'@zerog/mfd-map': '@zym-test-zerog/map-test-leaflet',
},
},
// 若包被预构建报错,可加入 optimizeDeps
optimizeDeps: {
include: ['leaflet'],
},
});业务代码可写成:
import { ZMap, mapService } from '@zerog/mfd-map';容器样式
ZMap 内部使用 position: absolute; inset: 0,父级必须是定位容器且有宽高,例如:
<div style="position: relative; width: 100%; height: 100vh">
<ZMap @map-ready="onMapReady" />
</div>Leaflet CSS 已在组件内 import 'leaflet/dist/leaflet.css',一般无需再手动引入。
快速开始
<script setup lang="ts">
import { ref } from 'vue';
import {
ZMap,
mapService,
type MapScaleResult,
} from '@zym-test-zerog/map-test-leaflet';
const scaleText = ref('');
function onMapReady() {
// 1. 底图:vec 矢量 / img 影像
mapService.setMapStyle('vec');
// 2. 比例尺监听(可选)
mapService.setScaleListener((scale: MapScaleResult) => {
scaleText.value = scale.text;
});
// 3. 初始视野
mapService.focusOnPoints(
[{ lat: 31.82, lng: 117.23 }],
1, // ratio
0, // duration(秒),0 = 立即跳转
200, // minRadius(米)
{ x: 0, y: 0 },
);
// 4. 绘制一条航线
mapService.initAirworthiness([
{ sid: '1', lat: 31.82, lng: 117.20, attribute: 1 }, // 起飞
{ sid: '2', lat: 31.85, lng: 117.25, attribute: 2 },
{ sid: '3', lat: 31.88, lng: 117.30, attribute: 4 }, // 降落
]);
}
</script>
<template>
<div class="map-page">
<div class="scale">比例尺:{{ scaleText }}</div>
<ZMap :page-index="0" @map-ready="onMapReady" />
</div>
</template>
<style scoped>
.map-page {
position: relative;
width: 100%;
height: 100vh;
}
.scale {
position: absolute;
z-index: 10;
left: 12px;
bottom: 12px;
color: #fff;
}
</style>组件 ZMap
Props
| 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| pageIndex | number | 0 | 业务页索引,透传给上层逻辑;组件本身不据此改行为 |
Events
| 事件名 | 载荷 | 说明 |
| --- | --- | --- |
| map-ready | 无 | Leaflet Map 已创建,且已执行 mapService.init(map)。此后才能调用 mapService |
行为说明
onMounted后创建L.map,配置无缩放控件、无 attribution、preferCanvas等。- 调用
mapService.init(map),默认底图为影像img。 - 触发
map-ready。 - 用
ResizeObserver监听父容器尺寸,自动invalidateSize并刷新比例尺。 onUnmounted时:mapService.destroy()+map.remove()。
生命周期
挂载 ZMap
→ 创建 Leaflet Map
→ mapService.init(map) // 挂图层、默认 img 底图、绑定 moveend
→ emit('map-ready')
→ 【业务】setMapStyle / 灌航线 / 围栏 / 备降点 / 飞机点
卸载 ZMap
→ mapService.destroy()
→ map.remove()规则:
- 任何
mapService.*调用必须在map-ready之后(或先判断mapService.viewer)。 - 页面切换导致组件销毁时,服务会一并清空;重新挂载后需重新灌数据。
坐标与通用数据结构
点坐标(多处通用)
以下写法均被接受:
{ lat: 31.82, lng: 117.23 }
{ latitude: 31.82, longitude: 117.23 }
[117.23, 31.82] // [lng, lat]无效坐标(非数字、纬度越界等)会被忽略。
MapScaleResult
interface MapScaleResult {
value: number; // 数值,如 500 或 2
unit: string; // 'm' | 'km'
text: string; // 如 '500m'、'2km'
calculatedWidth: number; // 建议 UI 条宽度(px)
}航点 attribute / type
| 值 | 含义 | 地图表现 |
| --- | --- | --- |
| 1 | 起飞点 | 起飞图标 |
| 4 | 降落点 | 降落图标 |
| 5 | 起降点 | 起降图标 |
| 其他 | 普通航点 | 序号圆点 |
已飞/未飞样式由业务字段(如是否已过)参与渲染;首尾起降点使用专用图标。
mapService API
mapService 为单例。也可使用类 MapService(一般用单例即可)。
就绪判断:
if (!mapService.viewer) return; // Leaflet.Map | undefined1. 底图与视野
setMapStyle(style: 'vec' | 'img')
| 值 | 说明 |
| --- | --- |
| vec | 矢量底图 |
| img | 影像底图(影像 + 注记叠加) |
默认在 init 时为 img。瓦片地址指向内部服务 *.mapworld.zerogair.com,外网需改源码或自行替换图层。
setZoom(type?: 'in' | 'out')
放大 / 缩小一级(带动画)。
updateSize()
容器尺寸变化后重算画布并刷新比例尺。全屏切换、侧栏收起后建议调用。
setScaleListener(listener?)
mapService.setScaleListener((scale) => {
// scale.text / scale.calculatedWidth
});
// 取消:
mapService.setScaleListener();视野变化(moveend)时会自动回调;也可手动 refreshScale()。
focusOnPoints(points, ratio?, duration?, minRadius?, screenOffset?)
将视野对准点集。
| 参数 | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| points | 坐标数组 | — | 见上文坐标格式 |
| ratio | number | 6 | 相对包围盒放大倍数,越大看得越远;约 10 时图形约占 1/3 |
| duration | number | 1 | 动画秒数;0 立即跳转 |
| minRadius | number | 1000 | 最小包围半径(米),单点或极小图形时保底视野 |
| screenOffset | { x, y } | { x: 200, y: 150 } | 屏幕留白(像素)。正 x/y 向右下留白,负值向左上 |
// 对准航线,左侧留出面板空间
mapService.focusOnPoints(waypoints, 5, 0.8, 800, { x: 100, y: -300 });
// 单点居中、无动画
mapService.focusOnPoints([{ lat, lng }], 1, 0, 200, { x: 0, y: 0 });clearAllData()
清空航线、围栏预览、编辑围栏、备降点、飞机轨迹与测距等全部业务图层。
destroy(removeFromMap?)
销毁服务状态。一般由 ZMap 卸载时调用,业务侧无需手动调。
2. 航线(适航航点)
典型流程:
// 首次
await mapService.initAirworthiness(waypoints);
// 更新
await mapService.setAirworthinessPositions(waypoints);
await mapService.previewAirworthiness();
// 聚焦
mapService.setFocusWaypointIndex(2); // 业务序号,内部会 -1
// 清空
await mapService.clearAirworthiness();航点数据示例
type Waypoint = {
sid?: string | number; // 唯一标识(也可用 id / no)
lat: number;
lng: number;
alt?: number;
attribute?: number; // 1 起飞 / 4 降落 / 5 起降 / 其他普通
type?: number; // 与 attribute 二选一
// ...业务扩展字段可保留,渲染时会合并
};
const waypoints = [
{ sid: 'wp-1', lat: 31.80, lng: 117.20, attribute: 1 },
{ sid: 'wp-2', lat: 31.83, lng: 117.24, attribute: 2 },
{ sid: 'wp-3', lat: 31.86, lng: 117.28, attribute: 4 },
];| 方法 | 说明 |
| --- | --- |
| initAirworthiness(positionList?) | 写入航点并渲染 |
| setAirworthinessPositions(positions) | 替换航点并渲染 |
| setFocusWaypointIndex(index) | 高亮焦点航点;传入业务序号(从 1 起),内部转为 index - 1 |
| setAirworthinessPointPosition(position) | 按 sid 更新单点 |
| setAirworthinessActivePointSid(sid) | 设置当前激活点 |
| setAirworthinessWaypointInfo(info) | 按 sid 合并航点信息 |
| delAirworthinessPosition(sid) | 删除航点 |
| drawAirworthiness() | 强制重绘 |
| previewAirworthiness() | 预览(当前实现为重绘) |
| clearAirworthiness() | 清空航线图层 |
| destroyAirworthiness() | 同清空 |
| setDiffHeight(height) | 占位 API(与 OL 包对齐,Leaflet 侧无效果) |
3. 飞机实时位置与尾迹
// 可选:历史轨迹一次性补齐,避免飞机从起点“飞”一遍
mapService.seedAircraftTrail([
{ lat: 31.80, lng: 117.20 },
{ lat: 31.81, lng: 117.21 },
{ lat: 31.82, lng: 117.22 },
]);
// 实时点(持续推送)
mapService.sendRouteData({
lat: 31.825,
lng: 117.225,
heading: 45, // 航向角(度),可选;缺失时按位移推算
// 其它字段可用于弹窗展示
});
// 跟随飞机(type === 'gs' 时开启跟随)
mapService.setCameraOptions({ type: 'gs' });
// 取消跟随
mapService.setCameraOptions({ type: '' });
mapService.destroyRouteManager(); // 清除飞机与尾迹| 方法 | 说明 |
| --- | --- |
| initRouteManager(force?) | 初始化尾迹图层;force 为 true 时先销毁再重建 |
| seedAircraftTrail(points?) | 批量写入历史轨迹,飞机落在最后一个点 |
| sendRouteData(data) | 推送实时点;短间隔时做插值动画,长间隔(>2s)直接跳变 |
| setCameraOptions({ type }) | type === 'gs' 时跟随飞机 |
| destroyRouteManager() | 移除飞机图标与尾迹 |
尾迹最多保留约 8000 个点(内部降采样)。
4. 备降点
mapService.sendAlternatePointData([
{
id: 'alt-1', // 或 airportId
lat: 31.90,
lng: 117.10,
name: '备降场 A',
linked: true, // 关联态影响图标样式
},
]);
mapService.setAlternatePointLinked('alt-1', false);
mapService.updateAlternatePoint({ id: 'alt-1', lat: 31.91, lng: 117.11, name: '备降场 A' });
mapService.deleteAlternatePoint('alt-1');
mapService.clearAllAlternatePoints();| 方法 | 说明 |
| --- | --- |
| sendAlternatePointData(data) | 全量替换备降点(先清空再写入) |
| updateAlternatePoint(data) | 新增或更新单个 |
| setAlternatePointLinked(id, linked) | 切换关联样式 |
| deleteAlternatePoint(id) | 删除 |
| clearAllAlternatePoints() | 清空 |
| selectAlternatePoint / setAllowHighlightOnClick / setAlternatePointEditMode / setPointDraw / setAlternateName | 占位 API,与 OL 对齐 |
5. 围栏
围栏分两类:
- 预览围栏(列表展示):
sendFenceData/deleteFence/setFenceLinked - 编辑围栏(当前正在画的一个):
drawFence/setPositions/setFenceName/clearPolygon
// 预览多个围栏
mapService.sendFenceData(
[
{
fenceId: 'f-1', // 或 id
fenceName: '围栏甲', // 或 name;showName=false 时不显示名称
linked: true,
positions: [ // 或 polygonVertices,至少 3 点
{ lat: 31.80, lng: 117.20 },
{ lat: 31.80, lng: 117.25 },
{ lat: 31.85, lng: 117.25 },
{ lat: 31.85, lng: 117.20 },
],
},
],
true, // showName
);
mapService.setFenceLinked('f-1', false);
mapService.deleteFence('f-1');
// 编辑态:画一个多边形
await mapService.drawFence({
name: '新建围栏',
positions: [/* ... */],
});
mapService.setFenceName('重命名');
mapService.setPositions([/* 新顶点 */]);
mapService.clearPolygon(); // 清编辑围栏| 方法 | 说明 |
| --- | --- |
| sendFenceData(data, showName?) | 全量同步预览围栏;未出现在列表中的旧 id 会被移除 |
| deleteFence(id) | 删除预览围栏 |
| setFenceLinked(id, linked) | 关联/未关联颜色(绿 / 灰) |
| drawFence(data) | 进入编辑多边形 |
| setPositions(positions) | 更新编辑多边形顶点 |
| setFenceName(name) | 更新编辑围栏名称标注 |
| clearPolygon() | 清除编辑围栏 |
| clearFencePositions() | 清空编辑顶点(保留编辑态结构) |
| setActivePointSid / setSectorHeight / changeFenceMode / setPointPosition / delFencePosition / setAllowPopup | 占位 API |
6. 测距
mapService.measureWork();
// 左键加点,右键结束当前路径;可画多条
// ...
mapService.clearMeasureWork();进入测距后:
- 光标变为十字
- 双击缩放会被临时禁用
- 需再调
clearMeasureWork()退出并清除线条
7. 事件监听
mapService.setEventListener((e, eventName) => {
console.log(eventName, e);
});
// 取消
mapService.setEventListener();业务也可主动派发:
mapService.setOnEvent(payload, 'custom-event');具体事件名依赖业务层约定;备降点等场景常在 composable 里注册监听。
8. 常量
import {
MAP_MIN_ZOOM, // 10
MAP_MAX_ZOOM, // 24
MAP_NATIVE_ZOOM, // 18
} from '@zym-test-zerog/map-test-leaflet';完整业务示例
模拟 MFD 主页面:就绪 → 底图 → 航线 / 围栏 / 备降点 → 实时飞机。
<script setup lang="ts">
import { onUnmounted, ref } from 'vue';
import { ZMap, mapService, type MapScaleResult } from '@zym-test-zerog/map-test-leaflet';
const scale = ref<MapScaleResult | null>(null);
let timer: number | undefined;
function onMapReady() {
mapService.setMapStyle('img');
mapService.setScaleListener((s) => {
scale.value = s;
});
const airline = [
{ sid: '1', lat: 31.80, lng: 117.20, attribute: 1 },
{ sid: '2', lat: 31.84, lng: 117.26, attribute: 2 },
{ sid: '3', lat: 31.88, lng: 117.32, attribute: 4 },
];
mapService.initAirworthiness(airline);
mapService.sendFenceData([
{
fenceId: 'fence-1',
fenceName: '作业区',
linked: true,
positions: [
{ lat: 31.81, lng: 117.22 },
{ lat: 31.81, lng: 117.28 },
{ lat: 31.86, lng: 117.28 },
{ lat: 31.86, lng: 117.22 },
],
},
]);
mapService.sendAlternatePointData([
{ id: 'alt-1', lat: 31.90, lng: 117.15, name: '备降 A', linked: true },
]);
mapService.focusOnPoints(airline, 5, 0, 800, { x: 100, y: -200 });
// 模拟实时飞机
mapService.initRouteManager();
let t = 0;
timer = window.setInterval(() => {
t += 0.01;
mapService.sendRouteData({
lat: 31.80 + t * 0.08,
lng: 117.20 + t * 0.12,
heading: 50,
});
}, 500);
}
function zoomIn() {
mapService.setZoom('in');
}
function zoomOut() {
mapService.setZoom('out');
}
function toggleMeasure(on: boolean) {
if (on) mapService.measureWork();
else mapService.clearMeasureWork();
}
onUnmounted(() => {
if (timer) clearInterval(timer);
});
</script>
<template>
<div class="wrap">
<div class="toolbar">
<button @click="zoomIn">放大</button>
<button @click="zoomOut">缩小</button>
<button @click="toggleMeasure(true)">测距</button>
<button @click="toggleMeasure(false)">清除测距</button>
<span v-if="scale">{{ scale.text }}</span>
</div>
<ZMap @map-ready="onMapReady" />
</div>
</template>
<style scoped>
.wrap {
position: relative;
width: 100%;
height: 100vh;
}
.toolbar {
position: absolute;
z-index: 20;
top: 12px;
left: 12px;
display: flex;
gap: 8px;
}
</style>切换 OpenLayers
两个包 API 相同:
npm uninstall @zym-test-zerog/map-test-leaflet
npm i @zym-test-zerog/map-test-ol- import { ZMap, mapService } from '@zym-test-zerog/map-test-leaflet';
+ import { ZMap, mapService } from '@zym-test-zerog/map-test-ol';若使用了 Vite alias @zerog/mfd-map,只需改 alias 指向,业务 import 不用动。
导出清单
import {
// 组件
ZMap,
// 默认导出同 ZMap
default as MapComponent,
// 服务
mapService,
MapService,
// 组件 props / emits 定义(供二次封装)
mapProps,
mapEmits,
// 常量
MAP_MIN_ZOOM,
MAP_MAX_ZOOM,
MAP_NATIVE_ZOOM,
// 兼容旧名(已 deprecated)
leafletMapProps,
leafletMapEmits,
} from '@zym-test-zerog/map-test-leaflet';
import type {
MapProps,
MapEmits,
MapScaleResult,
LeafletMapProps,
LeafletMapEmits,
} from '@zym-test-zerog/map-test-leaflet';注意事项与常见问题
必须等
map-ready
否则mapService.viewer为空,调用会被直接 return。源码包
未打包成 JS;消费端需支持 Vue SFC + TypeScript +less(组件内使用了 less)。底图域名
默认瓦片:http://{s}.mapworld.zerogair.com/anhui/hefei/...
外网或无权限环境底图会空白,需改mapService.ts中的TILE_BASE。sendAlternatePointData/sendFenceData是全量同步
每次调用会按新列表重建;增量更新请用updateAlternatePoint/setFenceLinked等。setFocusWaypointIndex传业务序号
传入1表示第一个点;内部会减 1。占位 API
部分方法仅为与 OpenLayers 包对齐而存在,Leaflet 实现为空操作(如setDiffHeight、changeFenceMode、setAllowPopup等)。调用不会报错,但无视觉效果。单例注意
同一应用内同时挂多个ZMap会争用同一个mapService,不建议并行多实例。
License
ISC
