Public
Edited
May 8, 2024
Importers
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function postAggrFn(cells, cellSize, global, panel) {
for (const cell of cells) {
cell.averages = [];
if (cell.records) {
// console.log("summarise", reshapeData(aggregatesCell(cell.records)));
cell.averages = reshapeData(aggregatesCell(cell.records));
}
}
}
Insert cell
//draw a little linechart of each variable
function interactiveDrawFn(mode) {
return function drawFn(cell, x, y, cellSize, ctx, global, panel) {
if (!cell) return;
const padding = 2;
// ctx.globalAlpha = 0.5;

var grid_long = cellSize - padding * 2;
var grid_wide = cellSize - padding * 2;

//draw cell background
const boundary = cell.getBoundary(padding);
// console.log("boundary: ", boundary);
ctx.fillStyle = "#cccb";
ctx.beginPath();
ctx.moveTo(boundary[0][0], boundary[0][1]);
for (let i = 1; i < boundary.length; i++)
ctx.lineTo(boundary[i][0], boundary[i][1]);
ctx.closePath();
ctx.fill();

// data cleaning-selection
const newData = Object.fromEntries(
getKeys.map((key) => [key, cell.averages[key]])
);
const cleanedAvg = removeEmpty(newData);

// draw line charts
if (mode == "Line Chart") {
drawLineChart(ctx, x, y, cellSize, cell.averages);
} else if (mode == "Heatmaps") {
drawHeatmapChart(ctx, x, y, cellSize, cell.averages);
} else if (mode == "Rose Chart") {
// console.log("avg data", cell.averages);
// console.log("cleaned", cleanedAvg);
drawNightingaleRoseChart(
ctx,
x,
y,
cellSize,
cleanedAvg,
colours,
padding
);
}

// Cek if data exists
// const averages = reshapeData(aggregatesCell(cell.records));
// console.log("sent averaged data to cells", cell.averages);
};
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function drawNightingaleRoseChart(
ctx,
x,
y,
cellSize,
data,
colours,
padding = 2
) {
// Calculate radius of the chart
let radius = (cellSize - 2 * padding) / 2;

// Calculate center of the chart
let centerX = x; //+ padding; //+ radius;
let centerY = y; // + padding; //+ radius;

// console.log("rose center", centerX, centerY);

// Calculate angle of each segment
let segmentAngle = (2 * Math.PI) / Object.keys(data).length;

// Iterate over each data type
let selectedColours = Object.values(colours).slice(0, getKeys.length);

// console.log("data type", data["employment"]);

Object.keys(data).forEach((type, i) => {
// // make the lower color 'whiter'
const colorScale = d3
.scaleLinear()
.domain([0, 100])
.range(["white", selectedColours[i]]);

const hslColour = d3.hsl(selectedColours[i]);
// console.log(hslColour.s);

// Initialize inner radius
let innerRadius = 0;

// Iterate over each time value
Object.values(data[type])
.slice()
.reverse()
.forEach((value, j) => {
// Calculate outer radius of the segment
let outerRadius = (value / 100) * radius;

// adjust saturation based on minutes (whiter=less minutes)
const adjustedColor = colorScale(value);
// console.log(value);

// const rgbaColor = d3.rgb(adjustedColor).toString();

// opacity based on weights
const d3Color = d3.color(adjustedColor);
// console.log(varWeights[getKeys[i]]);
d3Color.opacity = varWeights[getKeys[i]]; // Object.values(varWeights).slice(0, getKeys.length)[i]; // weights[i];
const modifiedRGBA = d3Color.toString();

// Set fill style with adjusted color and opacity
// ctx.fillStyle = rgbaColor + opacity.toString(16).padStart(2, "0");
// console.log("modified ", modifiedRGBA);
ctx.fillStyle = modifiedRGBA;

// Begin path
ctx.beginPath();

// Draw the segment
ctx.arc(
centerX,
centerY,
outerRadius,
i * segmentAngle,
(i + 1) * segmentAngle
);
ctx.lineTo(centerX, centerY);
ctx.closePath();

ctx.fill();

// white stroke between segment
ctx.strokeStyle = "white";
ctx.stroke();

innerRadius = outerRadius;
});
});
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function aggregatesCell(data) {
if (data.length < 1) return;

// Use a Map to collect unique LSOA11CD values and their associated data
const uniqueData = new Map();
for (const item of data) {
const LSOA11CD = item.LSOA11CD;
if (!uniqueData.has(LSOA11CD)) {
uniqueData.set(LSOA11CD, []);
}
uniqueData.get(LSOA11CD).push(item);
}

// Create a single object with averaged values and all unique LSOA11CD
const averagedObject = {
LSOA11CD: Array.from(uniqueData.keys()).join(", "), // Combine unique LSOA11CD into a single string
no_data: uniqueData.size // store how many unique data are aggregated in the cell
};

// const valueKeys = Object.keys(data[0]).filter((key) => key !== "LSOA11CD"); // Get value keys
const valueKeys = Object.keys(data[0]).filter(
(key) => key !== "LSOA11CD" && key !== "lat" && key !== "long"
);
for (const key of valueKeys) {
let total = 0;
let count = 0;
for (const entries of uniqueData.values()) {
for (const entry of entries) {
if (key in entry) {
total += entry[key];
count++;
}
}
}
const average = total / count || 0; // Default to 0 if no data
averagedObject[key] = average;
}
// for (const key of valueKeys) {
// const total = Array.from(uniqueData.values())
// .values()
// .flatMap((entries) => entries.map((entry) => entry[key]))
// .reduce((sum, value) => sum + value, 0);
// const average = total / uniqueData.size;
// averagedObject[key] = average;
// }

return [averagedObject]; // Return an array with the single averaged object
}
Insert cell
Insert cell
Insert cell
Insert cell
selected_data = filteredData.map((obj) => {
return [...selected_variables, "LSOA11CD", "lat", "long"].reduce(
(newObj, key) => {
if (obj.hasOwnProperty(key)) {
newObj[key] = obj[key];
}
return newObj;
},
{}
);
})
Insert cell
// select only percentage variables
selected_variables = {
const combinations = [];
for (const key of getKeys) {
for (const minute of getMinutes) {
combinations.push(`${key}_${minute}`);
combinations.push(`${key}_pct_${minute}`);
}
}
const filtered = combinations.filter((name) => name.includes("_pct_"));
return filtered;
}
Insert cell
Insert cell
Insert cell
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