预投影几何体v让浏览器做到这一点(又名效率v灵活性)

Mar*_*kus 4 data-visualization geo geojson d3.js topojson

为了提高我的在线地图的性能,特别是在智能手机上,我遵循Mike Bostock的建议,在将地理数据上传到服务器之前尽可能地准备地理数据(根据他的命令行制图).例如,我正在通过d3.geoConicEqualArea()命令行投射TopoJSON数据,而不是让观看者的浏览器在加载地图时执行这项繁重的工作.

不过,我也想用类似的方法.scale,.fitSize,.fitExtent.translate动态,这意味着我不能"烤"的规模或价值事先翻译成TopoJSON文件.

Bostock建议使用d3.geoTransform()作为预测的代理,例如,d3.geoConicEqualArea()如果您正在使用已经预测的数据但仍希望扩展或翻译它.例如,要在y轴上翻转投影,他建议:

var reflectY = d3.geoTransform({
      point: function(x, y) {
        this.stream.point(x, -y);
      }
    }),

    path = d3.geoPath()
        .projection(reflectY);
Run Code Online (Sandbox Code Playgroud)

我的问题:如果我使用这个D3功能,我是不是仍然强迫观众的浏览器进行大量的数据处理,这会使性能恶化?预处理数据的目的是避免这种情况.或者我是否过高估计上述d3.geoTransform()功能涉及的处理工作?

And*_*eid 8

如果我使用这个D3功能,我是不是仍然强迫观众的浏览器进行大量的数据处理,这会使性能恶化?预处理数据的目的是避免这种情况.或者我是否过高估计上面d3.geoTransform()函数中涉及的处理工作?

简答:您高估了转换预测数据所需的工作量.


D3 geoProjections的球形性质

d3 geoProjection相对独特.许多平台,工具或库采用由纬度和经度对组成的点,并将它们视为在笛卡尔平面上.这在很大程度上简化了数学运算,但需要付出代价:路径遵循笛卡尔路由.

D3将经度纬度点视为它们的位置:三维椭球上的点.这在计算上花费更多,但提供了其他好处 - 例如沿着大圆路线路由路径段.

在将坐标视为3d地球上的点时,额外的计算成本d3是:

  1. 球形数学

缩放,居中等之前,先看一下简单的地理投影:

function mercator(x, y) {
  return [x, Math.log(Math.tan(Math.PI / 4 + y / 2))];
}
Run Code Online (Sandbox Code Playgroud)

这可能比您上面提出的转换花费的时间更长.

  1. 寻路

在笛卡尔平面上,两个点之间的线很容易,在球体上,这很困难.沿着从东经179度到西经179度的路线 - 将这些视线放在一个很容易的笛卡尔平面上 - 在地球上划一条线.在球形地球上,线穿过反子午线.

因此,在平整路径时,沿路线需要采样,点之间的大圆距离需要弯曲,因此需要额外的点.我不确定d3中的这个过程,但肯定会发生.

笛卡尔平面上的点不需要额外采样 - 它们已经是平坦的,点之间的线是直的.没有必要检测线条是否以另一种方式缠绕在地球上.

投影后的操作

一旦投影,像.fitSize这样的东西将强制进行额外的工作,这与你使用d3.geoTransform()提出的要点基本相同:需要根据投影的位置和大小对特征进行变换和缩放.

这在自动进入特征的d3v3(之前fitSize())中非常明显:计算涉及投影特征的svg范围.


基本准科学绩效比较

使用美国的美国人口普查局shapefile,我创建了三个geojson文件:

  • 一个使用WGS84(长/纬)(文件大小:389 kb)
  • 一个在节点中使用带有普通d3.geoAlbers变换的geoproject(文件大小:386 kb)
  • 一个在节点中使用geoproject d3.geoAlbers().fitSize([500,500],d)(文件大小为385 kb)

速度的黄金标准应该是选项3,数据根据预期的显示范围进行缩放和居中,这里不需要变换,我将使用空投影来测试它

我继续使用以下方法将这些投影到500x500 svg:

//  For the unprojected data
var projection = d3.geoAlbers()
 .fitSize([500,500],wgs84);

var geoPath = d3.geoPath().projection(projection)


// for the projected but unscaled and uncentered data  
var transform = d3.geoIdentity()
   .fitSize([500,500],albers);

  var projectedPath = d3.geoPath()
    .projection(transform);

// for the projected, centered, and scaled data
var nullProjection = d3.geoPath()
Run Code Online (Sandbox Code Playgroud)

运行这几百次,我获得了平均渲染时间(数据被预加载):

  • 71毫秒:WGS84
  • 33毫秒:预计但未缩放和未中止
  • 21毫秒:投射,缩放和居中

我觉得很安全地说预先投影数据会有很大的性能提升,无论它是否实际上是居中和缩放的.

注意我使用的d3.geoIdentity()相反,d3.geoTransform()因为它允许使用fitSize(),你可以在y上反映如果需要:.reflectY(true);