Public
Edited
Jan 17, 2024
4 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
customStyle = new gviz.Style()
Insert cell
Insert cell
{
customStyle.draw = (cells, resolution, cg) => {
// example cell
// {
// "x": 3550000,
// "y": 2850000,
// "imputed": "363",
// "ind": "1593",
// "ind_snv": "31946271.4",
// "rIncome": 1671.1797133291484
// }
//draw with HTML canvas in geo coordinates
cg.setCanvasTransform();

//compute min/max population/av income
const popExt = d3.extent(cells, (c) => +c.ind);
const incomeExt = d3.extent(cells, (c) => +c.rIncome);

for (let cell of cells) {
//do not draw cells with no data
if (!cell.ind) continue;

let popT = (cell.ind - popExt[0]) / (popExt[1] - popExt[0]);
popT = gviz.sExpRev(popT, -popStretch);

let incomeT =
(cell.rIncome - incomeExt[0]) / (incomeExt[1] - incomeExt[0]);
incomeT = gviz.sExpRev(incomeT, -incomeStretch);

switch (style) {
case "Conic gradient":
conicGradientStyle(cg.ctx, resolution, cell, popT, incomeT);
break;
case "Spiral":
drawSpiral(
cg.ctx,
cell.x + resolution / 2,
cell.y + resolution / 2,
resolution,
popT,
incomeT
);
break;
case "Pattern":
drawPattern(
cg.ctx,
cell.x + resolution / 2,
cell.y + resolution / 2,
resolution,
popT,
incomeT
);
break;
case "Christmas":
drawChristmasTree(
cg.ctx,
cell.x + resolution / 2,
cell.y + resolution,
resolution,
popT,
incomeT
);
break;
default:
drawSpiral(
cg.ctx,
cell.x + resolution / 2,
cell.y + resolution / 2,
resolution,
popT,
incomeT
);
break;
}
}
};
//this redraw need to be executed to be ensure the map redraws when the style draw function changes
map.redraw();

return customStyle.draw;
}
Insert cell
conicGradientStyle = function (ctx, resolution, cell, popT, incomeT) {
// Create a conic gradient
// The start angle is 0
// The center position is 100, 100
//createConicGradient(startAngle, x, y)
const gradient = ctx.createConicGradient(incomeT, cell.x, cell.y);

// Add color stops
gradient.addColorStop(0, d3.interpolateYlGnBu(popT));
gradient.addColorStop(1, "#F98B85");

// Set the fill style and draw a rectangle
ctx.fillStyle = gradient;
ctx.fillRect(cell.x, cell.y, resolution, resolution);
}
Insert cell
scale = d3.scaleLog
Insert cell
christmasScale = d3.scaleLinear([0, 0.5, 1], ["🎄", "⛄️", "🎁"])
Insert cell
function drawChristmasTree(ctx, x, y, size, popT, incomeT) {
ctx.beginPath();
ctx.fillStyle = "#228B22"; // Dark green for the Christmas tree
ctx.strokeStyle = "black"; // Brown for the trunk
ctx.lineWidth = 2;

const numLayers = 5;
const trunkWidth = (size / 5) * popT;
const trunkHeight = (size / 5) * popT;
// Draw Christmas tree layers
for (let i = 0; i < numLayers; i++) {
const layerHeight = (size / 2) * (i / numLayers) * popT;
const layerWidth = (size / 2) * ((i + 1) / numLayers) * popT;

ctx.moveTo(x - layerWidth / 2, y - layerHeight - trunkHeight / 2);
ctx.lineTo(x + layerWidth / 2, y - layerHeight - trunkHeight / 2);
ctx.lineTo(x, y - trunkHeight / 2);
ctx.closePath();
ctx.fill();
ctx.stroke();
}

// Draw tree trunk
ctx.fillStyle = "brown";
ctx.fillRect(x - trunkWidth / 2, y, trunkWidth, trunkHeight);
}
Insert cell
function drawPattern(ctx, x, y, size, popT, incomeT) {
ctx.beginPath();
ctx.fillStyle = "#ffffff";
ctx.strokeStyle = d3.interpolateYlGnBu(incomeT);
ctx.lineWidth = (size / 20) * (popT * 3);

const numLayers = 5;
const numShapes = 8;

for (let i = 1; i <= numLayers; i++) {
for (let j = 0; j < numShapes; j++) {
const shapeSize = (size / 2) * (i / numLayers) * popT;
const angle = (j * 2 * Math.PI) / numShapes;

const xOffset = x + shapeSize * Math.cos(angle);
const yOffset = y + shapeSize * Math.sin(angle);

ctx.moveTo(x + size / 2, y);
ctx.lineTo(xOffset, yOffset);
}
}

ctx.closePath();
ctx.fill();
ctx.stroke();
}
Insert cell
function drawSpiral(ctx, x, y, size, popT, incomeT) {
ctx.beginPath();
ctx.fillStyle = "#ffffff";
ctx.strokeStyle = d3.interpolateYlGnBu(incomeT);
ctx.lineWidth = (size / 20) * (popT * 3);

for (let i = 0; i < 960; i += 10) {
const angle = (i * Math.PI) / 180;
const radius = (size / 10 - (size / 960) * i) * popT; // Adjust the size calculation
const xOffset = x + radius * Math.cos(angle);
const yOffset = y + radius * Math.sin(angle);
ctx.lineTo(xOffset, yOffset);
}

ctx.fill();
ctx.stroke();
ctx.closePath();
}
Insert cell
Insert cell
function getRandomColor() {
const hue = Math.random() * 50 + 20; // Warm hues
const saturation = Math.random() * 40 + 60;
const lightness = Math.random() * 30 + 40;

return `hsl(${hue}, ${saturation}%, ${lightness}%)`;
}
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) +
"<br>" +
"Average monthly income: €" +
Math.floor(c.rIncome)
});
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