D3.js + osm geojson = black rectangle

时光毁灭记忆、已成空白 提交于 2019-11-29 17:09:03

The issue is the winding order of the coordinates (see this block). Most tools/utilities/libraries/validators don't really care about winding order because they treat geoJSON as containing Cartesian coordinates. Not so with D3 - D3 uses ellipsoidal math - benefits of this is include being able to cross the antimeridian easily and being able to select an inverted polygon.

The consequence of using ellipsoidal coordinates is the wrong winding order will create a feature of everything on the planet that is not your target (inverted polygon). Your polygons actually contain a combination of both winding orders. You can see this by inspecting the svg paths:

Here one path appears to be accurately drawn, while another path on top of it covers the entire planet - except for the portion it is supposed to (the space it is supposed to occupy covered by other paths that cover the whole world).

This can be simple to fix - you just need to reorder the coordinates - but as you have features that contain both windings in the same collection, it'll be easier to use a library such as turf.js to create a new array of properly wound features:

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

Note the reverse winding order - through an odd quirk, D3, which is probably the most widespread platform where winding order matters actually doesn't follow the geoJSON spec (RFC 7946) on winding order, it uses the opposite winding order, see this comment by 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)

By rewinding each polygon we get a slightly more useful map:

An improvement, but the features are a bit small with these projection settings.

By adding a fitSize method to scale and translate we get a much better looking map (see block here):

Here's a quick fix to your problem, projection needs a little tuning, also path has fill:#000 by default and stroke: #FFF could make it more legible.

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)
});
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!