Public
Edited
Aug 28, 2024
1 fork
Importers
17 stars
Also listed in…
X3Dom
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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more