Published
Edited
Nov 16, 2021
8 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
rivers_lines = {
const group = new THREE.Group();

xyzcords.map((d) => {
const points = []; // reset the points for each river
d.cord.map((point) => {
if (point[2] < 0) point[2] = 0; // filers the noData -0x7fff code does cause a few issues in eastern rivers.
points.push(new THREE.Vector3(point[0], point[2], point[1])); // x y z
});

group.add(
new THREE.Line(new THREE.BufferGeometry().setFromPoints(points), material)
);
});

return group;
}
Insert cell
rivers_lines_seg = {
const group = new THREE.Group();

// https://github.com/mrdoob/three.js/blob/master/examples/webgl_buffergeometry_lines_indexed.html
const geometries = [];
let positions = [],
indices = [],
next = 0;

// We can only address up to 256*256 (0xFFFF) indices.
// "Committing" means we stor the collected positions and indices
// in a new BufferGeometry. Then we empty out the positions and
// indices array so that we can start to count from 0 again.
const commit = () => {
if (!indices.length || !positions.length) return;
const geometry = new THREE.BufferGeometry();
geometry.setIndex(indices);
geometry.setAttribute(
"position",
new THREE.Float32BufferAttribute(positions, 3)
);
geometries.push(geometry);
indices = [];
positions = [];
next = 0;
};

for (let { cord: points } of xyzcords) {
// We got some empty coordinates.
points = points.filter(([x, y]) => !isNaN(x) && !isNaN(y));
//console.assert(points.length > 1, "Contains at least 2 points");
if (points.length < 2) continue;

// If the next array of coordinates would exceed the maximum
// index length, start a new BufferGeometry.
if (positions.length + points.length * 3 > 0xffff) commit();

for (let i = 0; i < points.length; i++) {
const [x, y, z] = points[i];
if (i > 0) indices.push(next - 1, next);
positions.push(x, Math.max(0, z || 0), -y);
next++;
}
}
commit();

for (const geometry of geometries) {
group.add(new THREE.LineSegments(geometry, material));
}

return group;
}
Insert cell
material = new THREE.LineBasicMaterial({
color: 0x9999f0,
linewidth: 1,
opacity: 1.0
})
Insert cell
Insert cell
// Scale to Three JS space
xyzcords = rivers_with_elevation.map(({ id, cord }) => ({
id,
cord: cord.map((c) => [scaleLAT(c[0]), scaleLONG(c[1]), scaleHeight(c[2])])
}))
Insert cell
scaleLAT = d3.scaleLinear().domain(rivers_minmax.lat).range([-150, 150]) // -150 150 could be larger
Insert cell
scaleLONG = d3.scaleLinear().domain(rivers_minmax.long).range([-150, 150])
Insert cell
scaleHeight = d3.scaleLinear().domain([0, 2500]).range([0, 30]) // could be larger. these are not to scale of the orgional data.
Insert cell
// Work out the min and max of the data. Height has issue due to the dataset having a noData -0x7fff value

rivers_minmax = {
const lat = [];
const long = [];
const height_ = [];
rivers_with_elevation.map((d) => {
let cord = d.cord.map((c) => {
lat.push(c[0]);
long.push(c[1]);
height_.push(c[2]);
});
});
return {
lat: d3.extent(lat),
long: d3.extent(long),
height: d3.extent(height_) // not used as I hard code to 0-2500
};
}
Insert cell
Insert cell
// area_all = {
// let [x, y, z] = [0, 1, 2];
// let all = [];
// areas[0].map((d) => {
// let cord = d.cord.map((c) => {
// return [c[x], c[y], c[z]];
// });
// all.push({
// id: d.id,
// name: d.name,
// feature: d.feature,
// cord
// });
// });
// return all;
// }
Insert cell
// areas.map((area) =>
// area.map((d) => {
// let z = 2;
// let id = d.id;
// // look up that ID in all
// let where = _.findIndex(area_all, ["id", id]);

// return d.cord.map((c, i) => {
// if (!(c[z] == null)) area_all[where].cord[i][z] = c[z]; // do I also need to check area_all to see if it needs updating
// });
// })
// )
Insert cell
areas = Promise.all([
FileAttachment("xyzCords__jl.json").json(),
FileAttachment("xyzCords__jl2.json").json(),
FileAttachment("xyzCords__jk.json").json(),
FileAttachment("xyzCords__jj.json").json(),
FileAttachment("xyzCords__ik.json").json(),
FileAttachment("xyzCords__ij.json").json(),
FileAttachment("xyzCords__hk.json").json()
])
Insert cell
rivers_with_elevation = FileAttachment("area_all (2).json").json()
Insert cell
FileAttachment("area_all (2).json").json()
Insert cell
Insert cell
Insert cell
Insert cell
from = [0, 80, 180]
Insert cell
lookAt = new THREE.Vector3(120, 0, -80)
Insert cell
Insert cell
renderer = {
let rend = new THREE.WebGLRenderer({ antialias: true });

rend.setSize(width, height);
rend.setPixelRatio(devicePixelRatio);
return rend;
}
Insert cell
camera = {
const camera = new THREE.PerspectiveCamera(
80, // fov, could be 75
width / height, // aspect
0.1, // near
5000 // far
);
camera.position.set(...from);
camera.lookAt(lookAt);

return camera;
}
Insert cell
scene = {
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x050532); // dark background

scene.add(obj_source); // put object into the scene

scene.add(ambient_light, point_light);

return scene;
}
Insert cell
ambient_light = {
return new THREE.AmbientLight(0xffffff, 0.5);
}
Insert cell
point_light ={
let pl = new THREE.PointLight(0x909090, 0.5);
pl.position.set( 500, 500, 500 );
return pl
}
Insert cell
height = width * 0.75
Insert cell
// More recent versions change the way examples like OrbitControls are packaged.
VERSION = "0.127.0"
Insert cell
THREE = import(`https://unpkg.com/three@${VERSION}/build/three.module.js`)
Insert cell
OrbitControls = importExample("controls/OrbitControls", undefined, VERSION)
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