地图中浏览器的最大svg元素数

时间:2013-09-09 15:12:56

标签: javascript performance svg d3.js leaflet

我正在创建一个带有传单和d3的地图。很多圆圈都会在地图上绘制出来。在浏览器兼容性方面,预计会限制浏览器可以呈现多少svg元素。然而,就用户体验而言,我希望用户可以在地图上看到尽可能多的元素(否则用户可能需要不断地放大和缩小,并且需要等待ajax返回数据)。我需要考虑一些优化(用户等待时间用户与服务器查询负载与浏览器可以处理的内容相比)。

参见情节,现在对服务器返回的点数有一个限制,因此只填充了一部分地图。

enter image description here

浏览器无法在此处理完全填充的地图,用户也需要等待太长时间才能获得服务器响应。

我认为我面临的问题需要通过回答两个问题来解决:

  • 根据地图上简单svg形状(圆圈)的数量,平均浏览器可以处理的标准是什么?
  • 在地图上显示尽可能多的形状的最佳技巧是什么?

我正在考虑以下几点,但我不确定它是否会有所帮助;

  • 使用正方形代替圆圈
  • 使用传单API而不是D3

4 个答案:

答案 0 :(得分:5)

一般而言,您正在考虑的两点都没有帮助。在这两种情况下,浏览器要显示的处理量/信息将大致相同。

关于你的第一个问题,不是我所知道的。浏览器和平台之间存在巨大差异(特别是如果您考虑移动设备),平均值几乎毫无意义。此外,这种情况在不断变化。我发现多达1000个简单的形状通常不是问题。

要在地图上显示尽可能多的形状,我会将它们预先渲染为位图图块,然后使用传单API或类似d3.geo.tile(示例here)覆盖它实际的地图。通过这种方式,您可以轻松扩展到数百万个点。

答案 1 :(得分:5)

Although you can only render ~2-5k SVG elements before you start to see noticeable slowdown (depending on size, use of gradient fills, etc.), you can store and manipulate much larger datasets client-side. You can often handle tens or hundreds of thousands of data points efficiently in SVG: the trick is to be very selective about what you actually render, and to use techniques like debouncing to redraw only when necessary.

(For very large datasets: yes, you'll need to either aggregate/subsample points or pre-render.)

With this in mind, one technique I've used for d3 maps in particular is to use d3.geom.quadtree() to dynamically cull points as the user pans/zooms. More specifically, I avoid drawing points that are either

  1. outside the current map bounding box (since these aren't visible at all), or
  2. too close to other points (since these add visual clutter and are hard to interact with anyways).

In JS-ish pseudocode, this would look roughly like:

function getIndicesToDraw(data, r, bbox) {
  var indicesToDraw = [];
  var Q = d3.geom.quadtree();
  // set bounds in pixel space
  for (var i = 0; i < data.length; i++) {
    var d = data[i];
    var p = getPointForDatum(d);
    if (isInsideBoundingBox(bbox, p) && !hasPointWithinRadius(Q, r, p)) {
      Q.add(p); 
      indicesToDraw.push(i);
    }
  }
  return indicesToDraw;
}

function redraw(svg, data, r, bbox) {
  var indicesToDraw = getIndicesToDraw(data, r, bbox);
  var points = svg.selectAll('.data-point')
    .data(indicesToDraw, function(i) { return i; });

  // draw new points for points.enter()

  points.exit().remove();

  // update positions of points (or SVG transforms, etc.)
}

答案 2 :(得分:2)

这是一个制图问题,就像技术一样。仅仅因为你可以在地图上放置数千个点并不意味着你应该。您应该问自己,您的用户是否需要同时查看尽可能多的点,并且会更好地理解数据。看到这么多点会让用户感到困惑和压力,还是会帮助他们完成他们想要的任务?有什么方法可以过滤数据,以便不需要立即绘制所有点吗?

此问题最常见的解决方案是使用群集,通常使用类似Leaflet MarkerCluster的内容。在过去使用D3和Leaflet的过程中,我创建了一种热图,通过创建任意网格,将每个点分配到一个bin,并将颜色渐变应用于网格。然而,回到原来的问题,它是计算密集型的。

根据您要支持的平台,请注意手机和平板电脑并不总能很好地处理SVG,尤其是在绘制数千个功能时。

潜在性能提升的另一个地方是点几何的交付。我不确定你当前是如何加载它们的,但使用空间索引的PostGIS表并通过边界框选择通常很快,但因为你是绘制点而不是多边形,你甚至可以将它们加载到浏览器中通过CSV,它比GeoJSON或Topojson小得多。

答案 3 :(得分:1)

我会一次加载所有数据,但只在视口中绘制足够大的圆圈。在缩放或平移时,删除不应显示的圆圈,并检查是否应添加以前隐藏的圆圈。