D3.js 绘制 geojson 出现错误

5
我正在尝试可视化俄罗斯的地区。我从这里获取了数据:这里,在这里验证过了,一切都很好 - 图片。但是当我尝试绘制它时,我只得到一个大黑色矩形。
var width = 700, height = 400;

var svg = d3.select(".graph").append("svg")
        .attr("viewBox", "0 0 " + (width) + " " + (height))
        .style("max-width", "700px")
        .style("margin", "10px auto");


d3.json("83.json", function (error, mapData) {
    var features = mapData.features;

    var path = d3.geoPath().projection(d3.geoMercator());

    svg.append("g")
            .attr("class", "region")
            .selectAll("path")
            .data(features)
            .enter()
            .append("path")
            .attr("d", path)
});

示例 - http://ustnv.ru/d3/index.html Geojson 文件 - http://ustnv.ru/d3/83.json

2个回答

10
问题在于坐标的卷绕顺序(参见this block)。大多数工具/实用程序/库/验证器并不真正关心卷绕顺序,因为它们将geoJSON视为包含笛卡尔坐标。但D3不同-D3使用椭球形数学-其中的好处包括能够轻松穿越反子午线以及能够选择一个倒置的多边形。
使用椭球坐标的后果是错误的卷绕顺序将创建一个包含地球上非目标区域的要素(倒置的多边形)。你的多边形实际上包含了两种卷绕顺序的组合。您可以通过检查svg路径来看到这一点:

enter image description here

这里有一条路径看起来被准确地绘制出来,而另一条路径覆盖了整个星球 - 除了它应该占据的部分(被其他覆盖整个世界的路径所占据)。 这很容易解决 - 只需要重新排序坐标即可,但由于您拥有包含相同集合中的两个缠绕的要素,因此使用类似 turf.js 的库创建一个正确缠绕的要素的新数组会更容易些。
    var fixed = features.map(function(feature) {
        return turf.rewind(feature,{reverse:true});
    })

请注意反向绕组顺序——由于一个奇怪的小问题,D3可能是最广泛使用需要考虑绕组顺序的平台,但实际上并不遵循geoJSON规范(RFC 7946)中的绕组顺序,它使用相反的绕组顺序,请查看Mike Bostock的这条评论:
“我对RFC 7946标准化相反的绕组顺序感到失望,因为这会影响到D3、Shapefiles和PostGIS。而且我没有看到D3改变其行为的简单方法,因为这会破坏所有现有的(球形)GeoJSON文件,而这些文件都是由D3使用的。(source)”
通过重新绕组每个多边形,我们可以获得一个稍微更有用的地图:

enter image description here

这是一项改进,但使用这些投影设置时,功能有点小。通过添加fitSize方法进行缩放和平移,我们可以获得更美观的地图(参见block here):

enter image description here


OP的结果没有绕线,几乎就像屏幕上的一只蚂蚁,让我想起了Bostock教程中第2步的地图,他说“较差的制图师现在会宣布工作完成,回家喝啤酒”...哦,我笑翻了! - Gerardo Furtado
我记得那一行,欣赏他的幽默感 - 也记得通过那个教程作为我最初进入d3的尝试。但是,一旦你有了数据并且可以以某种形式显示它,那就是喝啤酒的好时机,通常这已经是成功的一半了。 - Andrew Reid
在我的情况下,当我执行 d3.select("body").append("svg") 时,已经是休息和喝啤酒的时间了... - Gerardo Furtado
经过10个小时的折磨,我终于找到了这个宝藏,谢谢! - Omar Omeiri
您可以从此处倒回您的GeoJSON:https://davidb.dev/tools/rewind-geojson-online - Hamed Damirchi

-2

这里有一个快速解决方案,投影需要进行一些微调,同时路径默认具有fill:#000,而stroke:#FFF可以使其更易读。

var width = 700, height = 400;

var svg = d3.select(".graph").append("svg")
        .attr("viewBox", "0 0 " + (width) + " " + (height))
        .style("max-width", "700px")
        .style("margin", "10px auto");


d3.json("mercator_files/83.json", function (error, mapData) {
    var features = mapData.features;


    var center = d3.geoCentroid(mapData);
    //arbitrary
    var scale  = 7000;
    var offset = [width/2, height/2];
    var projection = d3.geoMercator().scale(scale).center(center)
      .translate(offset);

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

    svg.append("g")
            .attr("class", "region")
            .selectAll("path")
            .data(features)
            .enter()
            .append("path")
            .attr("d", path)
});

虽然路径样式和投影需要调整,但我不认为这解决了核心问题:特征被反转,因此覆盖整个地图平面。使用您的代码,我发现这个,并非所有特征都可见(所有特征都已绘制),而且路径仍超出svg的边界(这就是为什么fitSize()/fitExtent()不能与这些特征一起使用,这也可能导致d3.geoCentroid()的行为异常)。 - Andrew Reid

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接