Published
Edited
Mar 26, 2020
4 stars
Also listed in…
X3Dom
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function* make_choropleth3D() {
let width = 900;
let height = 400;
let container = d3
.create('div')
.style('width', width.toString() + 'px')
.style('height', height.toString() + 'px');

let scene = container
.append('x3d')
.attr('width', width.toString() + 'px')
.attr('height', height.toString() + 'px')
.append('scene');
scene
.append("viewpoint")
.attr("centerOfRotation", "996 64700 0")
.attr("position", "-3065.65419 56723.69512 5351.94013")
.attr("orientation", "0.97625 -0.13632 -0.16837 1.00157");

let T = scene.append('transform');
['VD', 'VP', 'HD', 'HP'].forEach(function(t) {
let G = T.append('group').attr('id', t);
triangulated_counties.forEach(function(c) {
G.append(() => county3D(c, t));
});
});
scene
.select('#VP')
.selectAll('material')
.attr('transparency', 0);

T.attr('scale', '1,1,0.01');
Promises.delay(500).then(function() {
T.transition()
.duration(1000)
.attr('scale', '1,1,1');
});

yield container.node();
x3dom.reload();
}
Insert cell
// Determine height based type
// VD means volume proprtional to density
// VP means volume proportional to population
// HD means height proporitonal to density
// HP means height proporitonal to population

function height(county, type) {
let pop = county.properties.pop;
let A = county.properties.ALAND + county.properties.AWATER;
if (type == 'VD') {
return (3 * 10 ** 15 * pop) / (A * A);
} else if (type == 'VP' || type == 'HD') {
return (3 * 10 ** 6 * pop) / A;
} else if (type == 'HP') {
return (2 * pop) / 10 ** 3;
} else {
return 0;
}
}
Insert cell
function county3D(county, type) {
let pt_string_top = '';
let pt_string_bot = '';
let h = height(county, type);
county.pts.map(function(pt) {
pt_string_top = `${pt_string_top}${pt[0]} ${pt[1]} ${h}, `;
pt_string_bot = `${pt_string_bot}${pt[0]} ${pt[1]} 0, `;
});
let pt_string = pt_string_top + pt_string_bot;
let top_coord_string = '';
county.triangles.map(function(t) {
top_coord_string = `${top_coord_string}${t[0]} ${t[1]} ${t[2]} -1 `;
});

let n = county.pts.length;
let side_coord_string = '';
county.segments.map(function(s) {
side_coord_string = `${side_coord_string}${s[0]} ${s[1]} ${s[1] +
n} ${s[0] + n} -1 `;
});
let coord_string = `${top_coord_string} ${side_coord_string}`;

let color;
if (type == 'HD' || type == 'VD') {
let density =
county.properties.pop /
(county.properties.ALAND + county.properties.AWATER);
color = d3.interpolateBlues((density / max_density) ** 0.2);
} else if (type == 'HP' || type == 'VP') {
color = d3.interpolateBlues((county.properties.pop / max_pop) ** 0.2);
}
let g = d3.create('group');
let shape = g.append('shape');
let appearance = shape.append('appearance');
appearance
.append('material')
.attr('transparency', 1)
.attr('diffuseColor', color);
let ifs = shape
.append('IndexedFaceSet')
.attr('solid', 'false')
.attr('coordIndex', coord_string);
ifs.append('coordinate').attr('point', pt_string);

return g.node();
}
Insert cell
max_density = d3.max(
triangulated_counties.map(
c => c.properties.pop / (c.properties.ALAND + c.properties.AWATER)
)
)
Insert cell
max_pop = d3.max(triangulated_counties.map(c => c.properties.pop))
Insert cell
triangulated_counties = FileAttachment('nc_triangulated3.json').json()
Insert cell
d3 = require('d3@5')
Insert cell
x3dom = require('x3dom').catch(() => window['x3dom'])
Insert cell
style = html`<style>
canvas {
outline: none;
}
</style>`
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