316 lines
6.2 KiB
TypeScript
316 lines
6.2 KiB
TypeScript
import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm";
|
|
import { JSDOM } from "https://jspm.dev/jsdom@22";
|
|
|
|
type DataPoint = {
|
|
value: number;
|
|
date: Date;
|
|
};
|
|
export function drawGraph(data: DataPoint[], title: string) {
|
|
const context = {
|
|
jsdom: new JSDOM(""),
|
|
};
|
|
const { window } = context.jsdom;
|
|
const { document } = window;
|
|
|
|
const body = d3.select(document).select("body");
|
|
|
|
const width = 400;
|
|
const height = 200;
|
|
|
|
const marginTop = 20;
|
|
const marginRight = 30;
|
|
const marginBottom = 30;
|
|
const marginLeft = 60;
|
|
|
|
let svg = body
|
|
.append("svg")
|
|
.attr("xmlns", "http://www.w3.org/2000/svg")
|
|
.attr("width", width + marginLeft + marginRight)
|
|
.attr("height", height + marginTop + marginBottom);
|
|
|
|
svg
|
|
.append("rect")
|
|
.attr("width", "100%")
|
|
.attr("height", "100%")
|
|
.attr("fill", "white");
|
|
|
|
svg = svg
|
|
.append("g")
|
|
.attr("transform", "translate(" + marginLeft + "," + marginTop + ")");
|
|
|
|
const x = d3
|
|
.scaleTime()
|
|
.domain(
|
|
d3.extent(data, function (d: DataPoint) {
|
|
return d.date;
|
|
})
|
|
)
|
|
.nice()
|
|
.range([0, width]);
|
|
|
|
const xAxis = d3.axisBottom(x).ticks(5);
|
|
|
|
svg
|
|
.append("g")
|
|
.attr("transform", "translate(0," + height + ")")
|
|
.call(xAxis);
|
|
|
|
// Add Y axis
|
|
const y = d3
|
|
.scaleLinear()
|
|
.domain([
|
|
0,
|
|
d3.max(data, function (d: DataPoint) {
|
|
return +d.value;
|
|
}) * 1.5,
|
|
])
|
|
.range([height, 0])
|
|
.nice();
|
|
svg.append("g").call(d3.axisLeft(y));
|
|
|
|
svg
|
|
.append("text")
|
|
.attr("text-anchor", "middle")
|
|
.attr("style", "font-size: 12px")
|
|
.attr("transform", "rotate(-90)")
|
|
.attr("y", -marginLeft + 20)
|
|
.attr("x", -height / 2)
|
|
.text("[jobs/s]");
|
|
|
|
svg
|
|
.append("text")
|
|
.attr("text-anchor", "middle")
|
|
.attr("style", "font-size: 16px")
|
|
.attr("y", 0)
|
|
.attr("x", width / 2)
|
|
.text(title);
|
|
|
|
// Add the line
|
|
svg
|
|
.append("path")
|
|
.datum(data)
|
|
.attr("fill", "none")
|
|
.attr("stroke", "steelblue")
|
|
.attr("stroke-width", 1.5)
|
|
.attr(
|
|
"d",
|
|
d3.line(
|
|
function (d: DataPoint) {
|
|
return x(d.date);
|
|
},
|
|
function (d: DataPoint) {
|
|
return y(d.value);
|
|
}
|
|
)
|
|
);
|
|
|
|
return body.node().innerHTML;
|
|
}
|
|
|
|
interface DataPointMulti extends DataPoint {
|
|
kind: string;
|
|
}
|
|
|
|
export function drawGraphMulti(data: DataPointMulti[], title: string) {
|
|
const context = {
|
|
jsdom: new JSDOM(""),
|
|
};
|
|
const { window } = context.jsdom;
|
|
const { document } = window;
|
|
|
|
const body = d3.select(document).select("body");
|
|
|
|
const width = 400;
|
|
const height = 200;
|
|
|
|
const marginTop = 20;
|
|
const marginRight = 100;
|
|
const marginBottom = 30;
|
|
const marginLeft = 60;
|
|
|
|
let svg = body
|
|
.append("svg")
|
|
.attr("xmlns", "http://www.w3.org/2000/svg")
|
|
.attr("width", width + marginLeft + marginRight)
|
|
.attr("height", height + marginTop + marginBottom);
|
|
|
|
svg
|
|
.append("rect")
|
|
.attr("width", "100%")
|
|
.attr("height", "100%")
|
|
.attr("fill", "white");
|
|
|
|
svg = svg
|
|
.append("g")
|
|
.attr("transform", "translate(" + marginLeft + "," + marginTop + ")");
|
|
|
|
const x = d3
|
|
.scaleTime()
|
|
.domain(
|
|
d3.extent(data, function (d: DataPoint) {
|
|
return d.date;
|
|
})
|
|
)
|
|
.nice()
|
|
.range([0, width]);
|
|
|
|
const xAxis = d3.axisBottom(x).ticks(5);
|
|
|
|
svg
|
|
.append("g")
|
|
.attr("transform", "translate(0," + height + ")")
|
|
.call(xAxis);
|
|
|
|
// Add Y axis
|
|
const y = d3
|
|
.scaleLinear()
|
|
.domain([
|
|
0,
|
|
d3.max(data, function (d: DataPoint) {
|
|
return +d.value;
|
|
}) * 1.5,
|
|
])
|
|
.range([height, 0])
|
|
.nice();
|
|
svg.append("g").call(d3.axisLeft(y));
|
|
|
|
svg
|
|
.append("text")
|
|
.attr("text-anchor", "middle")
|
|
.attr("style", "font-size: 12px")
|
|
.attr("transform", "rotate(-90)")
|
|
.attr("y", -marginLeft + 20)
|
|
.attr("x", -height / 2)
|
|
.text("[jobs/s]");
|
|
|
|
svg
|
|
.append("text")
|
|
.attr("text-anchor", "middle")
|
|
.attr("style", "font-size: 16px")
|
|
.attr("y", 0)
|
|
.attr("x", width / 2)
|
|
.text(title);
|
|
|
|
const sumstat = d3.group(data, function (d: DataPointMulti) {
|
|
return d.kind;
|
|
});
|
|
|
|
const keys = Array.from(sumstat.keys());
|
|
|
|
const color = d3
|
|
.scaleOrdinal()
|
|
.domain(keys)
|
|
.range([
|
|
"#e41a1c",
|
|
"#377eb8",
|
|
"#4daf4a",
|
|
"#984ea3",
|
|
"#ff7f00",
|
|
"#ffff33",
|
|
"#a65628",
|
|
"#f781bf",
|
|
"#999999",
|
|
]);
|
|
|
|
// Add the line
|
|
svg
|
|
.selectAll("path.line")
|
|
.data(sumstat)
|
|
.join("path")
|
|
.attr("class", "line")
|
|
.attr("fill", "none")
|
|
.attr("stroke", function (d) {
|
|
return color(d[0]);
|
|
})
|
|
.attr("stroke-width", 1.5)
|
|
.attr("d", (d) => {
|
|
return d3
|
|
.line()
|
|
.x((d) => x(d.date))
|
|
.y((d) => y(d.value))(d[1]);
|
|
});
|
|
|
|
const size = 15;
|
|
svg
|
|
.selectAll(".dot")
|
|
.data(keys)
|
|
.enter()
|
|
.append("rect")
|
|
.attr("class", "dot")
|
|
.attr("x", 400)
|
|
.attr("y", function (d, i) {
|
|
return 5 + i * (size + 5);
|
|
})
|
|
.attr("width", size)
|
|
.attr("height", size)
|
|
.style("fill", function (d) {
|
|
return color(d);
|
|
});
|
|
svg
|
|
.selectAll(".label")
|
|
.data(keys)
|
|
.enter()
|
|
.append("text")
|
|
.attr("class", "label")
|
|
.attr("x", 400 + size * 1.2)
|
|
.attr("y", function (d, i) {
|
|
return 5 + i * (size + 5) + size / 2;
|
|
})
|
|
.style("fill", function (d) {
|
|
return color(d);
|
|
})
|
|
.text(function (d) {
|
|
return d;
|
|
})
|
|
.attr("text-anchor", "left")
|
|
.style("alignment-baseline", "middle");
|
|
|
|
return body.node().innerHTML;
|
|
}
|
|
|
|
if (import.meta.main) {
|
|
const svg = drawGraph(
|
|
[
|
|
{
|
|
value: 10,
|
|
date: new Date(86400000),
|
|
},
|
|
{
|
|
value: 12,
|
|
date: new Date(86400000 * 2),
|
|
},
|
|
],
|
|
"test"
|
|
);
|
|
|
|
const svg2 = drawGraphMulti(
|
|
[
|
|
{
|
|
value: 10,
|
|
date: new Date(86400000),
|
|
kind: "test",
|
|
},
|
|
{
|
|
value: 12,
|
|
date: new Date(86400000 * 2),
|
|
kind: "test",
|
|
},
|
|
{
|
|
value: 8,
|
|
date: new Date(86400000),
|
|
kind: "test2",
|
|
},
|
|
{
|
|
value: 9,
|
|
date: new Date(86400000 * 2),
|
|
kind: "test2",
|
|
},
|
|
],
|
|
"test"
|
|
);
|
|
|
|
console.log(svg);
|
|
console.log(svg2);
|
|
Deno.exit(0);
|
|
}
|