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

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