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

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