commit 6f9a18b8
Add a d3 pie chart to the page
Changed files
| A | static_content/css/oastat_d3.css |
| M | templates/map.tpl before |
diff --git a/static_content/css/oastat_d3.css b/static_content/css/oastat_d3.css
new file mode 100644
index 0000000..4b5c5a1
--- /dev/null
+++ b/static_content/css/oastat_d3.css
@@ -0,0 +1,42 @@
+/* D3.js visualizations styles */
+
+.pie-chart {
+ display: flex;
+ justify-content: center;
+ margin: 20px 0;
+}
+
+.pie-chart svg {
+ font-family: Arial, sans-serif;
+}
+
+.pie-slice {
+ stroke: #fff;
+ stroke-width: 2px;
+}
+
+.pie-label {
+ fill: #333;
+ font-size: 12px;
+ pointer-events: none;
+}
+
+.legend {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ margin: 10px 0;
+ gap: 10px;
+}
+
+.legend-item {
+ display: flex;
+ align-items: center;
+ margin: 5px;
+}
+
+.legend-color {
+ width: 15px;
+ height: 15px;
+ margin-right: 5px;
+}
diff --git a/templates/map.tpl b/templates/map.tpl
index 8e50aa2..7a42df9 100644
--- a/templates/map.tpl
+++ b/templates/map.tpl
@@ -30,6 +30,8 @@
<meta http-equiv="Content-Language" content="en">
<meta name="description" content="OpenArena stats by oastats">
<link rel="stylesheet" href="../static/css/oastat.css" type="text/css">
+ <link rel="stylesheet" href="../static/css/oastat_d3.css" type="text/css">
+ <script src="https://d3js.org/d3.v7.min.js"></script>
<title>OpenArena Stats - Map {{MAP_NAME}}</title>
</head>
<body>
@@ -41,6 +43,12 @@
</div>
<h2>Weapon Kills on this Map</h2>
+
+ <div class="pie-chart">
+ <div id="weapon-pie-chart"></div>
+ </div>
+ <div class="legend" id="weapon-legend"></div>
+
<table>
<tr>
<th>Weapon</th>
@@ -53,6 +61,90 @@
</tr>
{{/WEAPON_KILLS}}
</table>
+
+ <script>
+ // Weapon kills data
+ const weaponData = [
+ {{#WEAPON_KILLS}}
+ {name: "{{WEAPON_NAME}}", value: {{KILL_COUNT}}},
+ {{/WEAPON_KILLS}}
+ ];
+
+ // Set up dimensions
+ const width = 500;
+ const height = 400;
+ const radius = Math.min(width, height) / 2 - 40;
+
+ // Color scale
+ const color = d3.scaleOrdinal()
+ .domain(weaponData.map(d => d.name))
+ .range(d3.schemeCategory10);
+
+ // Create SVG
+ const svg = d3.select("#weapon-pie-chart")
+ .append("svg")
+ .attr("width", width)
+ .attr("height", height)
+ .append("g")
+ .attr("transform", `translate(${width/2},${height/2})`);
+
+ // Create pie layout
+ const pie = d3.pie()
+ .value(d => d.value)
+ .sort(null);
+
+ // Create arc generator
+ const arc = d3.arc()
+ .innerRadius(0)
+ .outerRadius(radius);
+
+ const labelArc = d3.arc()
+ .innerRadius(radius * 0.6)
+ .outerRadius(radius * 0.6);
+
+ // Create pie slices
+ const arcs = svg.selectAll(".arc")
+ .data(pie(weaponData))
+ .enter()
+ .append("g")
+ .attr("class", "arc");
+
+ arcs.append("path")
+ .attr("class", "pie-slice")
+ .attr("d", arc)
+ .attr("fill", d => color(d.data.name))
+ .append("title")
+ .text(d => `${d.data.name}: ${d.data.value} kills`);
+
+ // Add labels for larger slices
+ arcs.append("text")
+ .attr("class", "pie-label")
+ .attr("transform", d => `translate(${labelArc.centroid(d)})`)
+ .attr("text-anchor", "middle")
+ .style("display", d => {
+ // Only show label if slice is large enough
+ const percent = (d.endAngle - d.startAngle) / (2 * Math.PI) * 100;
+ return percent > 5 ? "block" : "none";
+ })
+ .text(d => {
+ const percent = ((d.endAngle - d.startAngle) / (2 * Math.PI) * 100).toFixed(1);
+ return `${percent}%`;
+ });
+
+ // Create legend
+ const legend = d3.select("#weapon-legend");
+ weaponData.forEach(d => {
+ const item = legend.append("div")
+ .attr("class", "legend-item");
+
+ item.append("div")
+ .attr("class", "legend-color")
+ .style("background-color", color(d.name));
+
+ item.append("span")
+ .text(`${d.name} (${d.value})`);
+ });
+ </script>
<h2>Most Recent Matches</h2>
<table>