如何使用少于 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点!