d3.js 世界 topojson v4

d3.js world topojson v4

我正在尝试让世界地图显示类似于 U.S。此处提供地图 (U.S. map),但我 运行 遇到了一些问题。目前我的世界地图代码是这样的:

<!DOCTYPE html>
<style>

</style>
<svg width="960" height="600"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<script>

var svg = d3.select("svg");

var path = d3.geoPath();

d3.json("https://unpkg.com/world-atlas@1/world/50m.json", function(error, world) 
{
    if (error) throw error;

    svg.append("g")
        .selectAll("path")
        .data(topojson.feature(world, world.objects.countries).features)
        .enter().append("path")
          .attr("d", path);

});

</script>

但是,它显示的地图只占据了我 window 的左上部分,看起来不正确。 目前我对我可能做错了什么感到困惑。如果有人有任何见解,我将不胜感激。

谢谢。

编辑:我实际上能够使用此处提供的代码让它工作

<!DOCTYPE html>
<style>

    .countries :hover
    {
        fill: red;
    }

    .country-borders
    {
        fill:   none;
        stroke: #FFF;
        stroke-width: 0.5px;
        stroke-linejoin: round;
        stroke-linecap: round;
        cursor: pointer;

    }

</style>
<meta charset="utf-8">
<body>
<script src="http://d3js.org/d3.v4.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script>
var format = d3.format(",");


var margin = {top: 0, right: 0, bottom: 0, left: 0},
            width = 1200 - margin.left - margin.right,
            height = 900 - margin.top - margin.bottom;


var path = d3.geoPath();

var svg = d3.select("body")
            .append("svg")
            .attr("width", width)
            .attr("height", height)
            .append('g')
            .attr('class', 'map');

var projection = d3.geoMercator()
                   .scale(170)
                  .translate( [width / 2, height / 1.5]);

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


d3.json("https://unpkg.com/world-atlas@1/world/50m.json", function( error, world ) 
{
    svg.append("g")
      .attr("class", "countries")
    .selectAll("path")
      .data(topojson.feature( world, world.objects.countries ).features )
    .enter().append("path")
      .attr("d", path);


    svg.append("path")
        .attr( "class", "country-borders" )
        .attr("d", path(topojson.mesh( world, world.objects.countries, function( a, b) 
        {
            return a !== b;
        })));
});

</script>
</body>
</html>

我认为我的缩放比例有误,没有正确地投影到地图上。如果有人对我的做法有任何意见,请告诉我。

您引用的美国 json 是一个特殊的 json 文件 - 它不需要使用投影。您的 path 变量使用空投影(默认投影),将 geojson 中的坐标(topojson 转换成的坐标)转换为像素值而不进行转换。

创建此文件可能是为了允许更简单的块通过跳过投影细节来显示 d3 功能,但无论如何它相对独特,因为它是如何投影的。

美国 json 文件中的每个顶点都已从表示 3 维地球上的点的 latitude/longitude 对转换为表示宽度为 960 的平面的笛卡尔 x,y 坐标高度为 500(bl.ock 默认视图的宽度和高度)。您的世界数据仍然包含纬度经度对,因此需要使用投影。

您的数据未投影是因为它是颠倒的,这是在将纬度经度数据转换为 svg 中的像素坐标而没有定义投影的变换时产生的。北极在北纬 90 度,赤道在 0 度。使用 SVG 时,y 坐标为零将位于屏幕顶部,y 坐标为 90 将向下 90 像素。因此,将纬度经度点转换为 x,y svg 点而不进行转换时,北极将位于赤道下方。此外,西半球和南半球的 x and/or y 值为负,因此如果不进行转换,它们将不会出现在您的 svg 中。这就是为什么您在地图上只能看到本初子午线以东和赤道以北的点。

如何解决这个问题?您需要投影数据。您可以使用任何一种投影,我将使用墨卡托,因为它可能比其他投影更容易辨认(尽管在两极附近存在严重的失真问题):

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

就是这样 (see here)。

供参考,墨卡托投影的默认参数旨在显示 960 像素的地球,如果您想专注于特定区域或显示不同尺寸(比例)的地图,则需要设置投影参数。