From a9e251d5b79460a788a3d26447076ae72e764799 Mon Sep 17 00:00:00 2001 From: Artorias Date: Sun, 23 Mar 2025 21:22:40 +0100 Subject: [PATCH] Updated radar chart --- src/main/resources/templates/charts.ftl | 3 +- .../templates/sentimentsRadarChart.ftl | 135 ++++++++++-------- 2 files changed, 79 insertions(+), 59 deletions(-) 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"> -

Chart Sortiment

+

Chart Sortiment

+

NLP Analyse aller Reden

<#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 }); - 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); + }); + + + + + + + +