Public
Edited
Jan 9
1 fork
37 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
sizelegend = legendCircle()
.tickValues([4, 4.5, 5, 6])
.tickFormat((d, i, e) => {
return `${d}`;
})
.scale(sizeScale)
Insert cell
Insert cell
camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 10000)
Insert cell
renderer = new THREE.WebGLRenderer();
Insert cell
scene = new THREE.Scene()
Insert cell
borders = new THREE.Group()
Insert cell
plates = new THREE.Group()
Insert cell
points = new THREE.Points(pointsGeometry, pointsMaterial)
Insert cell
lineMaterial = new THREE.LineBasicMaterial({
linewidth: 0.1,
color: lineColor
})
Insert cell
pointsMaterial = new THREE.ShaderMaterial({
uniforms: {
multiplier: {
value: 1000 // size multiplier
}
},
fragmentShader: pointFragmentShader,
vertexShader: pointVertexShader,
vertexColors: true
})
Insert cell
controls = new THREE.OrbitControls(camera, renderer.domElement)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
colorScale = d3.scaleSequential(magDomain, d3.interpolateYlOrRd)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function init() {
camera.position.z = 3000;

// add mouse event
renderer.domElement.addEventListener("mousemove", onMouseMove, false);
//renderer.domElement.addEventListener("mousedown", onMouseDown, false);
//renderer.domElement.addEventListener("mouseup", onMouseUp, false);

//add points
let sizes = [];
let colors = [];
let positions = [];
for (var i = 0; i < earthquakes.length; i++) {
let e = earthquakes[i];

let lat = e.latitude;
let lon = e.longitude;

let latRad = lat * (Math.PI / 180);
let lonRad = -lon * (Math.PI / 180);

let x = Math.cos(latRad) * Math.cos(lonRad) * radius;
let y = Math.sin(latRad) * radius;
let z = Math.cos(latRad) * Math.sin(lonRad) * (radius - e.depth);
let size = sizeScale(e.mag);
let color = new THREE.Color(colorScale(e.mag));

if (isNaN(x) || isNaN(y) || isNaN(z)) {
console.log("invalid coords", e);
} else {
positions.push(x, y, z);
sizes.push(size);
colors.push(color.r, color.g, color.b);
}
}

//set buffer geometry attributes
pointsGeometry.setAttribute(
"position",
new THREE.Float32BufferAttribute(positions, 3)
);
pointsGeometry.setAttribute(
"color",
new THREE.Float32BufferAttribute(colors, 3)
);
//Variable point size will affect raycasting: https://github.com/mrdoob/three.js/issues/5105
pointsGeometry.setAttribute(
"size",
new THREE.Float32BufferAttribute(sizes, 1)
);

scene.add(points);
}
Insert cell
//add countries
{
if (showBorders) {
addGeoJsonFeaturesToScene(countryBoundaries.features, borders);
} else {
emptyGroup(borders);
}
}
Insert cell
{
// add tectonic plates
if (showPlates) {
addGeoJsonFeaturesToScene(tectonicPlates.features, plates);
} else {
emptyGroup(plates)
}
}
Insert cell
function emptyGroup(group) {
while (group.children.length > 0) {
group.remove(group.children[0]);
}
}
Insert cell
function addGeoJsonFeaturesToScene(features, group) {
// GEOJSON to ThreeJS
console.log("add geojson features");
for (let i = 0; i < features.length; i++) {
let feature = features[i];
let coords = [];

// iterate coordinates
for (let c = 0; c < feature.geometry.coordinates.length; c++) {
// polygons
if (feature.geometry.type == "Polygon") {
let coords = [];
for (let s = 0; s < feature.geometry.coordinates[c].length; s++) {
const x = feature.geometry.coordinates[c][s][0];
const y = feature.geometry.coordinates[c][s][1];
if (isNaN(x) || isNaN(y)) console.log("invalid coords");
coords.push({ x, y });
}

if (coords.length > 0 && coords[0] && coords[1]) {
console.log("group.add");
group.add(createLineFromCoords(coords));
}
} else if (feature.geometry.type == "MultiPolygon") {
//multipolygons

for (let s = 0; s < feature.geometry.coordinates[c].length; s++) {
//each polygon in multipolygon:
let coords = [];
for (let m = 0; m < feature.geometry.coordinates[c][s].length; m++) {
const x = feature.geometry.coordinates[c][s][m][0];
const y = feature.geometry.coordinates[c][s][m][1];
if (isNaN(x) || isNaN(y)) console.log("invalid coords");
coords.push({ x, y });
}
}
if (coords.length > 0) {
group.add(createLineFromCoords(coords));
}
} else if (feature.geometry.type == "LineString") {
//linestrings
const x = feature.geometry.coordinates[c][0];
const y = feature.geometry.coordinates[c][1];
if (isNaN(x) || isNaN(y)) console.log("invalid coords");
coords.push({ x, y });
if (coords.length > 0) {
group.add(createLineFromCoords(coords));
}
} else if (feature.geometry.type == "MultiLineString") {
//multilinestrings
feature.geometry.coordinates.forEach((line) => {
//each line in multiline:
let coords = [];
line.forEach((coord) => {
const x = coord[0];
const y = coord[1];
if (isNaN(x) || isNaN(y)) return;
coords.push({ x, y });
});
if (coords.length > 0) {
group.add(createLineFromCoords(coords));
}
});
}
} // for each feature.geometry.coordinates
} // for each feature
scene.add(group);
}
Insert cell
function createLineFromCoords(coords) {
let lineGeom = new THREE.BufferGeometry();
let positions = [];
for (var i = 0; i < coords.length; i++) {
let lat = coords[i].y;
let lon = coords[i].x;
let latRad = lat * (Math.PI / 180);
let lonRad = -lon * (Math.PI / 180);
let x = Math.cos(latRad) * Math.cos(lonRad) * radius;
let y = Math.sin(latRad) * radius;
let z = Math.cos(latRad) * Math.sin(lonRad) * radius;
if (isNaN(x) || isNaN(y) || isNaN(z)) {
console.log("invalid coordinates", coords);
} else {
positions.push(x, y, z);
}

//lineGeom.vertices.push(new THREE.Vector3(x, y, z));
}
console.log("positions added");
lineGeom.setAttribute(
"position",
new THREE.Float32BufferAttribute(positions, 3)
);

return new THREE.Line(lineGeom, lineMaterial);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
pointer = new THREE.Vector2()
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
init()
Insert cell
Insert cell
Insert cell
earthquakes = {
const earthquakesB = await d3.csv(
`https://earthquake.usgs.gov/fdsnws/event/1/query?format=csv&starttime=${year}-06-01&endtime=${year}-12-31&minmagnitude=4&orderby=time`
);
const earthquakesA = await d3.csv(
`https://earthquake.usgs.gov/fdsnws/event/1/query?format=csv&starttime=${year}-01-01&endtime=${year}-06-01&minmagnitude=4&orderby=time`
);

return earthquakesA.concat(earthquakesB);
}
Insert cell
tectonicPlates = await d3.json(
"https://raw.githubusercontent.com/fraxen/tectonicplates/master/GeoJSON/PB2002_boundaries.json"
)
Insert cell
countryBoundaries = {
const geom = await d3.json(
"https://gisco-services.ec.europa.eu/distribution/v2/countries/geojson/CNTR_BN_20M_2020_4326.geojson"
);

return geom;
}
Insert cell
Insert cell
import { legendCircle } from "@harrystevens/circle-legend"
Insert cell
Insert cell
Insert cell
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