如何使用少于 200MB 的内存在 Openlayers 3 中显示 400,000 个或更多点?
How do I display 400,000 or more points in Openlayers 3 using less than 200MB of memory?
我创建了一个独立的地图来亲自测试一下。我使用 Chrome 开发人员工具在加载页面时拍摄了该页面的堆快照,发现它使用了 882MB 的内存。我希望绘制大约一个小时的闪电数据,我希望用户能够与之交互,以便 Openlayers 在这里有意义。然而,它占用了大量内存,需要一个内存效率更高的解决方案。
下面是我用来执行此操作的代码:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
<script src="https://cdnjs.cloudflare.com/ajax/libs/ol3/3.6.0/ol.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ol3/3.6.0/ol.css">
</head>
<body>
<div class="container">
<div class="row">
<div class="md-12">
<div id="map" class="map"></div>
</div>
</div>
<div id="span12">
</div>
</div>
<script>
var iconInfo = [{
points: 4,
radius: 3,
radius2: 0,
angle: 0
}, {
points: 4,
radius: 3,
radius2: 0,
angle: 0
}, {
points: 4,
radius: 3,
radius2: 0,
angle: 0
}, {
points: 4,
radius: 3,
radius2: 0,
angle: 0
}];
var i;
var iconCount = iconInfo.length;
var icons = new Array(iconCount);
for (i = 0; i < iconCount; ++i) {
var info = iconInfo[i];
icons[i] = new ol.style.RegularShape({
points: info.points,
radius: info.radius,
radius2: info.radius2,
angle: info.angle,
fill: new ol.style.Fill({color: 'rgba(0, 0, 0, 0.9)'}),
stroke: new ol.style.Stroke({width: 2, color: 'rgba(0, 0, 0, 0.9)'}),
});
}
var featureCount = 350000;
var features = new Array(featureCount);
var feature, geometry;
var e = 25000000;
for (i = 0; i < featureCount; ++i) {
geometry = new ol.geom.Point(
[2 * e * Math.random() - e, 2 * e * Math.random() - e]);
feature = new ol.Feature(geometry);
feature.setStyle(
new ol.style.Style({
image: icons[i % (iconCount - 1)]
})
);
features[i] = feature;
}
var vectorSource = new ol.source.Vector({
features: features
});
var vector = new ol.layer.Vector({
source: vectorSource
});
var map = new ol.Map({
layers: [vector],
target: document.getElementById('map'),
view: new ol.View({
center: [0, 0],
zoom: 5
})
});
var overlayFeatures = [];
for (i = 0; i < featureCount; i += 30) {
var clone = features[i].clone();
clone.setStyle(null);
overlayFeatures.push(clone);
}
var featureOverlay = new ol.layer.Vector({
map: map,
source: new ol.source.Vector({
features: overlayFeatures
}),
style: new ol.style.Style({
image: icons[iconCount - 1]
})
});
map.on('click', function(evt) {
var info = document.getElementById('info');
info.innerHTML =
'Hold on a second, while I catch those butterflies for you ...';
window.setTimeout(function() {
var features = [];
map.forEachFeatureAtPixel(evt.pixel, function(feature, layer) {
features.push(features);
return false;
});
if (features.length === 1) {
info.innerHTML = 'Got one butterfly';
} else if (features.length > 1) {
info.innerHTML = 'Got ' + features.length + ' butterflies';
} else {
info.innerHTML = 'Couldn\'t catch a single butterfly';
}
}, 1);
});
map.on('pointermove', function(evt) {
if (evt.dragging) {
return;
}
var pixel = map.getEventPixel(evt.originalEvent);
var hit = map.hasFeatureAtPixel(pixel);
map.getTarget().style.cursor = hit ? 'pointer' : '';
});
</script>
</body>
</html>
关于如何实现更好的内存效率有什么建议吗?
简答
OpenLayers 3 每个点特征使用大约 2 kB(见下文),因此虽然可以进行一些优化,但您必须减少特征的数量。 400 000 个特征将需要大约 800 MB 的内存。
动态加载您的功能,或使用多点几何。
将样式从几何体移动到图层。
更长的答案
风格
当我测试时,从功能中删除样式并用简单的 属性 替换它,每个功能的内存占用量减少了 290 B。见 http://jsfiddle.net/vkm2rg46/3/:
var vector = new ol.layer.Vector({
source: vectorSource,
style: function (feature, resolution) {
var i = feature.getProperties().styleId;
return [new ol.style.Style({
image: icons[i]
})];
}
});
并帮助样式函数:
feature.set('styleId', i % (iconCount - 1));
空间索引
您可以在矢量源上将 useSpatialIndex 设置为 false。源保留空间索引以快速检索给定范围内的要素,每个要素似乎需要大约 200-250 字节。但是,删除索引可能会对如此多的功能产生不良的性能影响。
减少特征数##
最好的办法可能是加载较少的功能。有几种解决方案。
按需加载
最常见的解决方法是让服务器处理数据,并在需要时动态加载数据。您可能不希望在较低的缩放级别显示 400 000 点,并且用户不会到处平移。
这可以通过矢量瓦片或使用 bbox 或瓦片的法线矢量源来完成。
也可以在客户端完成,方法是在矢量源加载器函数中从您自己的数据集创建 features/geometries。
多点
具有 10 或 100 个点的多点几何几乎不会比单个点几何占用更多 space。如果将雷击分组到 MultiPoint 几何图形中,内存可能不是问题。然而,您会失去一些语义,以及将元数据附加到每个单点的可能性。
JsFiddle: http://jsfiddle.net/vkm2rg46/8/
内存使用
我创建了 http://jsfiddle.net/g7qduy1w/3/ 来测试几何、特征和源的内存使用。您可以在不同阶段拍摄快照(值得注意的是,当向特征添加几何图形以及向源添加特征时,事件监听数据会增加)。
将一个简单的点几何添加到一个没有额外属性的特征,并添加到一个源中,每个特征的内存使用是:
288 B 几何事件侦听器
424 B 其余几何数据
752 B 特征事件侦听器
184 B 其余特征数据
261 B 来源(使用 100 000 个特征的总内存份额)
看看来自 camptocamp 的这个例子
https://www.camptocamp.com/en/actualite/drawing-large-amounts-of-points-with-openlayers-3-and-webgl
带有 WebGL 的 OpenLayers 符号:
http://openlayers.org/en/master/examples/symbol-atlas-webgl.html
非常高效地显示100k点!
我创建了一个独立的地图来亲自测试一下。我使用 Chrome 开发人员工具在加载页面时拍摄了该页面的堆快照,发现它使用了 882MB 的内存。我希望绘制大约一个小时的闪电数据,我希望用户能够与之交互,以便 Openlayers 在这里有意义。然而,它占用了大量内存,需要一个内存效率更高的解决方案。
下面是我用来执行此操作的代码:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
<script src="https://cdnjs.cloudflare.com/ajax/libs/ol3/3.6.0/ol.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ol3/3.6.0/ol.css">
</head>
<body>
<div class="container">
<div class="row">
<div class="md-12">
<div id="map" class="map"></div>
</div>
</div>
<div id="span12">
</div>
</div>
<script>
var iconInfo = [{
points: 4,
radius: 3,
radius2: 0,
angle: 0
}, {
points: 4,
radius: 3,
radius2: 0,
angle: 0
}, {
points: 4,
radius: 3,
radius2: 0,
angle: 0
}, {
points: 4,
radius: 3,
radius2: 0,
angle: 0
}];
var i;
var iconCount = iconInfo.length;
var icons = new Array(iconCount);
for (i = 0; i < iconCount; ++i) {
var info = iconInfo[i];
icons[i] = new ol.style.RegularShape({
points: info.points,
radius: info.radius,
radius2: info.radius2,
angle: info.angle,
fill: new ol.style.Fill({color: 'rgba(0, 0, 0, 0.9)'}),
stroke: new ol.style.Stroke({width: 2, color: 'rgba(0, 0, 0, 0.9)'}),
});
}
var featureCount = 350000;
var features = new Array(featureCount);
var feature, geometry;
var e = 25000000;
for (i = 0; i < featureCount; ++i) {
geometry = new ol.geom.Point(
[2 * e * Math.random() - e, 2 * e * Math.random() - e]);
feature = new ol.Feature(geometry);
feature.setStyle(
new ol.style.Style({
image: icons[i % (iconCount - 1)]
})
);
features[i] = feature;
}
var vectorSource = new ol.source.Vector({
features: features
});
var vector = new ol.layer.Vector({
source: vectorSource
});
var map = new ol.Map({
layers: [vector],
target: document.getElementById('map'),
view: new ol.View({
center: [0, 0],
zoom: 5
})
});
var overlayFeatures = [];
for (i = 0; i < featureCount; i += 30) {
var clone = features[i].clone();
clone.setStyle(null);
overlayFeatures.push(clone);
}
var featureOverlay = new ol.layer.Vector({
map: map,
source: new ol.source.Vector({
features: overlayFeatures
}),
style: new ol.style.Style({
image: icons[iconCount - 1]
})
});
map.on('click', function(evt) {
var info = document.getElementById('info');
info.innerHTML =
'Hold on a second, while I catch those butterflies for you ...';
window.setTimeout(function() {
var features = [];
map.forEachFeatureAtPixel(evt.pixel, function(feature, layer) {
features.push(features);
return false;
});
if (features.length === 1) {
info.innerHTML = 'Got one butterfly';
} else if (features.length > 1) {
info.innerHTML = 'Got ' + features.length + ' butterflies';
} else {
info.innerHTML = 'Couldn\'t catch a single butterfly';
}
}, 1);
});
map.on('pointermove', function(evt) {
if (evt.dragging) {
return;
}
var pixel = map.getEventPixel(evt.originalEvent);
var hit = map.hasFeatureAtPixel(pixel);
map.getTarget().style.cursor = hit ? 'pointer' : '';
});
</script>
</body>
</html>
关于如何实现更好的内存效率有什么建议吗?
简答
OpenLayers 3 每个点特征使用大约 2 kB(见下文),因此虽然可以进行一些优化,但您必须减少特征的数量。 400 000 个特征将需要大约 800 MB 的内存。
动态加载您的功能,或使用多点几何。
将样式从几何体移动到图层。
更长的答案
风格
当我测试时,从功能中删除样式并用简单的 属性 替换它,每个功能的内存占用量减少了 290 B。见 http://jsfiddle.net/vkm2rg46/3/:
var vector = new ol.layer.Vector({
source: vectorSource,
style: function (feature, resolution) {
var i = feature.getProperties().styleId;
return [new ol.style.Style({
image: icons[i]
})];
}
});
并帮助样式函数:
feature.set('styleId', i % (iconCount - 1));
空间索引
您可以在矢量源上将 useSpatialIndex 设置为 false。源保留空间索引以快速检索给定范围内的要素,每个要素似乎需要大约 200-250 字节。但是,删除索引可能会对如此多的功能产生不良的性能影响。
减少特征数##
最好的办法可能是加载较少的功能。有几种解决方案。
按需加载
最常见的解决方法是让服务器处理数据,并在需要时动态加载数据。您可能不希望在较低的缩放级别显示 400 000 点,并且用户不会到处平移。
这可以通过矢量瓦片或使用 bbox 或瓦片的法线矢量源来完成。
也可以在客户端完成,方法是在矢量源加载器函数中从您自己的数据集创建 features/geometries。
多点
具有 10 或 100 个点的多点几何几乎不会比单个点几何占用更多 space。如果将雷击分组到 MultiPoint 几何图形中,内存可能不是问题。然而,您会失去一些语义,以及将元数据附加到每个单点的可能性。
JsFiddle: http://jsfiddle.net/vkm2rg46/8/
内存使用
我创建了 http://jsfiddle.net/g7qduy1w/3/ 来测试几何、特征和源的内存使用。您可以在不同阶段拍摄快照(值得注意的是,当向特征添加几何图形以及向源添加特征时,事件监听数据会增加)。
将一个简单的点几何添加到一个没有额外属性的特征,并添加到一个源中,每个特征的内存使用是:
288 B 几何事件侦听器
424 B 其余几何数据
752 B 特征事件侦听器
184 B 其余特征数据
261 B 来源(使用 100 000 个特征的总内存份额)
看看来自 camptocamp 的这个例子
https://www.camptocamp.com/en/actualite/drawing-large-amounts-of-points-with-openlayers-3-and-webgl
带有 WebGL 的 OpenLayers 符号:
http://openlayers.org/en/master/examples/symbol-atlas-webgl.html
非常高效地显示100k点!