Published
Edited
Jan 26, 2022
2 forks
Importers
9 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
factory = buildFactory((z, x, y) => tileSource.url(x, y, z))
Insert cell
Insert cell
Insert cell
mutable cacheTileSize = tileSource.realTileSize();
Insert cell
resolutionString = {
let mqString = `(resolution: ${devicePixelRatio}dppx)`;
matchMedia(mqString).addEventListener("change", () => {
mutable cacheTileSize = tileSource.realTileSize();
});
return mqString;
}
Insert cell
cache = tileRack.initCache(cacheTileSize, factory)
Insert cell
Insert cell
Insert cell
tile = d3
.tile()
.tileSize(tileSource.cssTileSize())
.size([width, height])
.clampX(false) // Allow panning across longitude 180 degrees
Insert cell
Insert cell
tileset = tile(transform)
Insert cell
Insert cell
mutable transform = d3.zoomIdentity
.translate(width / 2, height / 2) // Center at the middle of the Canvas
.scale(2 ** 21) // Start at zoom level 13, plus 2 ** 8 tile size
.translate(...projection(lonLatSF).map((c) => -c)) // Center on San Francisco
Insert cell
projection = d3
.geoMercator()
.scale(1 / (2 * Math.PI))
.translate([0, 0])
Insert cell
lonLatSF = [-122.4183, 37.775]
Insert cell
Insert cell
transform.apply(projection(lonLatSF))
Insert cell
Insert cell
zoom = d3
.zoom()
.scaleExtent([1 << 8, 1 << 26])
.extent([[0, 0], [width, height]])
.on("zoom", () => {
mutable transform = d3.event.transform;
})
Insert cell
Insert cell
d3
.select(mapContext.canvas)
.call(zoom)
.call(zoom.transform, mutable transform)
Insert cell
Insert cell
upate = {
drawTiles(mapContext, tileset);
}
Insert cell
Insert cell
factory.element.addEventListener(
"tileLoaded",
() => drawTiles(mapContext, tileset),
false
)
Insert cell
Insert cell
function drawTiles(ctx, tiles) {
ctx.clearRect(0, 0, width, height);
let scale = tiles.scale;
// Edit translation: make sure the first tile starts at an integer pixel coordinate
const tx = Math.round(tiles.translate[0] * scale) / scale;
const ty = Math.round(tiles.translate[1] * scale) / scale;
// If we are on an integer zoom level, all tiles will now be pixel-aligned

for (const [x, y, z] of tiles) {
const [xw, yw, zw] = d3.tileWrap([x, y, z]);
let tileBox = cache.retrieve([zw, xw, yw]);
if (!tileBox) continue;
drawTileBox(ctx, tileBox, x + tx, y + ty, scale);
}
}
Insert cell
Insert cell
function drawTileBox(ctx, box, x, y, scale) {
ctx.drawImage(
box.tile.img,
box.sx,
box.sy,
box.sw,
box.sw,
x * scale,
y * scale,
scale,
scale
);
}
Insert cell
Insert cell
Insert cell
function rescalePixels(cssTileSize) {
return devicePixelRatio > 1 ? 2 * cssTileSize : cssTileSize;
}
Insert cell
Insert cell
Insert cell
tileRack = import("tile-rack@0.0.2")
Insert cell
d3 = require("d3@5", "d3-geo@1", "d3-tile@1")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more