diff --git a/src/main/resources/templates/charts.ftl b/src/main/resources/templates/charts.ftl
index bc45694..c9fcacd 100644
--- a/src/main/resources/templates/charts.ftl
+++ b/src/main/resources/templates/charts.ftl
@@ -5,7 +5,8 @@
<#include "header.ftl">
-
<#if aggregatedTopics?? && (aggregatedTopics?size gt 0)>
diff --git a/src/main/resources/templates/sentimentsRadarChart.ftl b/src/main/resources/templates/sentimentsRadarChart.ftl
index db70cba..5ed193b 100644
--- a/src/main/resources/templates/sentimentsRadarChart.ftl
+++ b/src/main/resources/templates/sentimentsRadarChart.ftl
@@ -13,105 +13,124 @@
<#else>
sentimentData.push({ pos: 0, neu: 0, neg: 0 });
#if>
- console.log("Sentiment Data:", sentimentData);
var width = 1000, height = 1000;
var svg = d3.select("#sentimentsRadarChart")
.attr("width", width)
.attr("height", height);
- var centerX = width / 2, centerY = height / 2;
- var maxRadius = 400; // Maximum radius for the chart
- // Create a radial scale (assumes sentiment values are normalized between 0 and 1)
- var rScale = d3.scaleLinear()
- .domain([0, 1])
- .range([0, maxRadius]);
+ var centerX = width / 2 - 100, centerY = height / 2;
+ var maxRadius = 400;
- // All relevant radar chart axes
+ var rScale = d3.scaleLinear().domain([0,1]).range([0,maxRadius]);
var axes = [
{ axis: "Positive", angle: 0 },
{ axis: "Neutral", angle: 120 },
{ axis: "Negative", angle: 240 }
];
- function polarToCartesian(cx, cy, r, angleDegrees) {
- var angleRadians = (angleDegrees - 90) * Math.PI / 180;
- return {
- x: cx + r * Math.cos(angleRadians),
- y: cy + r * Math.sin(angleRadians)
- };
+ function polarToCartesian(cx, cy, r, angle) {
+ var rad = (angle - 90) * Math.PI/180;
+ return { x: cx + r * Math.cos(rad), y: cy + r * Math.sin(rad) };
}
- // Draw grid (web) lines
+ // Draw grid (web)
var gridSteps = 5;
+ var gridLine = d3.line()
+ .x(d => d.x)
+ .y(d => d.y)
+ .curve(d3.curveLinearClosed);
for (var i = 1; i <= gridSteps; i++) {
var step = i / gridSteps;
- var gridPoints = axes.map(function(d) {
- var r = step * maxRadius;
- return polarToCartesian(centerX, centerY, r, d.angle);
- });
+ var pts = axes.map(d => polarToCartesian(centerX, centerY, step * maxRadius, d.angle));
svg.append("path")
- .datum(gridPoints)
- .attr("d", d3.line()
- .x(function(d) { return d.x; })
- .y(function(d) { return d.y; })
- .curve(d3.curveLinearClosed)
- )
+ .datum(pts)
+ .attr("d", gridLine)
.attr("stroke", "#aaa")
.attr("stroke-width", 1)
.attr("fill", "none");
}
- // Draw axis lines and labels
- axes.forEach(function(d) {
- var endPoint = polarToCartesian(centerX, centerY, maxRadius, d.angle);
+ // Draw axes & labels
+ axes.forEach(d => {
+ var end = polarToCartesian(centerX, centerY, maxRadius, d.angle);
svg.append("line")
- .attr("x1", centerX)
- .attr("y1", centerY)
- .attr("x2", endPoint.x)
- .attr("y2", endPoint.y)
- .attr("stroke", "#aaaaaa")
+ .attr("x1", centerX).attr("y1", centerY)
+ .attr("x2", end.x).attr("y2", end.y)
+ .attr("stroke", "#aaa")
.attr("stroke-width", 1);
-
- var labelOffset = 20;
- var labelPoint = polarToCartesian(centerX, centerY, maxRadius + labelOffset, d.angle);
+ var lbl = polarToCartesian(centerX, centerY, maxRadius + 20, d.angle);
svg.append("text")
- .attr("x", labelPoint.x)
- .attr("y", labelPoint.y)
+ .attr("x", lbl.x).attr("y", lbl.y)
.attr("text-anchor", "middle")
.style("font-size", "14px")
.text(d.axis);
});
- var colors = d3.schemeCategory10;
+ // Generate unique random colors
+ var colors = [];
+ sentimentData.forEach(() => {
+ let color;
+ do {
+ color = '#' + Math.floor(Math.random() * 16777215).toString(16).padStart(6, '0');
+ } while (colors.includes(color));
+ colors.push(color);
+ });
var radarLine = d3.line()
- .x(function(d) { return d.x; })
- .y(function(d) { return d.y; })
+ .x(d => d.x)
+ .y(d => d.y)
.curve(d3.curveLinearClosed);
- sentimentData.forEach(function(sent, i) {
- // Map each axis to a point using the sentiment value for that axis.
- var polygonPoints = axes.map(function(d) {
- var value;
- if (d.axis === "Positive") {
- value = sent.pos;
- } else if (d.axis === "Neutral") {
- value = sent.neu;
- } else if (d.axis === "Negative") {
- value = sent.neg;
- }
- var r = rScale(value);
- return polarToCartesian(centerX, centerY, r, d.angle);
+ // Plot radar series
+ sentimentData.forEach((sent, i) => {
+ var pts = axes.map(d => {
+ var val = d.axis === "Positive" ? sent.pos : (d.axis === "Neutral" ? sent.neu : sent.neg);
+ return polarToCartesian(centerX, centerY, rScale(val), d.angle);
});
-
svg.append("path")
- .datum(polygonPoints)
+ .datum(pts)
+ .attr("class", "radar-path radar-path-" + i)
.attr("d", radarLine)
- .attr("stroke", colors[i % colors.length])
+ .attr("stroke", colors[i])
.attr("stroke-width", 2)
- .attr("fill", colors[i % colors.length])
+ .attr("fill", colors[i])
.attr("fill-opacity", 0.3);
});
+
+ // Legend with wrap every 30 entries & hover highlight + bring to front
+ var legend = svg.append("g").attr("transform", "translate(" + (width - 200) + ",50)");
+ sentimentData.forEach((_, i) => {
+ var col = Math.floor(i/30), row = i%30;
+ var entry = legend.append("g")
+ .attr("transform", "translate(" + (col*100) + "," + (row*20) + ")")
+ .on("mouseover", () => {
+ d3.select(".radar-path-" + i).raise()
+ .attr("stroke-width", 4)
+ .attr("fill-opacity", 0.6);
+ })
+ .on("mouseout", () => {
+ d3.select(".radar-path-" + i)
+ .attr("stroke-width", 2)
+ .attr("fill-opacity", 0.3);
+ });
+ entry.append("rect")
+ .attr("width", 12)
+ .attr("height", 12)
+ .attr("fill", colors[i]);
+ entry.append("text")
+ .attr("x", 16)
+ .attr("y", 10)
+ .style("font-size", "12px")
+ .text(i === 0 ? "Rede" : "Satz " + i);
+ });
+
+
+
+
+
+
+
+