D3.js 错误地绘制 geojson

D3.js Drawing geojson incorrectly

我正在尝试可视化俄罗斯地区。我从 here, validate here and all was well - picture.

获得了数据

但是当我尝试绘制它时,我只收到一个黑色的大矩形。

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

问题是坐标的缠绕顺序(参见 this block)。大多数 tools/utilities/libraries/validators 并不真正关心缠绕顺序,因为它们将 geoJSON 视为包含笛卡尔坐标。 D3 并非如此 - D3 使用椭球数学 - 这样做的好处包括能够轻松穿过反子午线并能够 select 一个倒置的多边形。

使用椭球坐标的后果是错误的缠绕顺序将创建地球上不是您目标的所有事物的特征(倒多边形)。您的多边形实际上包含两种缠绕顺序的组合。您可以通过检查 svg 路径看到这一点:

这里一条路径似乎被准确地绘制出来,而在它上面的另一条路径覆盖了整个星球——除了它应该覆盖的部分(space 它应该占据被其他路径覆盖的部分覆盖整个世界)。

这很容易修复 - 您只需要重新排序坐标 - 但由于您的特征在同一集合中包含两个绕组,因此使用 turf.js 之类的库会更容易创建一组新的正确缠绕特征:

    var fixed = features.map(function(feature) {
        return turf.rewind(feature,{reverse:true});
    })

注意反向缠绕顺序 - 通过一个奇怪的怪癖,D3,它可能是最广泛的平台,其中缠绕顺序实际上并不遵循 geoJSON 规范(RFC 7946)的缠绕顺序,它使用相反的缠绕订购,请参阅 Mike Bostock 的评论:

I’m disappointed that RFC 7946 standardizes the opposite winding order to D3, Shapefiles and PostGIS. And I don’t see an easy way for D3 to change its behavior, since it would break all existing (spherical) GeoJSON used by D3. (source)

通过倒转每个多边形,我们得到了一张更有用的地图:

一个改进,但这些投影设置的功能有点小。

通过添加 fitSize 方法来缩放和平移,我们得到了更好看的地图(参见 block here):

这里有一个快速解决您的问题的方法,投影需要一点调整,路径默认有 fill:#000stroke: #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)
});