Published unlisted
Edited
May 13, 2020
Insert cell
Insert cell
chart = {
aframe; // wait for the module to load before building (???)
if (this) return this; // avoids a crash when going fullscreen (??)
const height = window.outerHeight - 50;
const chart = html`<div style="min-width:100%;height:${height}px; max-height:100vw;">${scene}</div>`;
return chart;
}
Insert cell
add_terrain = {
const a = d3
.select(chart)
.select("#terrain")
.html("");

a.selectAll("a-entity")
.data(mesh.coordinates)
.enter()
.append("a-entity")
//.attr("radius", 0.001)
.attr(
"meshline",
(l, i) =>
`lineWidth: 0.05; path: ${l
.map(xyz)
.join(", ")}; color:white; lineWidthStyler: Math.floor(p*${(13 + i) **
2})%8===0;`
);

yield "added terrain";
}
Insert cell
add_sims = {
const a = d3
.select(chart)
.select("#lines")
.html("");

const lines = a
.selectAll("a-entity")
.data(bus.features)
.enter()
.append("a-entity")
//.attr("radius", 0.001)
.attr("meshline", "") // no material for meshlines
/*.attr(
"material",
l => `color: rgb(${l.properties.couleur.replace(/ /g, ",")});`
)*/ .nodes();

//await Promises.delay(1000);

if (true)
for (var i = 0; i < lines.length; i++) {
d3.select(lines[i]).attr(
"meshline",
l =>
`lineWidth: 0.4; path: ${l.geometry.coordinates
.map(d => `${xyz(d)}`)
.join(", ")}; color: rgb(${l.properties.couleur.replace(
/ /g,
","
)});`
);
yield `${1 + i}/${lines.length}`;
}

a.select("a-text").remove();

const b = d3.select(chart).select("#dots");

const boxes = b
.html("")
.selectAll("a-box")
.data(bus.features)
.enter()
.append("a-box")
.attr("width", 0.01)
.attr("height", 0.01)
.attr("depth", 0.01)
.attr(
"material",
l => `color: rgb(${l.properties.couleur.replace(/ /g, ",")});`
);

const camera = d3.select(chart).select("#camera");

do {
const t = Date.now() / 30000,
s = sims(Date.now());
boxes.attr("position", (d, i) => `${xyz(s[i].coordinates, i)}`);
// recommended:
if (false)
boxes.each((d, i, e) => {
const A = xyz(s[i].coordinates, i)
.split(" ")
.map(d => +d),
pos = { x: A[0], y: A[1], z: A[2] };
e[i].setAttribute("position", pos);
});

// camera.attr("position", `${3 * Math.sin(t)} 1 ${3 + 3 * Math.cos(t)}`);
camera.each((d, i, e) => {
e[i].setAttribute("position", {
x: 3 * Math.sin(t),
y: 1,
z: 3 + 3 * Math.cos(t)
});
});

yield html`<tt>sims updated at ${Date.now()}`;
} while (true);
}
Insert cell
scene = `<a-scene embedded>

<a-entity id="model" position="0 0 0" rotation="-90 0 0">

<!-- space-time color tubes -->

<a-entity id="terrain"></a-entity>
<a-entity id="lines"><a-text value="Loading…"></a-text></a-entity>
<a-entity id="dots"></a-entity>

</a-entity>



<!-- sky & camera controls -->

<a-sky src="https://img-zkf1w4xjg.now.sh/sky.png" color="#222"></a-sky>

<a-entity id=camera camera look-controls orbit-controls="target: 0 0.5 0; minDistance: 0.3; maxDistance: 10; initialPosition: 0 0.5 3"></a-entity>

</a-scene>`
Insert cell
height = 500
Insert cell
projection = d3.geoMercator().fitExtent([[-3, -3], [3, 3]], bus)
Insert cell
sims(0)
Insert cell
sims = {
const agents = bus.features
//.filter((_, i) => i === 0)
.map(d => {
const line = d.geometry.coordinates;
const domain = [];
var c0 = line[0],
l = 0;
line.forEach(c => {
domain.push((l += d3.geoDistance(c, c0)));
c0 = c;
});
const lon = d3
.scaleLinear()
.domain(domain)
.range(line.map(d => d[0])),
lat = d3
.scaleLinear()
.domain(domain)
.range(line.map(d => d[1])),
alt = d3
.scaleLinear()
.domain(domain)
.range(line.map(d => d[2]));

return {
line,
domain,
max: l,
pos: l * Math.random(),
speed: l / 10000,
dir: 1,
lon,
lat,
alt,
color: `rgb(${d.properties.couleur})`
};
});

return function(t) {
return agents.map(a => {
a.pos = (t * a.speed) % (2 * a.max);
if (a.pos > a.max) a.pos = a.pos - a.max; /* a.pos = 2 * a.max - a.pos;*/
return {
coordinates: [a.lon(a.pos), a.lat(a.pos), a.alt(a.pos)],
color: a.color
};
});
};
}
Insert cell
// import {sims, d3} with {bus} from "72aee60478e18be8"
Insert cell
bus = d3.json(
"https://gist.githubusercontent.com/Fil/5f02b4379cb740e4970e4ba2cd3c9e94/raw/22f93245c560877d24d852d164d60dee7d4d3f02/bus_altitude.json"
)
Insert cell
function altitude(point) {
return point[2] || 0;
}
Insert cell
// saved from https://observablehq.com/d/3eb05d54a7f00cc9
mesh = d3.json(
"https://gist.githubusercontent.com/Fil/2349af28f2185f87ce0b36ddd3d8f432/raw/7cce191cd844703697cb9e7f43306f21d35a9efb/mesh3.json"
)
Insert cell
xyz = {
const z = d3
.scaleLinear()
.domain([100, 300])
.range([0, 0.4]),
xy = d => {
d = projection(d);
d[1] = -d[1];
return d;
};
return function(point, line) {
return [...xy(point), z(altitude(point))].join(" ");
};
}
Insert cell
aframe = {
await require("aframe@1.0.4");
await require("aframe-extras@5.1.0/dist/aframe-extras.js").catch(() => {}); // works on Firefox

// https://github.com/supermedium/superframe/tree/master/components/orbit-controls
await require("aframe-orbit-controls@1.2.0/dist/aframe-orbit-controls.min.js").catch(
() => {}
);

await require("aframe-meshline-component@0.3.1/dist/aframe-meshline-component.js").catch(
() => {}
);

// const THREE = window.THREE;

return window.AFRAME;
}
Insert cell
d3 = require("d3@5")
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