我有一系列轨迹。每个轨迹代表一辆移动的车辆,并且是一个由唯一 ID 和一组坐标组成的对象,这些坐标是车辆随时间变化的位置的等间隔样本。例如,
[
{
id: 1,
p: [[100, 200], [300, 400]] // Array<[x, y]>
},
{
id: 2,
p: [[300, 400], [500, 600], [700, 800]] // Array<[x, y]>
}
]
例如,这些轨迹可能代表这样的运动(2 辆车)。 (该图像与上面的数据无关,而只是另一个(视觉)示例)。
这里的困难部分是可能有多达 15000 个轨迹,我需要同时为所有这些移动车辆设置动画。
目前,如果我采用 15000 个点的 GeoJSON(坐标为 LonLat 对)并尝试将它们投影到地图上(使用 WebMercator 投影),整个操作大约需要 350 毫秒。鉴于我的目标是流畅的 60fps 动画,我知道理想情况下整个计算部分不会超过 16.7 毫秒。此外,绘画也是由CPU执行的(在我的例子中没有硬件加速),所以这16.7ms对于数据准备和绘画来说应该足够了。同样,目前这一切大约需要 350ms,所以我需要优化算法的某些部分。
最关键的优化是轨迹点的坐标已经计算为画布范围。也就是说,如果有坐标
[100, 200]
,则意味着该点需要在 canvas 的 x = 100 and y = 200
处渲染,而不是 Lon 100 和 Lat 200(或 WebMercator 坐标 100 and 200
)。这应该会跳过投影步骤,从而释放一些 CPU 时间。
但是,我仍然希望能够受益于 OpenLayers 为矢量图层和源提供的所有内置功能。即,造型。此外,还有其他功能,例如交互和选择。这意味着我不能盲目地获取 Canvas 的 2D 渲染上下文并在那里绘制一些
arc
。
据我了解,我仍然需要创建
Feature
。
// Create features with screen coordinates
const points = trajectory.p.map(([x, y]) => {
const geometry = new Point([x, y]);
return new Feature(geometry);
});
但是在这种情况下,坐标被视为 WebMercator 坐标(要素被投影到地图上)。
问题是,如何让要素在渲染时跳过这一部分,将自身投影到地图上,而是处理它们的坐标和相对于渲染地图的画布的绝对坐标?
一个额外问题。
将这些坐标转换为
Feature
对象也是一个相当昂贵的操作。我想知道是否有办法跳过它,但仍然受益于 OL 为其功能提供的所有样式和交互可能性?
我虽然想基于
VectorSource
创建自定义源,但这仍然涉及转换为 Features
。如果我扩展基本 Source
,则不可能将其他源类型与 VectorLayer
一起使用,因为它们仅适用于 VectorSource
。如果我使用自定义 Layer
扩展基础 CustomLayer
以与 CustomSource
一起使用并给它一个 CanvasCustomLayerRenderer
,那么我将失去所有默认矢量图层的样式可能性。
在我的情况下,我能做的最好的事情是什么,以保留 OpenLayers 开箱即用提供的所有功能,但同时通过删除我不感兴趣的步骤来提高性能?
OpenLayers 样式示例,像素坐标,无特征
<html>
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/ol.css">
<style>
html, body, .map {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/ol.js"></script>
</head>
<body>
<div id="map" class="map"></div>
<script>
const style = new ol.style.Style({
image: new ol.style.Circle({
radius: 10,
fill: new ol.style.Fill({
color: 'blue',
}),
}),
});
const base = new ol.layer.Tile({
source: new ol.source.OSM(),
});
base.on('postrender', e => {
const vectorContext = ol.render.toContext(e.context);
vectorContext.setStyle(style);
vectorContext.drawGeometry(
new ol.geom.Point([200, 100])
);
});
const map = new ol.Map({
target: 'map',
pixelRatio: 1,
layers: [base],
view: new ol.View({
center: [0, 0],
zoom: 5,
}),
});
</script>
</body>
</html>