Published
Edited
May 16, 2020
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
P = d3.geoAzimuthalEqualArea() // geoOrthographic()
.translate([size / 2, size / 2])
.clipExtent([[0, 0], [size, size]])
.precision(0.1)
Insert cell
Insert cell
Insert cell
getVisibleTiles = function(projection, zoom) {
function getVisibleChildren(x, y, z) {
//console.log(x, y, z);
let visible = false;
// Define a projection stream based on the input projection. This stream first does all of
// the operations (rotating, clipping, and resampling) that the input projection would do.
// Then, any geometries that survive that process (i.e. are visible in the output projection)
// get passed on to our custom stream, which just sets `visible = true` if it sees a point.
// We'll use this stream to determine if a tile is visible when projected through `projection`.
let stream = projection.stream({
point: () => { visible = true; },
lineStart: () => {},
lineEnd: () => {},
polygonStart: () => {},
polygonEnd: () => {}
});
// calculate screen-space coordinates of tile (x, y, z) in the `mercator` projection below
const extent = mercator.clipExtent();
// we assume that extent is a square, and thus all tiles are also squares
const size = (extent[1][0] - extent[0][0]) / (2 ** z);
const epsilon = 0.001;
const x0 = x * size + epsilon,
y0 = y * size + epsilon,
x1 = (x + 1) * size - epsilon,
y1 = (y + 1) * size - epsilon;

const step = Math.min(size, 1);
const tile = {
type: "Polygon",
coordinates: [
[[x0, y0]]
// Interpolate along each of the four edges of the Mercator polygon. This is a rather
// crude way to do interpolation (with a constant step size) but it's easier than the
// adaptive resampling that d3 does natively, which is not available to us here since
// we're not using a projection.
.concat(d3.range(x0, x1 + step / 2, +step).map(x => [x, y0]))
.concat([y0, y1].map(y => [x1, y]))
// .concat(d3.range(y0, y1 + step / 2, +step).map(y => [x1, y]))
.concat(d3.range(x1, x0 - step / 2, -step).map(x => [x, y1]))
.concat([y1, y0].map(y => [x0, y]))
// .concat(d3.range(y1, y0 - step / 2, -step).map(y => [x0, y]))
// convert this mercator polygon to a spherical polygon on the Earth
.map(point => mercator.invert(point))
],
};
// Project the spherical polygon representing the tile by feeding it through the stream.
d3.geoStream(tile, stream);
// If any point in the tile was visible in the output projection, then our stream will have
// received that point via its `point` method and set `visible = true`.
if (visible) {
if (z < zoom) {
// if we're not yet at the desired zoom level, recurse on each of the four sub-tiles
let visibleChildren = []
.concat(getVisibleChildren(2 * x, 2 * y, z + 1)) // upper left
.concat(getVisibleChildren(2 * x + 1, 2 * y, z + 1)) // upper right
.concat(getVisibleChildren(2 * x, 2 * y + 1, z + 1)) // lower left
.concat(getVisibleChildren(2 * x + 1, 2 * y + 1, z + 1)) // lower right
return visibleChildren;
} else {
return [tile]; // this tile is visible and is of the desired zoom level
}
} else {
return []; // this tile is not visible
}
}

return getVisibleChildren(0, 0, 0);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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