首页 > 解决方案 > 如何使用 d3.js 从 Mapbox 检索矢量切片并转换为 geojson?

问题描述

我正在尝试复制 Mike Bostock 的示例:https ://observablehq.com/@d3/mapbox-vector-tiles

由于 Observable 的语言不是原生 Javascript,我无法让示例运行。

特别是,我无法使用以下两个功能:

require()不是 Javascript 命令。那么,我怎样才能得到这两个库呢?

我尝试了什么:

所以,我的问题是:我怎样才能让 Mike Bostock 的例子起作用?或者以更一般的方式:我应该如何从 Mapbox 加载矢量图块,我可以将它们转换为 geojson 格式,就像 Mike 在这个例子中所做的那样?

标签: node.jsd3.jsmapbox

解决方案


关于您的第一个问题,为了让 Mike Bostock 的示例发挥作用,您可以牢记注意事项:

  1. Promise 是 Observable 自动处理的东西,而浏览器中的 node.js 或 JavaScript 不会,所以你应该注意它们
  2. 添加所有必要的库,同时记住您调用它们的方式可能与 Mike 这样做的方式不同。例如,默认情况下,如果您使用 pbf 库,您应该使用 Pbf,而不是 Protobuf(是require()的来自 node.js)
  3. 对于 @mapbox/vector-tile 库,请注意它导出 3 个对象,而您需要 VectorTile 函数(在 node.js 中您可以只使用require().VectorTile)。你可以在下面看到我是如何为 html 中的 JavaScript 完成的
  4. 最后,您需要将生成的 svg 代码插入 html 以显示它,这也是 Observable 自动执行的操作

我真的不知道你“应该”如何加载矢量图块,因为我在这个特定的咏叹调方面没有太多专业知识。但是,下面我将 Mike Bostock 的示例转换为 javascript,因此您可以看到它在 Observable 之外是如何工作的。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div>
        Map:
        <div id='map'></div>
    </div>
    <script src="https://d3js.org/d3-array.v2.min.js"></script>
    <script src="https://d3js.org/d3-geo.v2.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/d3-tile@1"></script>
    <script src="https://d3js.org/d3-dsv.v2.min.js"></script>
    <script src="https://d3js.org/d3-fetch.v2.min.js"></script>
    <script src="https://unpkg.com/pbf@3.0.5/dist/pbf.js"></script>
    
    
    <script src="https://bundle.run/@mapbox/vector-tile@1.3.1"></script>
    <script>let VectorTile = _mapbox_vectorTile.VectorTile</script>
    <script>
    height = 600;
    width = 954;
    API_KEY = 'cfNfEQR1Qkaz-6mvWl8cpw';// Sign up for an API key: https://www.nextzen.org

projection = d3.geoMercator()
    .center([-122.4183, 37.7750])
    .scale(Math.pow(2, 21) / (2 * Math.PI))
    .translate([width / 2, height / 2]).precision(0);

path = d3.geoPath(projection)

tile = d3.tile()
    .size([width, height])
    .scale(projection.scale() * 2 * Math.PI)
    .translate(projection([0, 0]));
    
function filter({features}, test) {
        return {type: "FeatureCollection", features: features.filter(test)};
      };
      
function geojson([x, y, z], layer, filter = () => true) {
        if (!layer) return;
        const features = [];
        for (let i = 0; i < layer.length; ++i) {
          const f = layer.feature(i).toGeoJSON(x, y, z);
          if (filter.call(null, f, i, features)) features.push(f);
        }
        return {type: "FeatureCollection", features};
      }

tiles_pr = Promise.all(tile().map(async d => {
        d.layers = new VectorTile(new Pbf(await d3.buffer(`https://tile.nextzen.org/tilezen/vector/v1/256/all/${d[2]}/${d[0]}/${d[1]}.mvt?api_key=${API_KEY}`))).layers;
        return d;
      }))

tiles_pr.then(
    function(tiles){


    //console.log(tiles)
    map = `<svg viewBox="0 0 ${width} ${height}" xmlns="http://www.w3.org/2000/svg">${tiles.map(d => `
    <path fill="#eee" d="${path(geojson(d, d.layers.water, d => !d.properties.boundary))}"></path>
    <path fill="none" stroke="#aaa" d="${path(geojson(d, d.layers.water, d => d.properties.boundary))}"></path>
    <path fill="none" stroke="#000" stroke-width="0.75" d="${path(geojson(d, d.layers.roads))}"></path>
    `)}
    </svg>`

    document.getElementById('map').innerHTML=map;
      }
);


    </script>


</body>
</html>


推荐阅读