Published
Edited
Feb 18, 2021
17 stars
Insert cell
Insert cell
Insert cell
n = map()
Insert cell
Insert cell
D = 1 + altitude
Insert cell
projection = d3
.geoSatellite()
.distance(D)
.clipAngle(Math.acos(1 / D) * degrees)
.rotate([80, -50])
Insert cell
import { zoom } from "@d3/versor-zooming"
Insert cell
d3 = require("d3@6", "d3-geo-projection@2")
Insert cell
import { fullscreen } from "@fil/fullscreen"
Insert cell
fullscreen(n)
Insert cell
Insert cell
function map() {
const renderer = new THREE.WebGLRenderer();
const size = Math.max(width, height);
renderer.setSize(width, height);
renderer.setPixelRatio(devicePixelRatio);

projection.fitExtent([[10, 10], [width - 10, height - 10]], {
type: "Sphere"
});

const scene = new THREE.Scene();
const mesh = new THREE.Mesh(
new THREE.SphereBufferGeometry(1, 32, 32),
new THREE.MeshBasicMaterial({ map: image })
);
scene.add(mesh);

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

const context = DOM.context2d(width, height),
canvas = context.canvas,
path = d3.geoPath(projection, context);

function render() {
// render THREE
const fov = 2 * Math.atan(height / projection.scale() / (D - 1) / 2),
camera = new THREE.PerspectiveCamera(
fov * (180 / Math.PI),
width / height,
0.01,
1000
);
camera.position.x = D;
camera.lookAt(new THREE.Vector3(0, 0, 0));

const a = projection.rotate();
mesh.rotation.x = (a[2] || 0) * radians;
mesh.rotation.y = a[0] * radians;
mesh.rotation.z = a[1] * radians;
mesh.rotation.order = "XZY";

renderer.render(scene, camera);

const t = projection.translate();
renderer.domElement.style.left = `${t[0] - width / 2}px`;
renderer.domElement.style.top = `${t[1] - height / 2}px`;

// render canvas
context.clearRect(0, 0, width, width);
context.beginPath();
context.strokeStyle = "lightblue";
path({ type: "Sphere" });
path(land);
context.stroke();
}

const div = d3.create("div");

div.node().appendChild(renderer.domElement);
div.node().appendChild(canvas);

div.node().style.height = `${height}px`;
canvas.style.position = "absolute";
canvas.style.left = 0;
renderer.domElement.style.position = "absolute";
div.node().style.overflow = "hidden";

return div
.call(
zoom(projection)
.on("zoom.render", render)
.on("end.render", render)
)
.call(render)
.node();
}
Insert cell
image = loadTexture(`https://solartextures.b-cdn.net/2k_earth_clouds.jpg`)
Insert cell
loadTexture = {
const loader = new THREE.TextureLoader();
return url => new Promise(resolve => loader.load(url, resolve));
}
Insert cell
THREE = require("three@0.108.0/build/three.min.js")
Insert cell
Insert cell
land = fetch("https://unpkg.com/visionscarto-world-atlas@0.0.6/world/50m_land.geojson").then(d => d.json())
Insert cell
import { slider } from "@jashkenas/inputs"
Insert cell
import { degrees, radians } from "@fil/math"
Insert cell
height = Math.max(500, width * 0.7) | 0
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