Public
Edited
Dec 15, 2024
Importers
Insert cell
Insert cell
import {cell} from "@user/notebook"
Insert cell
accessToken = 'pk.eyJ1IjoiYmxhc3Rlcm50IiwiYSI6ImNsYnl5aHZkajAyMzYzcHFtbmcyZjBma3YifQ.0HdV5k2GBktcA9WbZ5UpRw';
Insert cell
fetchTerrainTileMapbox = (x, y, z) => d3.image(`https://api.mapbox.com/v4/mapbox.terrain-rgb/${z}/${x}/${y}.pngraw?access_token=${accessToken}`, {crossOrigin: "anonymous"})
Insert cell
getElevationMapbox = (r, g, b) => -10000 + ((r * 256 * 256 + g * 256 + b) * 0.1)
Insert cell
fetchTerrainTileTerrarium = (x, y, z) => d3.image(`https://elevation-tiles-prod.s3.amazonaws.com/terrarium/${z}/${x}/${y}.png`, {crossOrigin: "anonymous"})
Insert cell
getElevationTerrarium = (r, g, b) => (r * 256 + g + b / 256) - 32768
Insert cell
getElevationData = async (
tiles,
tileSize,
outputWidth,
outputHeight,
fetchTerrainTileCb,
getElevationCb,
inputCanvas = new OffscreenCanvas(256, 256),
outputCanvas = new OffscreenCanvas(256, 256)
) => {
const [x0, y0] = tiles[0];
const [x1, y1] = tiles[tiles.length - 1];
const tilesWide = x1 - x0 + 1;
const tilesHigh = y1 - y0 + 1;
inputCanvas.width = tilesWide * tileSize;
inputCanvas.height = tilesHigh * tileSize;
const inputContext = inputCanvas.getContext("2d");
for (const [x, y, image] of await Promise.all(
tiles.map(([x, y, z]) =>
fetchTerrainTileCb(x, y, z).then((image) => [x, y, image])
)
)) {
inputContext.drawImage(
image,
(x - x0) * tileSize,
(y - y0) * tileSize,
tileSize,
tileSize
);
}
outputCanvas.width = outputWidth;
outputCanvas.height = outputHeight;
const outputContext = outputCanvas.getContext("2d");

outputContext.drawImage(
inputContext.canvas,
Math.round((x0 + tiles.translate[0]) * tiles.scale),
Math.round((y0 + tiles.translate[1]) * tiles.scale),
tilesWide * tiles.scale,
tilesHigh * tiles.scale
);
const demImageData = outputContext.getImageData(
0,
0,
outputWidth,
outputHeight
);
const demData = demImageData.data;
const values = new Float64Array(outputWidth * outputHeight);
for (let y = 0; y < outputHeight; y++) {
for (let x = 0; x < outputWidth; x++) {
let k = x + y * outputWidth;
let k4 = 4 * k;
values[k] = getElevationCb(demData[k4], demData[k4 + 1], demData[k4 + 2]);
}
}
return values;
}
Insert cell
elevationCanvas = (width, height, elevationData) => {
const canvas = DOM.canvas(width, height);
canvas.id = "elevation-canvas";

const ctx = canvas.getContext("2d");

const imgData = ctx.getImageData(0, 0, width, height);
const data = imgData.data;
const [min, max] = d3.extent(elevationData);
const scale = d3.scaleLinear().domain([min, max]).rangeRound([0, 255]);
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
let k = x + y * width;
let k4 = 4 * k;
const elevation = elevationData[k];
const color = scale(elevation);
data[k4] = color;
data[k4 + 1] = color;
data[k4 + 2] = color;
data[k4 + 3] = 255;
data[k4 + 3] = 255;
}
}
ctx.putImageData(imgData, 0, 0);
return canvas;
}
Insert cell
projectContours3DThree = (
width,
height,
contours,
threeCamera,
metersPerPixel
) => {
return turf.geometryCollection(
contours.geometries.map((contour) => ({
...contour,
coordinates: contour.coordinates.map((polygon) =>
polygon.map((path) =>
path.map((point) => {
const vec3 = new THREE.Vector3(
point[0] - width / 2,
contour.value / metersPerPixel,
point[1] - height / 2
);
const screenVec = vec3.project(threeCamera);
return [
(screenVec.x * width) / 2 + width / 2,
(screenVec.y * -height) / 2 + height / 2
];
})
)
)
}))
).geometry;
}
Insert cell
projectContours3DDeck = (width, height, geoContours, viewState, deckMap) => {
const viewport = deckMap.getViewports()[0];
return turf.geometryCollection(
geoContours.features.map((feature) => ({
...feature.geometry,
coordinates: feature.geometry.coordinates.map((polygon) =>
polygon.map((path) =>
path.map((point) => {
return viewport
.project([point[0], point[1], point[2]], {
topLeft: true
})
.map((point) => point / 2);
})
)
)
}))
).geometry;
}
Insert cell
svg2DOutput = (
width,
height,
contours,
contoursPerIndexLine,
fill = "#000",
stroke = "#fff"
) => {
const svg = d3
.create("svg")
.attr("viewBox", [0, 0, width, height])
.style("display", "block")
.style("background", "black")
.style("width", width * 2)
.style("height", height * 2);

svg
.append("g")
.attr("fill", fill)
.attr("stroke", stroke)
.attr("stroke-opacity", 1)
.selectAll("path")
.data(contours.geometries)
.join("path")
.attr("d", d3.geoPath())
.style("vector-effect", "non-scaling-stroke")
.style("shape-rendering", "geometricPrecision")
.attr("stroke-width", (contour, i) =>
i % contoursPerIndexLine === 0 ? 3 : 1
)
.attr(
"class",
(contour, i) => `${i} ${i % contoursPerIndexLine === 0 ? "every-5" : ""}`
);
return svg.node();
}
Insert cell
svgTransform3DOutput = (
width,
height,
contours,
contoursPerIndexLine,
metersPerPixel
) => {
const svg = d3
.create("svg")
.attr("viewBox", [0, 0, width, height])
.style("display", "block")
.style("background", "black")
.style("width", width * 2)
.style("height", height * 2)
// .style("transform", "perspective(5000px) rotate3d(1, 0, 0, 66deg)")
.style("transform-style", "preserve-3d");

svg
.append("g")
.style("transform-style", "preserve-3d")
.attr("fill", "#000")
.attr("stroke", "#fff")
.attr("stroke-opacity", 1)
.selectAll("path")
.data(contours)
.join("path")
.attr("d", d3.geoPath())
.style("vector-effect", "non-scaling-stroke")
.style("shape-rendering", "geometricPrecision")
.style("transform-style", "preserve-3d")
.style("transform-origin", "center")
.style(
"transform",
(d) =>
`scale3d(0.5, 0.5, 0.5) rotate3d(1, 0, 0, 45deg) rotate3d(0, 0, 1, 45deg) translate3d(0, 0, ${
(d.value * metersPerPixel) / 100
}px)`
)
.attr("data-altitude", contours.value)
.attr("stroke-width", (contour, i) =>
i % contoursPerIndexLine === 0 ? 3 : 1
)
.attr(
"class",
(contour, i) => `${i} ${i % contoursPerIndexLine === 0 ? "every-5" : ""}`
);
return svg.node();
}
Insert cell
set = (input, value) => {
input.value = value;
input.dispatchEvent(new Event("input", {bubbles: true}));
}
Insert cell
appxEql = (a, b, epsilon = 1E-05) => Math.abs(a - b) < epsilon
Insert cell
Insert cell
turf = await import("https://cdn.jsdelivr.net/npm/@turf/turf@7.1.0/+esm")
Insert cell
THREE = {
const THREE = await import("https://cdn.jsdelivr.net/npm/three@0.171.0/+esm");
// THREE.Object3D.DefaultUp = new THREE.Vector3(0, 0, 1);
return THREE;
}
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