为什么D3不能正确渲染TopoJSON?

时间:2017-03-22 13:37:37

标签: javascript json d3.js topojson

我尝试使用EXISTS(SELECT 1 FROM t_file WHERE f.f_revisionId = r.id AND f.filename LIKE '%src%') D3.js呈现瑞士地图。可以找到基础JSON here。首先,我尝试关注this tutorial,在我无法像地图一样远距离渲染任何内容之后,我发现this question on here带有指向工作示例的链接。我从那里开始使用这个代码,我目前正在使用:

TopoJSON

由于<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> <script src="http://d3js.org/topojson.v1.min.js"></script> <style> path { fill: #ccc; } </style> </head> <body> <h1>topojson simplified Switzerland</h1> <script> var width = window.innerWidth, height = window.innerHeight; var svg = d3.select("body") .append("svg") .attr("width", width) .attr("height", height); var projection = d3.geo.mercator() .scale(500) .translate([-900, 0]); var path = d3.geo.path() .projection(projection); d3.json("ch-cantons.json", function(error, topology) { if (error) throw error; svg.selectAll("path") .data(topojson.feature(topology, topology.objects.cantons).features).enter().append("path").attr("d", path); }); </script> </body> </html> 看起来没问题(在一些在复制/粘贴后正确呈现它的在线平台上检查)并且只有很少的代码可能出错,我认为错误在投影参数中。摆弄一点也无法使其发挥作用。所以任何帮助都将非常感谢!

1 个答案:

答案 0 :(得分:1)

你是对的,错误在投影中。 但是,错误取决于您的数据是投影还是未投影(长对)。

未投影数据

如果你有WGS84中的数据 - 也就是说lat long pair,那么你有这个问题:

使用您的投影,但只改变数据源我得到类似的东西(我在右边的空海中刮掉了):

enter image description here

要正确居中墨卡托,您需要知道您感兴趣的区域的中心坐标。这通常可以相当普遍,对瑞士我可能会尝试47N 8.25E。

一旦你有这个坐标,你需要把它放在中间。一种方法是在x轴上旋转并以y:

为中心
var projection = d3.geo.mercator()
    .scale(3500)
    .rotate([-8.25,0])
    .center([0,47])
    .translate([width/2,height/2])

请注意,x旋转为负,您正在投影下旋转地球。

另一种选择是在x和y上旋转,这可能是接近极点时的首选选项 - 因为墨卡托变形在高纬度变得不可行。这种方法看起来像:

var projection = d3.geo.mercator()
    .scale(4000)
    .rotate([-8.25,-47])
    .center([0,0])
    .translate([width/2,height/2])

再次注意负旋转值。第二种选择需要更高的比例值,因为这种方法基本上将瑞士视为零,在墨卡托投影上为零 - 沿着赤道的土地面积最小化。

使用这些预测中的第二个,我得到: enter image description here

所以你必须稍微调整一下比例,但是现在你应该能够看到你的数据了(假设你的数据是正确的lat长对)。

预计数据

根据下面的评论,其中包含一个链接的json文件,我们可以看到这是你的问题。

有两种可能的解决方案:

  1. 将数据转换为lat long pair
  2. 使用geoTransform
  3. 选项一是最简单的,你将取消数据的投影 - 这需要知道当前的投影。在GIS软件中,这通常会将其投影为WGS84,可以说它不是真正的投影而是基准。获得lat long对后,您可以按照上述步骤查看未投影的数据。

    选项二完全跳过d3.geoProjection。相反,我们将创建一个转换函数,将投影坐标转换​​为所需的SVG坐标。

    地理投影看起来像:

    function scale (scaleFactor) {
        return d3.geo.transform({
            point: function(x, y) {
                this.stream.point(x * scaleFactor, (y * scaleFactor);
            }
        });
    }
    

    用作投影:

    var path = d3.geo.path().projection(scale(0.1));
    

    它只需要一个已经是笛卡尔坐标的x,y坐标流,并以指定的方式对它们进行转换。

    要翻译地图以使其居中,您需要知道中心坐标。您可以使用path.bounds

    找到此信息
    var bounds = path.bounds(features);
    var centerX = (bounds[0][0] + bounds[1][0])/2;
    var centerY = (bounds[0][1] + bounds[1][1])/2;
    

    Path.bounds返回要素的左上角和右下角。这是关键部分,你可以做一个自动缩放功能,那里有很多例子,但我喜欢经常手动缩放。如果地图居中,这很容易。对于您的地图,您的geoTransform可能如下所示:

    function scale (scaleFactor,cx,cy,width,height) {
        return d3.geo.transform({
            point: function(x, y) {
                this.stream.point((x-cx) * scaleFactor + width/2, (y-cy)  * scaleFactor +height/2);
            }
        });
    }
    

    此处cxcy指的是要素的中间,宽度和高度指的是svg元素的宽度和高度 - 我们不希望在SVG点聚集的要素[0 ,0]。

    总而言之,这给了我们类似的东西(比例因子为0.002):

    enter image description here

    这是一个更新的JSbin:http://jsbin.com/wolamuzeze/edit?html,output

    请记住,比例取决于窗口大小,因为您的宽度/高度与窗口大小相关。这可能最好通过自动设置缩放级别来解决,但如果您有标签(例如),这可能会产生问题。

    这个答案也许有帮助:Scaling d3 v4 map to fit SVG (or at all)