// Six locations, each a real heatmap of day x hour check-ins, all six drawn
// against ONE colour scale derived from the union of every panel's values.
// That is the whole point of the grid: the same fill means the same count
// everywhere, so the quiet sites read as quiet. Six independently shaded
// heatmaps would give every panel its own darkest cell and the comparison
// would be a lie.
//
// Deterministic counts so the e2e snapshot is stable.
function mulberry32(seed) {
return function () {
seed |= 0
seed = (seed + 0x6d2b79f5) | 0
var t = Math.imul(seed ^ (seed >>> 15), 1 | seed)
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t
return ((t ^ (t >>> 14)) >>> 0) / 4294967296
}
}
var DAYS = ['Sun', 'Sat', 'Fri', 'Thu', 'Wed', 'Tue', 'Mon']
var HOURS = ['6a', '8a', '10a', '12p', '2p', '4p', '6p', '8p']
// Each location has its own rhythm AND its own overall volume: the commuter
// sites are busiest, the campus site is a midday hump, the lakeside site is
// small and evening-heavy. Volume differences are the point of one scale.
var LOCATIONS = [
{
name: 'Central Station',
seed: 7,
volume: 1.0,
peaks: [1, 6],
weekend: 0.45,
},
{ name: 'Harbor Point', seed: 19, volume: 0.82, peaks: [1, 6], weekend: 0.6 },
{
name: 'University Campus',
seed: 31,
volume: 0.66,
peaks: [3, 4],
weekend: 0.3,
},
{
name: 'Riverside Mall',
seed: 43,
volume: 0.58,
peaks: [4, 5],
weekend: 1.15,
},
{
name: 'Airport North',
seed: 59,
volume: 0.74,
peaks: [0, 7],
weekend: 0.95,
},
{
name: 'Lakeside Park',
seed: 71,
volume: 0.31,
peaks: [5, 6],
weekend: 1.4,
},
]
function hourlyRow(rand, loc, dayIndex) {
// DAYS runs Sun..Mon top to bottom, so weekend factor keys off the ends.
var isWeekend = dayIndex === 0 || dayIndex === 1
var dayFactor = isWeekend ? loc.weekend : 0.85 + rand() * 0.3
return HOURS.map(function (hour, hi) {
var near = Math.min(
Math.abs(hi - loc.peaks[0]),
Math.abs(hi - loc.peaks[1]),
)
var shape = Math.max(0.12, 1 - near * 0.28)
var v = 130 * loc.volume * dayFactor * shape * (0.85 + rand() * 0.3)
return { x: hour, y: Math.round(v) }
})
}
var activitySeries = []
LOCATIONS.forEach(function (loc) {
var rand = mulberry32(loc.seed)
DAYS.forEach(function (day, di) {
activitySeries.push({
name: day,
location: loc.name,
data: hourlyRow(rand, loc, di),
})
})
})
var options = {
series: activitySeries,
chart: {
id: 'activityTrellis',
type: 'heatmap',
height: 620,
animations: {
enabled: false,
},
},
trellis: {
by: 'location',
columns: 3,
minPanelWidth: 300,
gap: 14,
},
colors: ['#0E7490'],
plotOptions: {
heatmap: {
radius: 2,
enableShades: true,
shadeIntensity: 0.55,
colorScale: {
gradientLegend: {
formatter: function (val) {
return Math.round(Number(val)) + ' check-ins'
},
},
},
},
},
dataLabels: {
enabled: false,
},
stroke: {
width: 1,
colors: ['#fff'],
},
xaxis: {
type: 'category',
},
tooltip: {
compact: true,
y: {
formatter: function (val) {
return val + ' check-ins'
},
},
},
}
var chart = new ApexCharts(document.querySelector('#chart'), options)
chart.render()