Public
Edited
Aug 28, 2024
1 fork
Importers
17 stars
Also listed in…
Maps
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function* create_globe() {
let this_width = width < 600 ? width : 600;
let height = this_width; // d3.min([this_width, window.screen.height]);
// let this_width = 600;
// let height = 600;
let container = d3
.create("div")
.style("width", this_width.toString() + "px")
.style("height", height.toString() + "px");
// .style('border', 'solid 1px black');

let scene = container
.append("x3d")
.attr("width", this_width.toString() + "px")
.attr("height", height.toString() + "px")
.append("scene");
let r = (2 * Math.PI) / 3;
r = r.toString();
let d = 1 / Math.sqrt(3);
d = d.toString();
let ddd = d + " " + d + " " + d;
scene
.append("viewpoint")
.attr("position", "4 0 0")
.attr("orientation", ddd + " " + r);
scene
.append("directionalLight")
.attr("direction", "-0.5 1 0")
.attr("on", "TRUE")
.attr("intensity", "0.75");
// .attr('shadowIntensity', '0.2')
// .attr('shadowOffset', 100);
// Unfortunatley, the shadow produces a fairly poor effect.

let tilt = scene
.append("transform")
.attr("id", "tilt")
.attr("rotation", "1 0 0 0.2");
let rotation = tilt.append("transform").attr("id", "rotation");
let sphere_shape = rotation.append("shape");
sphere_shape
.append("appearance")
.append("material")
.attr("diffuseColor", "0.63 0.73 0.85")
.attr("shininess", "0.1");
sphere_shape
.append("sphere")
.attr("subdivision", "256,256")
.attr("radius", "0.999");

for (let cnt = 0; cnt < land.pts.length; cnt++) {
let pt_string = "";
let color_string = "";
land.pts[cnt].forEach(function (lnglat) {
let lnglat_sphere_string = lnglat_to_globe(lnglat, { r: 1.6 });
pt_string = pt_string + lnglat_sphere_string + ", ";
color_string = color_string + shade(lnglat[2] / M) + ", ";
});
let index_string = "";
land.triangles[cnt].forEach(function (ijk) {
let i = ijk[0];
let j = ijk[1];
let k = ijk[2];
index_string = index_string + `${i} ${j} ${k} -1 `;
});

let land_shape = rotation.append("shape");
let indexedFaceSet = land_shape
.append("indexedFaceset")
.attr("coordIndex", index_string)
.attr("creaseAngle", "0");
indexedFaceSet.append("coordinate").attr("point", pt_string);
indexedFaceSet.append("Color").attr("color", color_string);

let mesh = rotation.append("shape").attr("class", "mesh");
mesh.append("appearance").append("material").attr("transparency", 1);
mesh
.append("IndexedLineSet")
.attr("coordIndex", index_string)
.append("Coordinate")
.attr("point", pt_string);
}

let pt_string = "";
let index_string = "";
d = Math.PI / 12;
let dd = Math.PI / 144;
let cnt = 0;
for (let lng = -Math.PI; lng <= Math.PI; lng = lng + d) {
for (let lat = -Math.PI / 2; lat <= Math.PI / 2; lat = lat + dd) {
pt_string = pt_string + lnglat_to_globe([lng, lat]) + ", ";
index_string = `${index_string} ${cnt++}`;
}
index_string = index_string + " -1";
}
for (let lat = -Math.PI / 2; lat <= Math.PI / 2; lat = lat + d) {
for (let lng = -Math.PI; lng <= Math.PI; lng = lng + dd) {
pt_string = pt_string + lnglat_to_globe([lng, lat]) + ", ";
index_string = `${index_string} ${cnt++}`;
}
index_string = index_string + " -1";
}
let graticule = rotation.append("shape").attr("class", "graticule");
graticule.append("appearance").append("material").attr("transparency", 0);
graticule
.append("IndexedLineSet")
.attr("coordIndex", index_string)
.append("Coordinate")
.attr("point", pt_string);

yield container.node();
x3dom.reload();
}
Insert cell
function lnglat_to_globe(lnglat, opts = {}) {
let { r = 1, mode = "radians" } = opts;
let lat = lnglat[1];
let lng = lnglat[0];
let R;

// Third component represents elevation, if it exists
if (lnglat.length > 2) {
R = 1.001 + 0.000005 * lnglat[2];
} else {
R = r;
}
if (mode == "degrees") {
lat = (lat * Math.PI) / 180;
lng = (lng * Math.PI) / 180;
}

// Convert latitude to spherical phi
let phi = Math.PI / 2 - lat;
let theta = lng;
let x = R * Math.sin(phi) * Math.cos(theta);
if (Math.abs(x) < 0.000001) {
x = 0;
}
let y = R * Math.sin(phi) * Math.sin(theta);
if (Math.abs(y) < 0.000001) {
y = 0;
}
let z = R * Math.cos(phi);
if (Math.abs(z) < 0.000001) {
z = 0;
}
return x + " " + y + " " + z;
}
Insert cell
lnglat_to_globe([-82, 35])
Insert cell
function shade(elevation) {
let result;
if (elevation < 0.5) {
result = d3.interpolateRgb('rgb(0, 105, 62)', "rgb(210, 180, 140)")(
2 * elevation
);
} else {
result = d3.interpolateRgb("rgb(210, 180, 140)", 'white')(
2 * elevation - 1
);
}
return String(
result
.split('(')[1]
.split(')')[0]
.split(',')
.map(x => String(parseFloat(x) / 255))
).replace(/,/g, ' ');
}
Insert cell
// The maximum of all elevations
M = d3.max(
land.pts.map(pt_list => d3.max(pt_list.map(p => p[p.length - 1])))
)
Insert cell
rotate = d3
.select(globe)
.select('#rotation')
.attr('rotation', `0 0 1 ${rotation}`)
Insert cell
show_grat = {
let transparency;
if (show_graticule) {
transparency = "0";
} else {
transparency = "1";
}
d3.select(globe)
.selectAll(".graticule")
.select("material")
.attr("transparency", transparency);
}
Insert cell
show_tri = {
let transparency;
if (show_triangulation) {
transparency = '0';
} else {
transparency = '1';
}
d3.select(globe)
.selectAll('.mesh')
.select('material')
.attr('transparency', transparency);
}
Insert cell
rotation_values = d3.range(0, 2 * Math.PI, Math.PI / 500)
Insert cell
// This file defines 90 triangulated land masses.
// The array in pts[k] defines the points in the kth land mass as
// [longitude, latitude, elevation] triples.
// The kth element of the triangles array is a list of triples and
// each triple contains indices of pts[k] defining a triangle in
// that land mass. Of course, that's easy to translate to either an
// indexedLineSet or an indexedFaceSet

land = FileAttachment("land.json").json()
Insert cell
// d3 = require('d3-selection@2', 'd3-array@2', 'd3-interpolate@2')
Insert cell
x3dom = require('x3dom').catch(() => window['x3dom'])
Insert cell
import { checkbox } from "@jashkenas/inputs"
Insert cell
import { Scrubber } from "@mbostock/scrubber"
Insert cell
// Hide the canvas outline and some Scrubber features
style = html`<style style="display: none">
canvas {
outline: none;
}
output {
display: none;
}
</style>`
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