Published
Edited
Dec 31, 2019
Fork of An X3D Globe
1 fork
2 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function* create_globe() {
let this_width = width;
let height = 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');

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('navigationinfo')
.attr('explorationMode','-pan')
.attr('headlight', 'false');
scene
.append('viewpoint')
.attr('position', 4*6370000 + " 0 0")
.attr('orientation', ddd + " " + r);
scene
.append('directionalLight')
.attr('direction', '-1 1 0')
.attr('on', 'TRUE')
.attr('ambientIntensity','0.5')
.attr('intensity', '1.0');
// .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.4');
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', 6350000 + vE*100);

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);
pt_string += [lnglat[1]*180/Math.PI, lnglat[0]*180/Math.PI, lnglat[2] * vE].join(" ")+", ";//lnglat_sphere_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');
land_shape
.append('appearance')
.append('material'); // material required for shading
let indexedFaceSet = land_shape
.append('indexedFaceset')
.attr('coordIndex', index_string)
.attr('creaseAngle', '3.14');
indexedFaceSet.append('geocoordinate').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('geocoordinate')
.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;
if (lnglat.length > 2) {
R = 1 + 0.000005 * lnglat[2];
} else {
R = r;
}
if (mode == 'degrees') {
lat = (lat * Math.PI) / 180;
lng = (lng * Math.PI) / 180;
}
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
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
d3
.select(globe)
.select('#rotation')
.attr('rotation', `0 0 1 ${rotation}`)
Insert cell
{
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@5')
Insert cell
x3dom = require('https://x3dom.org/download/dev/x3dom-full.js').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
html`<style>
canvas {
outline: none;
}
input[name=i] {
display: 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