You will find see source code of the model here: generateHeatmapSeattle.ts. And the raw data here: generateHeatmapSeattleData.ts.
In this example, we demonstrate how write a custom body area model by extending an AreaModel
.
The background color is given by the getCustomStyleAt()
method.
class HeatMapSeattleBodyModel extends AreaModel {
override getRowCount(): number { return times.length; }
override getRowHeight(_rowIndex: number): number { return defaultRowHeights; }
override getValueAt(rowIndex: number, columnIndex: number): any { if (columnIndex === 0) return times[rowIndex]; return ""; }
override getTooltipAt(rowIndex: number, columnIndex: number): any { if (columnIndex === 0) { return ""; } columnIndex--; const date = days[columnIndex]; const time = times[rowIndex]; if (map[date] && map[date][time]) { return `${date} ${time} → ${map[date][time].temperature}`; } return ""; }
override getCustomStyleAt(rowIndex: number, columnIndex: number): { [p: string]: string } | undefined { if (columnIndex === 0) { return undefined; } columnIndex--; const date = days[columnIndex]; const time = times[rowIndex]; if (!map[date] || !map[date][time]) return undefined;
const n = map[date][time].temperature;
// const red = new ColorRgb(255, 0, 0); // const blue = new ColorRgb(0, 0, 255); // const p = new TwoColorGradientArg(MIN, red, MAX, blue); // -> "background": GeCssColorUtil.getTwoColorGradientRGB(n, p),
const minColor = new ColorRgb(67, 1, 84); const middleColor = new ColorRgb(31, 144, 141); const maxColor = new ColorRgb(250, 230, 37); const MIDDLE = (MAX + MIN) / 2; const p = new ThreeColorGradientArg(MIN, minColor, MIDDLE, middleColor, MAX, maxColor);
return { "background": GeCssColorUtil.getThreeColorGradientRGB(n, p), "color": "transparent" // we only want to show the bg color }; }}
We also write our own footer area model, because of so many col spans.
class HeatMapSeattleFooterModel extends AreaModel {
override getRowCount(): number { return 1; }
override getRowHeight(_rowIndex: number): number { return defaultRowHeights; }
override getValueAt(_rowIndex: number, columnIndex: number): any { if (columnIndex === 1) return "Jan"; if (columnIndex === 32) return "Feb"; if (columnIndex === 60) return "Mar"; if (columnIndex === 91) return "Apr"; if (columnIndex === 121) return "May"; if (columnIndex === 152) return "Jun"; if (columnIndex === 182) return "Jul"; if (columnIndex === 213) return "Aug"; if (columnIndex === 244) return "Sep"; if (columnIndex === 274) return "Oct"; if (columnIndex === 305) return "Nov"; if (columnIndex === 335) return "Dec"; return ""; }
override getColspanAt(_rowIndex: number, columnIndex: number): number { if (columnIndex === 1) return 31; if (columnIndex === 32) return 28; if (columnIndex === 60) return 31; if (columnIndex === 91) return 30; if (columnIndex === 121) return 31; if (columnIndex === 152) return 30; if (columnIndex === 182) return 31; if (columnIndex === 213) return 31; if (columnIndex === 244) return 30; if (columnIndex === 274) return 31; if (columnIndex === 305) return 30; if (columnIndex === 335) return 31; return 0; }
override getMaxColspan(): number { return 32; }
override getCustomStyleAt(_rowIndex: number, columnIndex: number): { [p: string]: string } | undefined { return { "background": "#fff", "border-left": columnIndex > 2 ? "solid 1px #555" : "none" }; }}
The TableModel
is created by:
export function createHeatMapSeattleModel(): TableModelIf { const bodyAreaModel = new HeatMapSeattleBodyModel(); const footerAreaModel = new HeatMapSeattleFooterModel(); const columnSizes = [60, ...(new Array(days.length).fill(defaultColumnWidth))];
return TableFactory.createTableModel({ bodyAreaModel, footerAreaModel, columnSizes, fixedLeftColumnCount: 1 });}
Full demo code: heatmap2/run.astro