Public
Edited
Dec 23
1 fork
5 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
customStyle.draw = (cells, resolution, cg) => {
//draw with HTML canvas in geo coordinates
cg.setCanvasTransform();

const min = d3.min(cells, (c) => +c.ind);
const max = d3.max(cells, (c) => +c.ind);

// population determines opacity, income determines color
for (let cell of cells) {
//do not draw cells with no data or no "ind"
if (!cell.ind) continue;

// example cell
// {
// "x": 3550000,
// "y": 2850000,
// "imputed": "363",
// "ind": "1593",
// "ind_snv": "31946271.4",
// "rIncome": 1671.1797133291484
// }

let t = (cell.ind - min) / (max - min);
t = gviz.sExpRev(t, -d);

movement(cg.ctx, resolution, cell, t);
}
};
//this redraw need to be executed to be ensure the map redraws when the style draw function changes
map.redraw();
}
Insert cell
movement = function (ctx, resolution, cell, t) {
let x = cell.x + resolution / 2;
let y = cell.y + resolution / 4;

// randomly move circles that are above a certain population threshold
// move circles with higher populations more violently (within a larger area) and lower populations less violently (within a smaller area)
if (t > 0.5) {
//highest intensity (no cell padding)
x = randomIntFromInterval(cell.x, cell.x + resolution);
y = randomIntFromInterval(cell.y, cell.y + resolution);
} else if (t > 0.35) {
//medium intensity
let padding = resolution / 2.7;
x = randomIntFromInterval(cell.x + padding, cell.x + resolution - padding);
y = randomIntFromInterval(cell.y + padding, cell.y + resolution - padding);
} else if (t > 0.25) {
//medium intensity
let padding = resolution / 2.5;
x = randomIntFromInterval(cell.x + padding, cell.x + resolution - padding);
y = randomIntFromInterval(cell.y + padding, cell.y + resolution - padding);
} else if (t > 0.15) {
//low intensity
let padding = resolution / 2.3;
x = randomIntFromInterval(cell.x + padding, cell.x + resolution - padding);
y = randomIntFromInterval(cell.y + padding, cell.y + resolution - padding);
} else if (t > 0) {
//tiny intensity
let padding = resolution / 2.1;
x = randomIntFromInterval(cell.x + padding, cell.x + resolution - padding);
y = randomIntFromInterval(cell.y + padding, cell.y + resolution - padding);
}

// drawing a black circle
ctx.beginPath();
//arc(x, y, radius, startAngle, endAngle)
ctx.arc(x, y, resolution / 5, 0, 2 * Math.PI);
ctx.fillStyle = "black";
ctx.fill();
}
Insert cell
{
setInterval(() => {
map.redraw();
}, 150);
}
Insert cell
Insert cell
Insert cell
Insert cell
function randomIntFromInterval(min, max) {
// min and max included
return Math.floor(Math.random() * (max - min + 1) + min);
}
Insert cell
Insert cell
Insert cell
Insert cell
gviz = require("gridviz@2.1.0")
Insert cell
Insert cell
map = new gviz.App(container)
.setGeoCenter({ x: 3750000, y: 2960000 })
.setZoomFactor(400)
.setLabelLayer({
url: "https://raw.githubusercontent.com/eurostat/euronym/main/pub/v2/UTF/50/EUR.csv",
preprocess: (lb) => {
//project from geo coordinates to ETRS89-LAEA
const p = proj([lb.lon, lb.lat]);
lb.x = p[0];
lb.y = p[1];
delete lb.lon;
delete lb.lat;
return lb.cc == "FR";
},
style: (lb, zf) => {
if (lb.rs < zf) return;
if (lb.r1 < zf) return "1em Arial";
return "1.5em Arial";
},
haloColor: () => "white"
})
Insert cell
proj = d3
.geoAzimuthalEqualArea()
.rotate([-10, -52])
.reflectX(false)
.reflectY(true)
.scale(6378137)
.translate([4321000, 3210000])
Insert cell
Insert cell
Insert cell
Insert cell
layer = {
const lay = new gviz.Layer(ds, [customStyle], {
pixNb: 11,
cellInfoHTML: (c) => "Inhabitants: " + Math.floor(c.ind)
});
map.layers = [lay];
return lay;
}
Insert cell
Insert cell
textures = require("textures")
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