目标:在私有化海图上,用 Canvas 复现在线平台船位展示服务( ShipxyAPI.ShipService/ CanvasShipService)与历史轨迹服务(ShipxyAPI.TrackService/ TrackServiceImpl)的业务绘制效果:海量船舶渲染、船型符号、选中交互、红色虚线轨迹、停/慢状态标记、起终点标记、轨迹回放播放器。数据全部来自客户内网自建接口。
#FFFF66,按航向定向的三角符号,无船名标注;group.colorhex,示例取绿色 #00CC00),整队同色、置于区域船之上(置顶),并带橙色标注框 #ED5835,框内白字 S + 船名;#ffd700 + 船名/航速标签(setSelectedShip 效果);dShipColor: #FF6437(deleteShipByShipID 过程色)。resetShipColorCallBack(ship) 钩子:绘制前按船逐个回调返回颜色,自定义配色(例如把客户自有船队刷成企业 VI 色)走这里,而不是按速度改色。tomato 红色 2px 虚线(dash:true, dashArray:[5,5]),节点带时间标注,停泊点标红色「停」、低速点标绿色「慢」,起点绿圈 #06c84a、终点红圈 #f70006;回放开启时底部弹出深色播放器面板(显示字段复选框:船名/MMSI/船首向/航迹向/速度 + 时间轴)。{ lineColor:'tomato', lineWeight:2, dash:true, dashArray:[5,5], circleOverColor:'yellow', isDilute:true, isEnablePlayer:true }。new CanvasShip() 后逐个赋值):addShips / addFleetShips 的必填字段为 shipid / mmsi / lat / lng,其余字段用于符号、tooltip、过滤。getAreaShipsCallBack 的定位就是"区域船数据由外部数据源提供",私有化场景正是它的目标形态;我们自实现时把"外部数据源"落到本地接口即可。| 官方能力 | 私有化实现 |
|---|---|
| Canvas 海量绘制(数万级流畅) | 单个全屏 Canvas + requestAnimationFrame 重绘 |
enableAreaShip 区域船自动刷新 | 地图 moveend + delayTime 定时器拉取可视范围船位 |
delayTime: 5000 刷新间隔 | 同名配置 |
shipOptions 船舶符号设置 | 符号大小随缩放级别调整 |
tooltip_fields 悬浮字段 | 鼠标命中检测后渲染 HTML tooltip |
addSelectedListener 选中监听 | Canvas 命中检测 + 选中高亮 |
setFilter 条件过滤 | filterFn 谓词 |
locationShip 单船定位 | locate(mmsi) 居中 + 高亮 |
drawShipsEndCallBack 绘制完成回调 | onDrawEnd({count}) |
ShipCanvasLayer.jslat/lon 为 ×10⁶ 定点数、hdg/cog 为 ×100 定点、width/length 为 ×10(分米);自建接口建议直接返回浮点数(如上例),避免前端到处除系数。hdg,缺失时回落 cog(在线平台同款回落逻辑)。hdg=511(不可用值)时也回落。0 在航(动力)、1 锚泊、5 系泊、8 捕鱼等;用于命中悬浮信息与过滤统计。注意颜色不随状态/速度变化——在线效果中所有区域船一律默认黄,仅船队船按分组着色、选中船加金色高亮圈。ShipxyAPI.TrackService(map) 后,map.trackService.addAndShow(trackId, tracks, options, ship) 绘制自有轨迹数据;tracks 为 Track 对象数组,必需字段 utc / sog / lng / lat:addAndShowByUrl(mmsi, btime, etime, options) 是"自动拉数"版本(btime/etime 为秒级时间戳)——私有化把"自动拉数"指向内网接口即可,其余绘制逻辑相同。TrackLayer.js(红色虚线 + 节点时间标注 + 停/慢标记 + 回放)
### 4.3 使用示例(含官方同款深色回放播放器 UI)
```html
<div id="map" class="my-map"></div>
<!-- 回放播放器(官方轨迹回放播放器等价 UI) -->
<div id="player" style="position:absolute;left:0;bottom:0;width:100%;z-index:999;
background:#1d374c;color:#e6eef5;padding:8px 14px 10px;display:none;
box-shadow:0 -2px 8px rgba(0,0,0,.35);font:12px/1.7 'Microsoft YaHei',sans-serif">
<div>
<b>显示字段:</b>
<label><input type="checkbox" checked onchange="trackUI.f.name=this.checked"/>船名</label>
<label><input type="checkbox" checked onchange="trackUI.f.mmsi=this.checked"/>MMSI</label>
<label><input type="checkbox" checked onchange="trackUI.f.hdg=this.checked"/>船首向</label>
<label><input type="checkbox" checked onchange="trackUI.f.cog=this.checked"/>航迹向</label>
<label><input type="checkbox" checked onchange="trackUI.f.sog=this.checked"/>速度</label>
</div>
<div style="display:flex;align-items:center;gap:10px;margin-top:6px">
<button onclick="trackUI.toggle()" style="width:30px;height:30px;border-radius:50%;
border:none;background:#ff8573;color:#fff;cursor:pointer">⏸</button>
<span id="curTime" style="font:12px Consolas,monospace;color:#fff"></span>
<input type="range" min="0" max="1000" value="0" style="flex:1" oninput="trackUI.seek(this.value)"/>
<span style="font-size:11px;color:#9fb8cd">起始 ~ 结束时间</span>
</div>
</div>
<script>
var track = new TrackLayer().addTo(_map); // options 默认即官方效果:tomato 虚线 + 节点标注 + 停/慢
track.onPlayerChange = function (st) {
document.getElementById("pinfo").textContent =
st.idx + " / " + st.total + (st.playing ? " 播放中" : " 已暂停");
};
// 从内网轨迹库拉取(对应 addAndShowByUrl 的私有化版)
fetch("http://localhost:9000/api/track?mmsi=413555666&btime=1717200000&etime=1717804800")
.then(function (r) { return r.json(); })
.then(function (tracks) { // Track 数组:utc(秒)/sog/lng/lat/cog
track.addAndShow("trk-001", tracks, { mmsi: 413555666, length: 450, width: 100 });
document.getElementById("player").style.display = "block";
track.showTrackAnalysis("analysis"); // 航速曲线(对应 showTrackAnalysis)
});
</script>isDilute: true 为抽稀显示。本地实现推荐 Douglas-Peucker:delayTime 间隔轮询),海量船舶 Canvas 绘制流畅;setFilter 生效(过滤后仅显示过滤集);locationShip 可居中定位单船;