D3.js 绘制geojson不正确

Posted

技术标签:

【中文标题】D3.js 绘制geojson不正确【英文标题】:D3.js Drawing geojson incorrectly 【发布时间】:2018-08-24 22:41:53 【问题描述】:

我正在尝试可视化俄罗斯地区。我从here 获得数据,验证here,一切都很好 - 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

【问题讨论】:

【参考方案1】:

问题在于坐标的缠绕顺序(请参阅this block)。大多数工具/实用程序/库/验证器并不真正关心缠绕顺序,因为它们将 geoJSON 视为包含笛卡尔坐标。 D3 并非如此 - D3 使用椭圆体数学 - 这样做的好处包括能够轻松穿过反子午线并能够选择倒置多边形。

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

这里似乎准确地绘制了一条路径,而其顶部的另一条路径覆盖了整个星球 - 除了它应该占据的部分(它应该占据的空间被覆盖整个世界的其他路径所覆盖) .

这很容易解决 - 您只需要重新排序坐标 - 但由于您的特征包含同一集合中的两个绕组,因此使用诸如 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 的简单方法 改变它的行为,因为它会破坏所有现有的(球形) D3 使用的 GeoJSON。 (source)

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

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

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

【讨论】:

OP 没有绕线的结果,几乎是屏幕上的一只蚂蚁,这让我想起了Bostock's tutorial 的第 2 步中的地图,他说 “较小的制图师现在会宣布一份工作干得好,回家喝杯啤酒”...哦,我崩溃了! 我记得那句台词,欣赏他的幽默感 - 还记得我在最初涉足 d3 时完成了该教程。但是,一旦您拥有了数据并且可以以某种形式显示它,现在就是喝啤酒的好时机,这通常是成功的一半。 在我的情况下,当我执行d3.select("body").append("svg") 时,已经是休息和喝啤酒的时候了... 拉头发 10 小时后,我找到了这颗宝石,谢谢! 你可以从这里倒回你的geojson:davidb.dev/tools/rewind-geojson-online【参考方案2】:

这里有一个快速解决您的问题的方法,投影需要稍作调整,路径默认为 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)
);

【讨论】:

虽然路径样式和投影需要调整,但我看不出这如何解决核心问题:要素被倒置,从而覆盖整个地图平面。使用您的代码我发现 this ,并非所有功能都是可见的(所有都被绘制)并且路径仍然超出 svg 的范围(这就是为什么 fitSize()/fitExtent() 不适用于这些功能,这也可以导致 d3.geoCentroid() 的行为不正常

以上是关于D3.js 绘制geojson不正确的主要内容,如果未能解决你的问题,请参考以下文章

使用D3.js绘制地图并打点

班加罗尔geojson地图与d3 js

d3.js 地图:使用 geojson 文件和 CSV 数据

在d3.js中的点之间绘制线条

平滑 D3.js/GeoJSON/TopoJSON/Shapefile 中的弧线/绘图点(沿途某处)

GeoJson 世界数据库 [关闭]