首页 > 解决方案 > D3 实现:D3 地图背后的自定义拓扑图

问题描述

我需要显示具有拓扑/阴影浮雕背景的 D3 地图。需要实现所有用户功能(例如缩放和平移)

到目前为止,我已经将地图分层到具有拓扑的 PNG 上。然后我对投影进行了一些修改,以将 PNG 边框与地图边框对齐。然后我允许用户缩放 PNG(例如:http ://bl.ocks.org/pbogden/7363519 )。结果实际上非常好。当我平移和缩放时,地图会随着很棒的 PNG 移动(下图):

在此处输入图像描述

问题是PNG非常重(20MB),整个结果体验严重错误,以至于无法使用。结果显然是使用了较低分辨率的图像,但是当用户放大时,拓扑看起来很糟糕。我尝试将 PNG 转换为 JPG ......这实际上更糟!

在 D3 中实现我的目标的最佳解决方案是什么?初步想法如下:

(1) d3.geo.tile 插件 ( http://bl.ocks.org/mbostock/4132797 )。这里的困难是我需要从我的 PNG 图像创建自己的图块。这是一个有前途的途径吗?我可以在上面叠加一张 D3 地图吗?我找不到带有自定义图块的示例。

(2) 我已经看到了 OpenSeaDragon 和 D3 的成功实施 ( http://bl.ocks.org/zloysmiertniy/0ab009ca832e7e0518e585bfa9a7ad59 )。这里的问题是我不确定是否可以实现所需的 D3 功能(缩放、平移、过渡),以便 D3 地图和基础图像同时移动。

(3) 还有其他想法或想法吗?

标签: javascriptd3.jsopenseadragon

解决方案


要将图像转换为图块,您需要具有地理参考图像 - 或者能够自己对图像进行地理参考。我相信您使用的是自然地球数据集来创建此图像,您可以使用源 tif 文件并使用它。我通常使用瓷砖磨来制作瓷砖(使用一些 python),它相当简单。您将无法将 png 原样用于瓷砖。

但是,如果您正在寻找山体阴影或某种高程/地形纹理指示,则无需在图块集上创建。使用此处的传单示例,您可以找到很多 tile 提供商,ESRI.WorldShadedRelieve 看起来很符合要求。这是一个将其拉入 d3 的演示,并在其上绘制了一个 topojson 功能:

var pi = Math.PI,
        tau = 2 * pi;

    var width = 960;
        height = 500;

    // Initialize the projection to fit the world in a 1×1 square centered at the origin.
    var projection = d3.geoMercator()
        .scale(1 / tau)
        .translate([0, 0]);

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

    var tile = d3.tile()
        .size([width, height]);

    var zoom = d3.zoom()
        .scaleExtent([1 << 11, 1 << 14])
        .on("zoom", zoomed);

    var svg = d3.select("svg")
        .attr("width", width)
        .attr("height", height);

    var raster = svg.append("g");
    var vector = svg.append("g");

    // Compute the projected initial center.
    var center = projection([-98.5, 39.5]);

    d3.json("https://unpkg.com/world-atlas@1/world/110m.json",function(error,data) {
    
     vector.append("path")
       .datum(topojson.feature(data,data.objects.land))
       .attr("stroke","black")
       .attr("stroke-width",2)
       .attr("fill","none")
       .attr("d",path)

    // Apply a zoom transform equivalent to projection.{scale,translate,center}.
    svg
        .call(zoom)
        .call(zoom.transform, d3.zoomIdentity
            .translate(width / 2, height / 2)
            .scale(1 << 12)
            .translate(-center[0], -center[1]));
     
    })

    function zoomed() {
        var transform = d3.event.transform;

        var tiles = tile
            .scale(transform.k)
            .translate([transform.x, transform.y])
            ();

        projection
            .scale(transform.k / tau)
            .translate([transform.x, transform.y]);

        var image = raster
            .attr("transform", stringify(tiles.scale, tiles.translate))
            .selectAll("image")
            .data(tiles, function(d) {
                return d;
            });

        image.exit().remove();
        // enter:
        var entered = image.enter().append("image");
        // update:
        image = entered.merge(image)
            .attr('xlink:href', function(d) {
                return 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer/tile/' + d.z + '/' + d.y + '/' + d.x + '.png';
            })
            .attr('x', function(d) {
                return d.x * 256;
            })
            .attr('y', function(d) {
                return d.y * 256;
            })
            .attr("width", 256)
            .attr("height", 256);
            
      vector.selectAll("path")
        .attr("transform", "translate(" + [transform.x, transform.y] + ")scale(" + transform.k + ")")
        .style("stroke-width", 1 / transform.k);
    }

    function stringify(scale, translate) {
        var k = scale / 256,
            r = scale % 1 ? Number : Math.round;
        return "translate(" + r(translate[0] * scale) + "," + r(translate[1] * scale) + ") scale(" + k + ")";
    }
body {    margin: 0;  }
<svg></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://unpkg.com/d3-tile@0.0.4/build/d3-tile.js"></script>
<script src="https://unpkg.com/topojson-client@3"></script>


推荐阅读