I've been trying for about 10 days to use D3.js to create map displaying vector data (geojson) and raster tiles that I can zoom and pan, but can't just get it to work. I can zoom and pan the vector data but not the tiles.
I noticed that when I call tile(transform)
I get the exact same x, y and z for the tiles despite having what I think are meaningful numbers in the transform (e.g. {"k": 1, "x": -35, "y": -37}). What am I messing up? I'm assuming the problem lies somewhere in the snippet below, though I also have the whole code further down.
const tile = d3.tile()
.extent([[0, 0], [width, height]])
let zoom = d3.zoom()
.on('zoom', () => zoomed(d3.event.transform))
svg.call(zoom)
function zoomed(transform) {
const tiles = tile(transform);
lines.attr('transform', transform.toString())
images = images.data(tiles, d => d)
.join('image')
.attr('xlink:href', d => 'https://tiles.wmflabs.org/bw-mapnik/' + d[2] + '/' + d[0] + '/' + d[1] + '.png')
.attr('x', ([x]) => (x + tiles.translate[0]) * tiles.scale)
.attr('y', ([, y]) => (y + tiles.translate[1]) * tiles.scale)
.attr('width', tiles.scale)
.attr('height', tiles.scale);
}
Below are the data and the full script.
{
"type": "FeatureCollection",
"name": "lines",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{ "type": "Feature", "properties": { "id": 3}, "geometry": { "type": "LineString", "coordinates": [ [ -74.201304101157845, 40.033790926216739 ], [ -74.201226425025339, 40.033761910802717 ], [ -74.201164135201353, 40.033738641825124 ] ] } },
{ "type": "Feature", "properties": { "id": 4}, "geometry": { "type": "LineString", "coordinates": [ [ -74.200521185229846, 40.034804885753857 ], [ -74.200535458528648, 40.034780636493231 ], [ -74.200698022608137, 40.034504451003734 ], [ -74.200932444446437, 40.034106179618831 ], [ -74.201017665586349, 40.033961391736824 ] ] } },
{ "type": "Feature", "properties": { "id": 5}, "geometry": { "type": "LineString", "coordinates": [ [ -74.243246789888346, 40.038535382572938 ], [ -74.243221882547942, 40.038496558418132 ], [ -74.243182498790546, 40.038435169141529 ], [ -74.243137176965064, 40.038364523771932 ], [ -74.243057495263045, 40.038240319569326 ], [ -74.242984178076739, 40.038126035633333 ], [ -74.242983803896664, 40.038125452375041 ], [ -74.242970932683164, 40.03810547276553 ], [ -74.242927580863451, 40.038038178902433 ] ] } }
]
}
let width = 900,
height = 500,
initialScale = 1 << 17,
initialCenter = [-74.20465,39.98783]
let svg = d3.select('body')
.append('svg')
.attr('height', height)
.attr('width', width)
let map = svg.append('g')
let images = map.append('g')
.attr('pointer-events', 'none')
.selectAll('image');
let glines = map.append('g')
let lines = glines.append('g')
let projection = d3.geoMercator()
.scale(initialScale)
.center(initialCenter)
.translate([width / 2, height / 2])
let path = d3.geoPath(projection)
const tile = d3.tile()
.extent([[0, 0], [width, height]])
let zoom = d3.zoom()
.on('zoom', () => zoomed(d3.event.transform))
svg.call(zoom)
function zoomed(transform) {
const tiles = tile(transform);
lines.attr('transform', transform.toString())
images = images.data(tiles, d => d)
.join('image')
.attr('xlink:href', d => 'https://tiles.wmflabs.org/bw-mapnik/' + d[2] + '/' + d[0] + '/' + d[1] + '.png')
.attr('x', ([x]) => (x + tiles.translate[0]) * tiles.scale)
.attr('y', ([, y]) => (y + tiles.translate[1]) * tiles.scale)
.attr('width', tiles.scale)
.attr('height', tiles.scale);
}
function drawMap(data) {
let tiles = tile
.scale(projection.scale() * 2 * Math.PI)
.translate(projection([0, 0]))();
lines.selectAll('path')
.data(data.features)
.enter()
.append('path')
.attr('class', 'line')
.attr('d', path)
images = images.data(tiles, d => d)
.enter()
.append('image')
.attr('xlink:href', d => 'https://tiles.wmflabs.org/bw-mapnik/' + d[2] + '/' + d[0] + '/' + d[1] + '.png')
.attr('x', d => (d[0] + tiles.translate[0]) * tiles.scale)
.attr('y', d => (d[1] + tiles.translate[1]) * tiles.scale)
.attr('width', tiles.scale)
.attr('height', tiles.scale);
}
d3.json('test.geojson').then(drawMap)
I found an easier way of putting lines on top of a basemap, by using D3 + Leaflet. See https://observablehq.com/@sfu-iat355/intro-to-leaflet-d3-interactivity . Works wonders.