D3 Web 地图:将栅格 PNG 与 TopoJSON 对齐

D3 Web Map: Align raster PNG with TopoJSON

我正在努力对齐加拿大的 topojson 和 PNG DEM。

我进行了大量搜索,并主要基于 https://gist.github.com/ThomasThoren/5e676d0de41bac8a0e96. My understanding is that the PNG must be preprojected and is simply scaled + translated to the appropriate spot on the page, while the topojson is either fed in as lat/long and reprojected with d3 tools or preprojected and drawn using d3.geoIdentity (as per https://medium.com/p/e9ccc6c0bba3/responses/show) 拼凑了一些测试。尽管我知道这两个数据集在投影坐标 space 中对齐(至少在预投影 topojson 并使用 d3.geoIdentity 时),但我还是无法对齐这两个数据集,这让我相信我在 d3 中遗漏了一些东西代码。有任何想法吗?如果这是解决方案,我在其他工具中操作 topojson 或源栅格没有问题,但我还没有找到适用于此对齐的方法。这是一个预投影 topojson 的示例。无论如何,无论我使用 geoConicConformal 在 d3 中进行预投影还是投影,我都会得到相同的结果。

这是 PNG file:

这是我的 TopoJSON file

这是我用来显示 topojson 和 png 的代码:

<html>
<head>
    <meta charset="utf-8">
    <title>D3 Canada</title>
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/3.0.2/topojson.min.js"></script>
    <script src="test-canada-topojson-canlam.js"></script>
    <style>
        body {
            padding: 0;
            margin: 0;
            background: whitesmoke;
            display: flex;
            flex-direction: column;
            align-items: center;
        }
        .province {
            fill: white;
            fill-opacity: 10%;
            transition: fill 0.25s ease-in-out;
        }
        .province:hover {
            fill: aliceblue;
            fill-opacity: 90%;
            transition: fill 0.25s ease-in-out;
        }
    </style>
</head>
<body>
        <div class="home"></div>
    <script>
        var width = window.innerWidth,
        height = window.innerHeight;

        const container = d3.select(".home");
        var svg = container.append("svg");

        svg.attr("width", width)
           .attr("height", height);

        var provinces = topojson.feature(canada,canada.objects.canada).features;

        var projection = d3.geoIdentity().reflectY(true).fitSize([width,height],topojson.feature(canada, canada.objects.canada));
        
        var path = d3.geoPath(projection);

        var bounds = path.bounds(topojson.feature(canada, canada.objects.canada));

        var scale = 1 / Math.max((bounds[1][0] - bounds[0][0]) / width,(bounds[1][1] - bounds[0][1]) / height);

        var raster_width = (bounds[1][0] - bounds[0][0]) * scale;
        var raster_height = (bounds[1][1] - bounds[0][1]) * scale;

        var rtranslate_x = (width - raster_width) / 2;

        var rtranslate_y = (height - raster_height) / 2;

        const g = svg.append("g");

        g.append("image")
            .attr("xlink:href", "canada-dem-conic.png")
            .attr("class", "raster")
            .attr("width", raster_width)
            .attr("height", raster_height)
            .attr("transform",
                        "translate(" + rtranslate_x + ", " + rtranslate_y + ")");

        g.append("g")
        .selectAll("path")
            .data(provinces)
            .enter()
            .append("path")
            .attr("d", path)
            .attr("stroke", "lightslategrey")
            .attr("stroke-width", 1.5)
            .attr("stroke-linejoin","round")
            .attr("class", "province")

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

以上结果:

我承认这比我希望的更令人沮丧。通常我会指出诸如 之类的答案。不幸的是,这实际上给了你已经使用过的投影:

var projection = d3.geoConicConformal()  
    .parallels([50,70])
    .rotate([96,0])
    .fitSize([width,height],geojson)

问题出在别处。

您的光栅像素似乎不是正方形:

当然它们呈现为正方形,但它们代表的面积为 5.7 平方公里乘 7.1 平方公里。

Spacing or cell size is the fixed distance in the x and y dimensions between each pixel in the raster. Some formats store only one spacing value, meaning that it must be the same for both the x and y dimensions – this is often referred to as square cells. source

但 D3 将像素视为正方形。如果我们使用以下方法拉伸您的图片:

var height = width / pixelWidth * pixelHeight

我们应该得到很好的对齐:

至少这看起来应该对齐。

这是该实现中最简单的 example

当然,您可以压缩投影的 geojson 坐标而不是拉伸图像;然而,这比简单地缩放父容器更复杂(保留 fitSize 方法)。