Published
Edited
Aug 5, 2020
Insert cell
Insert cell
Insert cell
scene = {
const scene = new THREE.Scene();
scene.background = new THREE.Color("#fff");

// add cube outline
const box = new THREE.BoxGeometry(c, c, c);
var geo = new THREE.EdgesGeometry(box);
var mat = new THREE.LineBasicMaterial({ color: 0xdddddd, linewidth: 1 });
var wireframe = new THREE.LineSegments(geo, mat);
scene.add(wireframe);

// add map
var texture = new THREE.TextureLoader().load(tileURL);
var material = new THREE.MeshBasicMaterial({ map: texture });
var geometry = new THREE.PlaneGeometry(c, c);
var mesh = new THREE.Mesh(geometry, material);
mesh.position.x = 0;
mesh.position.y = -c / 2;
mesh.position.z = 0;
mesh.rotation.x = -Math.PI / 2;
mesh.rotation.z = Math.PI / 2;
scene.add(mesh);

// add plot
var coord = data.map(d => project3D([d.lon, d.lat], d.date));

coord.forEach(function(d) {
var geo = new THREE.SphereGeometry(0.07, 4, 4);
var mat = new THREE.MeshBasicMaterial({ color: "steelblue" });
var cube = new THREE.Mesh(geo, mat);

cube.position.x = d.x;
cube.position.y = d.y;
cube.position.z = d.z;

scene.add(cube);
});

// add line through data points

// for an interpolated curve
// var curve = new THREE.CatmullRomCurve3(coord);
// var points = curve.getPoints(200);

// line directly through points
var geometry = new THREE.BufferGeometry().setFromPoints(coord);
var material = new THREE.LineBasicMaterial({ color: "#777" });

var curve = new THREE.Line(geometry, material);
scene.add(curve);

return scene;
}
Insert cell
Insert cell
function project3D(coord, t = minDate) {
const proj = projection(coord);
const time = timeScale(t);
return new THREE.Vector3(proj[1] - c / 2, time, c / 2 - proj[0]);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
box = ({
l: +d3.min(data, d => d.lon),
r: +d3.max(data, d => d.lon),
t: +d3.max(data, d => d.lat),
b: +d3.min(data, d => d.lat)
})
Insert cell
Insert cell
Insert cell
tiles = {
return {
zoom: zoom,
top_tile: ytile(box.t, zoom),
left_tile: xtile(box.l, zoom),
bottom_tile: ytile(box.b, zoom),
right_tile: xtile(box.r, zoom)
};
}
Insert cell
Insert cell
xtile = function(lon_deg, zoom) {
var n = Math.pow(2, zoom);
var tile = n * ((lon_deg + 180) / 360);
return Math.floor(tile);
}
Insert cell
ytile = function(lat_deg, zoom) {
var n = Math.pow(2, zoom);
var lat_rad = (lat_deg * Math.PI) / 180;
var tile =
(n * (1 - Math.log(Math.tan(lat_rad) + 1 / Math.cos(lat_rad)) / Math.PI)) /
2;
return Math.floor(tile);
}
Insert cell
Insert cell
tile_bounds = {
var n = Math.pow(2, tiles.zoom);
// top left
var lon_deg = (tiles.left_tile / n) * 360 - 180;
var lat_rad = Math.atan(Math.sinh(Math.PI * (1 - (2 * tiles.top_tile) / n)));
var lat_deg = (lat_rad * 180.0) / Math.PI;
// bottom right
var lon_deg2 = ((tiles.left_tile + 1) / n) * 360 - 180;
var lat_rad2 = Math.atan(
Math.sinh(Math.PI * (1 - (2 * (tiles.top_tile + 1)) / n))
);
var lat_deg2 = (lat_rad2 * 180.0) / Math.PI;

return { t: lat_deg, l: lon_deg, r: lon_deg2, b: lat_deg2 };
}
Insert cell
md`From these coordinates, we define a bounding box. This can be used to scale a D3 projection function automatically.`
Insert cell
bbox = ({
type: "Polygon",
coordinates: [
[
[tile_bounds.l, tile_bounds.t],
[tile_bounds.r, tile_bounds.t],
[tile_bounds.r, tile_bounds.b],
[tile_bounds.l, tile_bounds.b],
[tile_bounds.l, tile_bounds.t]
]
]
})
Insert cell
Insert cell
projection = d3.geoMercator().fitSize([c, c], bbox)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
parseDate = d3.timeParse("%Y/%m/%d %H:%M:%S%Z")
Insert cell
data = d3.csv('https://gist.githubusercontent.com/sarah37/e484b2266cbcd539fe0bbbaf8156405f/raw/235ada5ca8d5e8e459d98f8ad69550786a1e5591/test.csv', function(d) {return {date: parseDate(d.date), lat: d.lat, lon: d.lon}})
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