给定geoJSON对象,在d3中居中地图

cli*_*oid 133 geojson d3.js topojson

目前在d3中,如果您要绘制一个geoJSON对象,则必须对其进行缩放并对其进行翻译,以使其达到所需大小并将其翻译以使其居中.这是一个非常乏味的反复试验,我想知道是否有人知道更好的方法来获得这些价值观?

所以,例如,如果我有这个代码

var path, vis, xy;
xy = d3.geo.mercator().scale(8500).translate([0, -1200]);

path = d3.geo.path().projection(xy);

vis = d3.select("#vis").append("svg:svg").attr("width", 960).attr("height", 600);

d3.json("../../data/ireland2.geojson", function(json) {
  return vis.append("svg:g")
    .attr("class", "tracts")
    .selectAll("path")
    .data(json.features).enter()
    .append("svg:path")
    .attr("d", path)
    .attr("fill", "#85C3C0")
    .attr("stroke", "#222");
});
Run Code Online (Sandbox Code Playgroud)

到底如何获得.scale(8500)和.translate([0,-1200]),而一点一点去?

mbo*_*ock 165

我的答案接近Jan van der Laan,但你可以稍微简化一下,因为你不需要计算地理质心; 你只需要边界框.并且,通过使用未缩放的,未翻译的单位投影,您可以简化数学运算.

代码的重要部分是:

// Create a unit projection.
var projection = d3.geo.albers()
    .scale(1)
    .translate([0, 0]);

// Create a path generator.
var path = d3.geo.path()
    .projection(projection);

// Compute the bounds of a feature of interest, then derive scale & translate.
var b = path.bounds(state),
    s = .95 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height),
    t = [(width - s * (b[1][0] + b[0][0])) / 2, (height - s * (b[1][1] + b[0][1])) / 2];

// Update the projection to use computed scale & translate.
projection
    .scale(s)
    .translate(t);
Run Code Online (Sandbox Code Playgroud)

在单位投影对要素的边界框进行复合后,可以通过比较边界框(和)的纵横比与画布(和)的纵横比来计算适当的比例.在这种情况下,我还将边界框缩放到95%的画布,而不是100%,因此在笔迹和周围特征或填充的边缘上有一点额外的空间.b[1][0] - b[0][0]b[1][1] - b[0][1]widthheight

然后你就可以计算转换使用边界框(中心(b[1][0] + b[0][0]) / 2(b[1][1] + b[0][1]) / 2)和画布(中央width / 2height / 2).请注意,由于边界框位于单位投影的坐标中,因此必须乘以scale(s).

例如,bl.ocks.org/4707858:

项目到边界框

有一个相关的问题,其中它是如何放大到一个特定的功能集合在无需调整投影,,投影用几何变换组合以放大和缩小.它使用与上述相同的原理,但数学略有不同,因为几何变换(SVG"变换"属性)与地理投影相结合.

例如,bl.ocks.org/4699541:

缩放到边界框

  • 所以:`bs = b [0] [1]; bn = b [1] [1]; bw = b [0] [0]; be = b [1] [0]; b.height = Math.abs(bn - bs); b.width = Math.abs(be - bw); s = .9/Math.max(b.width/width,(b.height/height));` (4认同)
  • 我想指出上面的代码中有一些错误,特别是在边界的索引中.它应该看起来像:s =(0.95/Math.max((b [1] [0] - b [0] [0])/ width,(b [1] [1] - b [0] [0] )/ height))*500,t = [(width - s*(b [1] [0] + b [0] [0]))/ 2,(height - s*(b [1] [1] + b [0] [1]))/ 2]; (2认同)
  • @iros - 看起来`*500`在这里是无关的......还有,`b [1] [1] - b [0] [0]`应该是`b [1] [1] - b [0] [1]`在比例计算中. (2认同)
  • 错误报告:http://meta.stackexchange.com/questions/184140/markdown-editor-messing-with-arrays-of-arrays (2认同)
  • 正是因为这样的社区,D3才能与之共创欢乐.真棒! (2认同)

Jan*_*aan 126

以下似乎大概做了你想要的.缩放似乎没问题.将它应用到我的地图时会有一个小的偏移量.这个小偏移可能是因为我使用translate命令使地图居中,而我应该使用center命令.

  1. 创建投影和d3.geo.path
  2. 计算当前投影的边界
  3. 使用这些边界来计算比例和平移
  4. 重新创建投影

在代码中:

  var width  = 300;
  var height = 400;

  var vis = d3.select("#vis").append("svg")
      .attr("width", width).attr("height", height)

  d3.json("nld.json", function(json) {
      // create a first guess for the projection
      var center = d3.geo.centroid(json)
      var scale  = 150;
      var offset = [width/2, height/2];
      var projection = d3.geo.mercator().scale(scale).center(center)
          .translate(offset);

      // create the path
      var path = d3.geo.path().projection(projection);

      // using the path determine the bounds of the current map and use 
      // these to determine better values for the scale and translation
      var bounds  = path.bounds(json);
      var hscale  = scale*width  / (bounds[1][0] - bounds[0][0]);
      var vscale  = scale*height / (bounds[1][1] - bounds[0][1]);
      var scale   = (hscale < vscale) ? hscale : vscale;
      var offset  = [width - (bounds[0][0] + bounds[1][0])/2,
                        height - (bounds[0][1] + bounds[1][1])/2];

      // new projection
      projection = d3.geo.mercator().center(center)
        .scale(scale).translate(offset);
      path = path.projection(projection);

      // add a rectangle to see the bound of the svg
      vis.append("rect").attr('width', width).attr('height', height)
        .style('stroke', 'black').style('fill', 'none');

      vis.selectAll("path").data(json.features).enter().append("path")
        .attr("d", path)
        .style("fill", "red")
        .style("stroke-width", "1")
        .style("stroke", "black")
    });
Run Code Online (Sandbox Code Playgroud)

  • 嘿Jan van der Laan我从未感谢你的回应.这是一个非常好的回应,如果我可以分出我想的赏金.谢谢! (7认同)

Pau*_*ine 51

我是d3的新手 - 将尝试解释我是如何理解的,但我不确定我是否做得对.

秘诀是知道某些方法将在制图空间(纬度,经度)和笛卡尔空间(屏幕上的x,y)上进行操作.制图空间(我们的星球)是(几乎)球形,笛卡尔空间(屏幕)是平坦的 - 为了将一个映射到另一个,你需要一个算法,称为投影.这个空间太短,无法深入到投影的迷人主题中,以及它们如何扭曲地理特征以便将球形转变为平面; 一些设计用于保存角度,另一些设计用于保存距离等等 - 总是存在折衷(Mike Bostock有大量示例).

在此输入图像描述

在d3中,投影对象具有中心属性/ setter,以地图单位给出:

projection.center([位置])

如果指定了center,则将投影的中心设置为指定位置,以度为单位的经度和纬度的双元素数组并返回投影.如果未指定center,则返回当前中心,默认为⟨0°,0°⟩.

还有以像素为单位的平移 - 投影中心相对于画布的位置:

projection.translate([点])

如果指定了point,则将投影的平移偏移设置为指定的双元素数组[x,y]并返回投影.如果未指定point,则返回当前平移偏移,默认为[480,250].平移偏移确定投影中心的像素坐标.默认平移偏移在960×500区域的中心放置⟨0°,0°..

当我想在画布中居中显示一个特征时,我喜欢将投影中心设置为特征边界框的中心 - 当我的国家(巴西)使用mercator(WGS 84,用于谷歌地图)时,这适用于我,从未使用其他投影和半球进行测试.您可能需要对其他情况进行调整,但如果您确定了这些基本原则,那么您将没事.

例如,给定投影和路径:

var projection = d3.geo.mercator()
    .scale(1);

var path = d3.geo.path()
    .projection(projection);
Run Code Online (Sandbox Code Playgroud)

bounds方法以像素为单位path返回边界框.使用它来查找正确的比例,将像素的大小与地图单位的大小进行比较(0.95给出的宽度或高度最佳拟合度为5%).这里的基本几何,计算对角线对角的矩形宽度/高度:

var b = path.bounds(feature),
    s = 0.9 / Math.max(
                   (b[1][0] - b[0][0]) / width, 
                   (b[1][1] - b[0][1]) / height
               );
projection.scale(s); 
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

使用此d3.geo.bounds方法以地图单位查找边界框:

b = d3.geo.bounds(feature);
Run Code Online (Sandbox Code Playgroud)

将投影的中心设置为边界框的中心:

projection.center([(b[1][0]+b[0][0])/2, (b[1][1]+b[0][1])/2]);
Run Code Online (Sandbox Code Playgroud)

使用此translate方法将地图的中心移动到画布的中心:

projection.translate([width/2, height/2]);
Run Code Online (Sandbox Code Playgroud)

到目前为止,你应该让地图中心的特征放大5%边距.


dnl*_*tsk 44

随着d3 v4,它越来越容易!

var projection = d3.geoMercator().fitSize([width, height], geojson);
var path = d3.geoPath().projection(projection);
Run Code Online (Sandbox Code Playgroud)

最后

g.selectAll('path')
  .data(geojson.features)
  .enter()
  .append('path')
  .attr('d', path)
  .style("fill", "red")
  .style("stroke-width", "1")
  .style("stroke", "black");
Run Code Online (Sandbox Code Playgroud)

享受,干杯

  • 我希望这个答案得到更多的投票。使用 `d3v4` 一段时间后才发现这种方法。 (2认同)
  • `g` 是从哪里来的?那是 svg 容器吗? (2认同)
  • 遗憾的是,在 2 个高质量答案之后,这已经如此落后了。很容易错过这一点,而且它显然比其他答案简单得多。 (2认同)