简体   繁体   中英

How to retrieve Vector Tiles from Mapbox with d3.js and convert to geojson?

I am trying to replicate the example from Mike Bostock: https://observablehq.com/@d3/mapbox-vector-tiles

Since the language of Observable is not native Javascript, I am unable to get the example running.

Especially, the following two functions I am unable to get to work:

  • VectorTile = (await require("https://bundle.run/@mapbox/vector-tile@1")).VectorTile
  • Protobuf = require("pbf@3/dist/pbf.js")

require() is not a Javascript command. So, how can I get these two libraries?

What I tried:

  • Insert the libraries via <script></script> tags
  • Loading with await :

     let VectorTile = await fetch('https://bundle.run/@mapbox/vector-tile@1.3.1'); let Protobuf = await fetch('https://unpkg.com/pbf@3.0.5/dist/pbf.js');
  • I am not sure if require() comes from node.js. So I played around with node.js but did not find a working solution either.

So, my question is: How can I get the example from Mike Bostock to work? Or in more general manner: How should I load vector tiles from Mapbox that I can convert them to geojson format as Mike is it doing in this example?

Regarding your first question, in order to get Mike Bostock's example to work you can keep in mind considerations:

  1. Promises are something that Observable takes care of automatically, while node.js or JavaScript in browser does not, so you should take care of them
  2. Add all the necessary libraries, while keeping in mind that the way you call them might be different from how Mike does so. As, for example, by default you should use Pbf, not Protobuf if you use pbf library (And yes require() comes from node.js)
  3. For @mapbox/vector-tile library notice that it exports 3 objects, while you need VectorTile function (in node.js you could just use require().VectorTile ). You can see how I've done it below for JavaScript in html
  4. Finally you will need to insert generated svg code into html in order to display it, this is also something that Observable does automatically

I can't really tell how you 'should' load vector tiles, as I don't have much expertise in this particular aria. However, below I converted Mike Bostock's example to javascript, so you can see how it works outside of 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>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM