在高德地图实现船舶AIS图层最近在做船运方面项目到数据可视化大屏的开发,使用到了不少高德地图基础组件和可视化图层,在技术
介绍
最近在做船运方面项目,其中有一部分涉及到数据可视化大屏的开发,由于项目时间紧迫,秉承着”能有现成就用现成的”原则,使用到了不少高德地图基础组件和可视化图层。其中船舶AIS图层方面的需求适合是个挑战,在这里分享一下遇到的问题和解决方案。
这是个定制化项目,主场景地图中涉及到一些业务关联性比较强的图层,比如航海地图(海图)、港区航道和锚地(停泊区域)规划,实时船舶AIS数据图层,效果如下。
1.叠加底图服务
1.1 底图选型
由于高德的卫星影像地图不够清晰,且高德地图使用的并未WGS84坐标系,不符合项目需求,因此我们使用了第三方的瓦片地图服务——国家天地图服务 进行叠加,需要注意的一点是个人开发者的流量配额非常有限,如果需要在具体项目中使用请务必申请为企业卡开发者(免费)。
1.2 底图接入
高德地图提供了TileLayer.WMTS用于接入第三方瓦片地图,代码实现简单易懂。
const layer = new AMap.TileLayer.WMTS({
url: 'https://t4.tianditu.gov.cn/img_w/wmts',
blend: false,
tileSize: 256,
params: {
Layer: 'img',
Version: '1.0.0',
Format: 'tiles',
TileMatrixSet: 'w',
STYLE: 'default',
tk: '从天地图获取的token'
},
visible: true
});
2.绘制航道线
2.1 创建数据
高德地图画贝赛尔曲线貌似有点麻烦,虽然有BevpzierCurve类,但还要结合曲线编辑器一起使用才能造数据,我直接在QGIS上完成绘制工作了,调整起来也是比较方便,完成后导出geoJSON格式即可直接使用。
2.2 代码实现
线和面的绘制我们可以使用高德自带的Polygon和Polyline组织就好,把图层中涉及到的线路、范围、标注点分别按照类型渲染即可,这里也没啥难度。 有个注意点就是在初始Polygon的时候需要把bubble设置为true,以防被它盖在下面的元素无法触发光标事件。
data.forEach(item => {
const {properties, geometry} = item;
const {name, type, center} = properties;
let mesh = null;
let text = null;
switch (type) {
case 'portArea':
// 港区范围
mesh = new AMap.Polygon({
path: geometry.coordinates,
fillColor: '#46DCC4',
strokeOpacity: 1,
fillOpacity: 0.3,
strokeColor: '#46DCC4',
strokeWeight: 2,
strokeStyle: 'dashed',
strokeDasharray: [10, 10],
map,
bubble: true, // 派发事件让其他图层可交互
zIndex: 1
});
break;
case 'mileage':
// 海里距离
const {mileage} = properties;
const coords = geometry.coordinates[0];
mesh = new AMap.Polyline({
path: coords,
strokeOpacity: 1,
strokeColor: '#e8d389',
strokeWeight: 2,
strokeStyle: 'dashed',
strokeDasharray: [6, 6],
map,
zIndex: 10
});
text = new AMap.Text({
position: coords[Math.floor(coords.length / 2)],
anchor: 'bottom-center',
text: `${mileage} 海里`,
style: {'border-color': '#FFC800', 'user-select': 'none'},
zooms
});
map.add(text);
break;
default:
break;
}
if (mesh) {
// 把polgyon缓存起来
this.geometries.push(mesh);
}
if (text) {
//
this.markers.push(text);
}
});
最终渲染出来效果如下
3.开发船舶AIS图层
3.1 业务分析
本文的重点来了(敲黑板),我们了解一下什么是AIS图层。AIS 即Automatic Identification System,是一种用于船舶之间以及船舶与岸基设施之间自动交换船舶信息的系统,它能自动向其他船舶和岸基设施发送本船的身份、位置、航向、航速等重要信息。同时也能接收其他配备 AIS 设备船舶的相关信息。
由于以上的业务特性,我们可以总结归纳出图层的功能需求如下:
-
船舶的坐标带有朝向,因此必须使用能够调整每个数据角度的图层方案
-
不同的地图缩放程度下船舶的图标不一样,必须选用能够动态调整图标或样式的图层方案
-
图层必须能够支持在大量数据(5000以上)渲染下能够流畅使用,满足这一条件的图层需要能够调用GPU算力
-
在满足以上需求的前提下,AIS图层必须支持2D和3D模式,由于图层数据带有朝向这种特异性,在3D模式下将其在三维世界的空间旋转结果展示出来,我们需要选择一个所有元素与地面平行的图层。基于以上情况,最后ScatterLayer图层成为了不二之选。
3.2 代码实现
-
获取船舶AIS数据,数据来源于船讯网,我们可以将接口返回的数据缓存到本地以方便开发。
// 获取船舶数据 async function getData(){ //1. 获取原始数据 const {data} = await axios.get(`../mock/getAreaShip.json`); //2. 整理数据格式为标准geoJSON数据 const features = data.data.map(item => { const {name, cnname, ShipID, hdg, lon, lat} = item; return { 'type': 'Feature', 'properties': { id: ShipID, name: cnname || name, angle: hdg / 1000 }, 'geometry': { 'type': 'Point', 'coordinates': [lon / 1000000, lat / 1000000] } }; }); const res = new Loca.GeoJSONSource({ data: { 'type': 'FeatureCollection', 'features': features } }); return res }
-
使用高德地图可视化图层scatterLayer,开发船舶AIS图层
// 可视化图层管理器 const loca = new Loca.Container({ map: this.map, }); // 初始化图层 async function initShipLayer (){ const scatter = new Loca.ScatterLayer({ zIndex: 10, opacity: 1, visible: true, zooms: [2, 22], }); const scatterGeo = await getData() scatter.setSource(scatterGeo); scatter.setStyle({ unit: 'px', size: [20,20], borderWidth: 0, texture: '../images/map/icon/ship.svg', rotation: function (index, v) { const {angle} = v.properties; return angle; }, }); loca.add(scatter); } /** * 根据像素坐标查找元素 * @param x * @param y * @returns {*|null} */ query({x, y}) { const {instance} = this; if (instance) { const feat = instance.queryFeature([x, y]); return feat; } else { return null; } }
-
实时监听光标在地图上的动作,通过光标的屏幕坐标查找最近的数据元素
// 创建悬浮面板 const normalMarker = new AMap.Marker({ anchor: 'bottom-center', offset: [0, 0], zIndex: 100, cursor: 'default' }); // 鼠标移动到船舶元素上方,则悬浮面板显示元素名称 map.on('mousemove', e => { const feat = layer.query(e.pixel); if (feat) { const {coordinates, properties} = feat; if (normalMarker) { normalMarker.setContent(`<div class="amap-info-tip">${properties.name || '未知'}</div>`); normalMarker.setPosition(coordinates); normalMarker.setSize([150, 36]); normalMarker.setAnchor('bottom-center'); map.add(normalMarker); } this.container.classList.add('mouse_over'); } else { this.container.classList.remove('mouse_over'); map.remove(this.normalMarker); } }); // 鼠标点击元素,则派发事件给外部处理 map.on('click', e => { const feat = layer.query(e.pixel); if (feat) { const {name, id} = feat.properties; this.dispatchEvent('poiClick', { poiType: 'ship', data: {name, ShipID: id} }); } });
-
最终实现效果如下
小技巧
数据格式
为了保证数据的可移植性,我还是将所有的GIS数据约束为geoJSON格式。这样做的好处有不少
- 多人编辑,我可以无缝迁移数据到antv的线上编辑器,这样降低了使用门槛,可以让产品经理协助我造一些相对静态的数据,比如港口码头位置,港区锚地范围等
- 二次加工,把需要深度加工的数据放到QGIS上作处理,比如对POI进行坐标系转换,将区域边界的节点数简化以提升性能,计算几何中心点等等
- 数据来源多样性,由于使用了国际标准数据格式都是一样的,我可以用各种GIS平台上搜集原始数据后追加或合并使用
关于页面性能
浏览器卡顿,有时候并不一定是代码质量引起,这次我们就遇到一个特别的情况。测试组同事一直在吐槽他的笔记本上浏览我们开发的数据可视化页面非常卡,但有些持笔记本办公室同事又不会遇到卡顿问题,这个缺陷困扰了好久。
后来通过不断排查,发现是他的浏览器使用的是集成显卡,NVIDIA没有用上,估计是一直开着节能模式,既然终于找到问题原因,问题就迎刃而解而解了。在浏览器控制面板输入以下内容可以查看当前浏览器的使用显卡信息。
(function () {
var canvas = document.createElement('canvas'),
gl = canvas.getContext('experimental-webgl'),
debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
console.log(gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL));
})();
浏览器使用显卡算力的具体步骤: 系统设置-屏幕-图形设置,桌面应用把浏览器选上,并设置为高性能。
待优化内容
相关链接
转载自:https://juejin.cn/post/7395862170560741426