Public
Edited
May 27, 2024
2 forks
5 stars
Insert cell
Insert cell
{
const renderer = new THREE.WebGLRenderer({
antialias: true
});

// Controls
const controls = new THREE.OrbitControls(camera, renderer.domElement);

controls.minDistance = 1.5;
controls.maxDistance = 8;

// update the renderer
renderer.setSize(width, height);
renderer.setPixelRatio(devicePixelRatio);

// Object
const texture = await loadTexture(
await FileAttachment("8k_earth_daymap.jpg").url()
);
texture.needsUpdate = true;

const material = new THREE.MeshBasicMaterial({
// color: "white"
map: texture
});

const geometry = new THREE.SphereGeometry(1, 64, 32);

const sphere = new THREE.Mesh(geometry, material);
// scene.rotation.x = 0.22;
// scene.rotation.y = 0.1;

scene.add(temperatureMesh);
scene.add(sphere);
renderer.render(scene, camera);

// Event listener
controls.addEventListener("change", () => renderer.render(scene, camera));

invalidation.then(() => (controls.dispose(), renderer.dispose()));

// let clock = new THREE.Clock();
// const tl1 = gsap.timeline();

// return renderer.domElement;
let ind = 0;
// const allFiles = [data2, data3, data4, data5, data6];
const allFiles = [
file1,
file2,
file3,
file4,
file5,
file6,
file7,
file8,
file9,
file10
];

// Rotation
// function animate() {
// // console.log("hi");
// scene.rotation.y += 0.0004;
// requestAnimationFrame(animate);
// renderer.render(scene, camera);
// // labelRenderer.render(scene, camera);
// }
// animate();
// while (true) {
// animateFiles(allFiles[ind % allFiles.length]);

// renderer.render(scene, camera);

// await Promises.delay(100, (ind += 1));

// let html_render = html`
// <div style="position: relative; overflow: hidden;">
// ${renderer.domElement}
// <div style="position: absolute; pointer-events: none; top: 0; left: 0; color: white;">

// </div>
// </div>`;
// yield html_render;
// }

// for testing
return renderer.domElement;
}
Insert cell
function animateFiles(colorsIn) {
const count = colorsIn.length;
const colors = new Float32Array(count * 3);
for (let i = 0; i < count; i++) {
const i3 = i * 3;

// Update colors. Currently not the same point as original
temperatureMesh.geometry.attributes.color.array[i3 + 0] = colorsIn[i3 + 0]; //Math.random();
temperatureMesh.geometry.attributes.color.array[i3 + 1] = colorsIn[i3 + 1];
temperatureMesh.geometry.attributes.color.array[i3 + 2] = colorsIn[i3 + 2];
}

temperatureMesh.geometry.attributes.color.needsUpdate = true; // important!
// requestAnimationFrame(animate);
}
Insert cell
function normalizedAnomalyCalc(data) {
// console.log(data);

const out = d3.map(data, (d) => {
if (d.sst === "NA") {
// console.log(d);
d.sst = 0;
}
let anom =
(d.sst - -minMaxValueToUse) / (minMaxValueToUse - -minMaxValueToUse);
return anom > 1 ? 1 : anom < 0 ? 0 : anom;
});
return out;
}
Insert cell
scene = {
const scene = new THREE.Scene();
scene.background = new THREE.Color("black");
// scene.add(temperatureMesh);
return scene;
}
Insert cell
camera = {
const fov = 60;
const aspect = width / height;
const near = 0.1;
const far = 10;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(-1.5, 1.5, 2);
camera.lookAt(new THREE.Vector3(0, 0, 0));
return camera;
}
Insert cell
textureLoader = new THREE.TextureLoader()
Insert cell
loadTexture = {
const loader = new THREE.TextureLoader();
return (url) => new Promise((resolve) => loader.load(url, resolve));
}
Insert cell
scaleAnomaly = d3
.scaleDiverging((t) => d3.interpolateRdBu(1 - t))
.domain([0, 0.5, 1])
Insert cell
minMaxValueToUse = 4
Insert cell
coldColor = new THREE.Color("#053061")
Insert cell
hotColor = new THREE.Color("#67001f")
Insert cell
mixedColor = coldColor.clone()
Insert cell
mixedColor.lerp(hotColor, 0)
Insert cell
// d3.mean(normalizedAnomaly)
Insert cell
new THREE.BufferGeometry()
Insert cell
temperatureMesh = {
// the geometry that will contain all our cubes
const particlesGeometry = new THREE.BufferGeometry();
const count = firstPositions.length / 3;
const colorsIn = file1;

const positions = new Float32Array(count * 3); // Multiply by 3 because each position is composed of 3 values (x, y, z)

const colors = new Float32Array(count * 3);

// for (let i = 0; i < count; i++) {
// const i3 = i * 3;

// // Convert from lat long to position on earth
// let positionOnGlobe = latLongToVector3(
// sstaRaw[i].y,
// sstaRaw[i].x,
// 1,
// 0.001
// );
// positions[i3 + 0] = positionOnGlobe.x;
// positions[i3 + 1] = positionOnGlobe.y;
// positions[i3 + 2] = positionOnGlobe.z;

// // Color based on anomaly
// // const coldColor = new THREE.Color("#053061");
// // const hotColor = new THREE.Color("#ff3d03");

// // const mixedColor = coldColor.clone();
// // mixedColor.lerp(hotColor, normalizedAnomaly[i]);
// // const d3Colors = d3.color(scaleAnomaly(normalizedAnomaly[i]));

// colors[i3 + 0] = colorsIn[i3 + 0];
// colors[i3 + 1] = colorsIn[i3 + 1];
// colors[i3 + 2] = colorsIn[i3 + 2];
// // mutable debug = positionOnGlobe;
// }

particlesGeometry.setAttribute(
"position",
new THREE.BufferAttribute(firstPositions, 3)
);
// console.log(firstPositions, positions);
particlesGeometry.setAttribute("color", new THREE.BufferAttribute(file1, 3));

// Material
const particlesMaterial = new THREE.PointsMaterial();
particlesMaterial.size = 0.008;
// particlesMaterial.blending = THREE.AdditiveBlending;
// particlesMaterial.depthWrite = false;
particlesMaterial.vertexColors = true;

// Points
const particles = new THREE.Points(particlesGeometry, particlesMaterial);
return particles;
// scene.add(particles);
}
Insert cell
// sstaRawToPlot = sstaRaw.slice(0, 200000)
Insert cell
// latLongToVector3(sstaRaw[0].x, sstaRaw[0].y, 1, sstaRaw[0].sst)
Insert cell
// convert the positions from a lat, lon to a position on a sphere.
function latLongToVector3(lat, lon, radius, heigth) {
var phi = (lat * Math.PI) / 180;
var theta = ((lon - 180) * Math.PI) / 180;

var x = -(radius + heigth) * Math.cos(phi) * Math.cos(theta);
var y = (radius + heigth) * Math.sin(phi);
var z = (radius + heigth) * Math.cos(phi) * Math.sin(theta);

return new THREE.Vector3(x, y, z);
}
Insert cell
height = width
Insert cell
// darkEarth = await FileAttachment("world.jpeg").image()
Insert cell
import {
file1,
file2,
file3,
file4,
file5,
file6,
file7,
file8,
file9,
file10,
firstPositions
} from "2e45491fcbc572a9"
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