Public
Edited
Apr 1, 2022
6 forks
61 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
colorScale = d3
.scaleSqrt()
.domain(d3.extent(csv, (d) => d.value))
.range([0, 4])
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
cells = {
let data_points = [];
for (let i = 0; i < csv.length; i++) {
let position = [csv[i].x,csv[i].y]; // EPSG:3035
let label = "Population: " + csv[i].value.toLocaleString(); // text shown in tooltip
let value = csv[i].value;
let point = { position, label, value };
data_points.push(point);
}
return data_points;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
scene = {
let scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
scene.add(points);
return scene;
}
Insert cell
Insert cell
Insert cell
camera = {
let aspect = viz_width / viz_height;
return new THREE.PerspectiveCamera(fov, aspect, near, far + 1);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
viz_height = {
if (
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
navigator.userAgent
)
) {
return 300;
} else {
return 800;
}
}
Insert cell
Insert cell
Insert cell
view = d3.select(renderer.domElement)
Insert cell
{
function setUpZoom() {
view.call(zoom);
let initial_scale = getScaleFromZ(far);
var initial_transform = d3.zoomIdentity.translate(viz_width/2, viz_height/2).scale(initial_scale);
zoom.transform(view, initial_transform);
camera.position.set(0, 0, far);
}
setUpZoom();
return setUpZoom;
}
Insert cell
zoom = {
let d3_zoom = d3.zoom()
.scaleExtent([getScaleFromZ(far), getScaleFromZ(near)])
.on('zoom', () => {
let d3_transform = d3.event.transform;
zoomHandler(d3_transform);
});
return d3_zoom;
}
Insert cell
function zoomHandler(d3_transform) {
let scale = d3_transform.k;
let x = -(d3_transform.x - viz_width/2) / scale;
let y = (d3_transform.y - viz_height/2) / scale;
let z = getZFromScale(scale);
camera.position.set(x, y, z);
}
Insert cell
function getScaleFromZ (camera_z_position) {
let half_fov = fov/2;
let half_fov_radians = toRadians(half_fov);
let half_fov_height = Math.tan(half_fov_radians) * camera_z_position;
let fov_height = half_fov_height * 2;
let scale = viz_height / fov_height; // Divide visualization height by height derived from field of view
return scale;
}
Insert cell
Insert cell
function getZFromScale(scale) {
let half_fov = fov/2;
let half_fov_radians = toRadians(half_fov);
let scale_height = viz_height / scale;
let camera_z_position = scale_height / (2 * Math.tan(half_fov_radians));
return camera_z_position;
}
Insert cell
Insert cell
Insert cell
function showTooltip(mouse_position, datum) {
let tooltip_width = 120;
let x_offset = -tooltip_width/2;
let y_offset = 30;
tooltip_state.display = "block";
tooltip_state.left = mouse_position[0] + x_offset;
tooltip_state.top = mouse_position[1] + y_offset;
tooltip_state.label = datum.label;
tooltip_state.color = datum.color;
}
Insert cell
Insert cell
function hideTooltip() {
tooltip_state.display = "none";
}
Insert cell
Insert cell
Insert cell
raycaster = new THREE.Raycaster()
Insert cell
Insert cell
{
raycaster.params.Points.threshold = 0.006;
return raycaster;
}
Insert cell
Insert cell
function sortIntersectsByDistanceToRay(intersects) {
return _.sortBy(intersects, "distanceToRay");
}
Insert cell
{
view.on("mousemove", () => {
let [mouseX, mouseY] = d3.mouse(view.node());
let mouse_position = [mouseX, mouseY];
checkIntersects(mouse_position);
});
return view;
}
Insert cell
function mouseToThree(mouseX, mouseY) {
return new THREE.Vector3(
mouseX / viz_width * 2 - 1,
-(mouseY / viz_height) * 2 + 1,
1
);
}
Insert cell
function checkIntersects(mouse_position) {
let mouse_vector = mouseToThree(...mouse_position);
raycaster.setFromCamera(mouse_vector, camera);
let intersects = raycaster.intersectObject(points);
if (intersects[0]) {
let sorted_intersects = sortIntersectsByDistanceToRay(intersects);
let intersect = sorted_intersects[0];
let index = intersect.index;
let datum = cells[index];
highlightPoint(datum);
showTooltip(mouse_position, datum);
} else {
removeHighlights();
hideTooltip();
}
}
Insert cell
hoverContainer = new THREE.Object3D()
Insert cell
{
scene.add(hoverContainer);
return scene;
}
Insert cell
function highlightPoint(datum) {
removeHighlights();
let geometry = new THREE.Geometry();
geometry.vertices.push(
new THREE.Vector3(
datum.position[0],
datum.position[1],
0
)
);
geometry.colors = [ new THREE.Color(datum.color) ];

let material = new THREE.PointsMaterial({
size: 0.02,
sizeAttenuation: true,
vertexColors: THREE.VertexColors
});
let point = new THREE.Points(geometry, material);
hoverContainer.add(point);
}
Insert cell
function removeHighlights() {
hoverContainer.remove(...hoverContainer.children);
}
Insert cell
{
view.on("mouseleave", () => {
removeHighlights()
})
return view;
}
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

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