diff --git a/index.html b/index.html index 45303fa77ecc0090b32d7863f6aa2c009e467ecb..8cc4cceef786961fa3d2c1a2c57a11d77a0591df 100644 --- a/index.html +++ b/index.html @@ -20,7 +20,7 @@ <div id="bubble-chart-wrapper"> </div> - <div class="bubble-chart-settings"> + <div class="chart-settings bubble-chart-settings"> <div class="size-by"> <label for="group-by-setting">Größe nach:</label> @@ -58,6 +58,7 @@ <div id="radar-chart-wrapper"> </div> + <div class="radar-chart-settings chart-settings"></div> </div> </div> <script type="module" src="/src/main.ts"></script> diff --git a/src/charts/radarChart.ts b/src/charts/radarChart.ts index c8c6e74d36d0d4774960d818767f6189b299b63d..988a308864e879a8f636747a0555f04047af6646 100644 --- a/src/charts/radarChart.ts +++ b/src/charts/radarChart.ts @@ -1,6 +1,7 @@ import * as d3 from "d3"; import {ScaleLinear} from "d3"; import Chart, {ChartConfig, ChartConfigParam} from "@/charts/chart.ts"; +import {BubbleChartConfigParam} from "@/charts/bubbleChart.ts"; export type RadarChartConfig = ChartConfig & { selectedData: RadarChartSelection[], @@ -10,7 +11,7 @@ export type RadarChartConfig = ChartConfig & { attributes: { key: string, label: string }[] | [], } -export type RadarChartConfigParam = ChartConfigParam & Partial<RadarChartConfig> +export type RadarChartConfigParam = Partial<ChartConfigParam> & Partial<RadarChartConfig> type RadarChartSelection = { _color: string | null | undefined, @@ -71,13 +72,18 @@ export default class RadarChart extends Chart { vis.chart = svg.append('g') - for (const attribute of vis.config.attributes) { - const domain: [number, number] = [0, (d3.max(vis.data, (d: any) => d[attribute.key] as number) as number)] + vis.updateAxes() + } + + updateAxes() { + this.axes = [] + for (const attribute of this.config.attributes) { + const domain: [number, number] = [0, (d3.max(this.data, (d: any) => d[attribute.key] as number) as number)] const scale = d3.scaleLinear( domain, - [0, vis.axisLength], + [0, this.axisLength], ) - vis.axes.push({ + this.axes.push({ scale, domain, label: attribute.label, @@ -92,6 +98,18 @@ export default class RadarChart extends Chart { this.drawData(); } + updateSelectedAttribute(attributes: { key: string, label: string }[]): void { + this.config.attributes = attributes + + this.updateAxes() + // force a redraw. Due to the idAccessor, the data would not be rerendered otherwise + const copy = this.config.selectedData + this.config.selectedData = [] + this.renderVis() + this.config.selectedData = copy + this.drawData() + } + private getPreparedData(): { data: RadarChartSelection, axesValues: { label: string, r: number, value: number }[] @@ -124,12 +142,23 @@ export default class RadarChart extends Chart { renderVis(): void { let vis: RadarChart = this; - const axisGrid = vis.chart.append("g") + vis.drawAxes() + + vis.drawData(); + } + + private drawAxes() { + let axisGrid = this.chart.selectAll(".axisWrapper") + + if (axisGrid.empty()) { + axisGrid = this.chart.append("g") .attr("class", "axisWrapper") - .attr('transform', `translate(${vis.chartCenter},${vis.chartCenter})`) + .attr('transform', `translate(${this.chartCenter},${this.chartCenter})`) + } + axisGrid.selectAll('*').remove() const axes = axisGrid.selectAll('.axis') - .data(vis.axes) + .data(this.axes) .enter() .append('g') .attr('class', 'axis') @@ -137,18 +166,16 @@ export default class RadarChart extends Chart { axes.append("path") .attr("pointer-events", "none") .attr("d", (_: any, index: number) => d3.lineRadial() - ([[0, 0], [Math.PI * 2 * index / vis.axes.length, this.axisLength]]) + ([[0, 0], [Math.PI * 2 * index / this.axes.length, this.axisLength]]) ) axes.append('text') - .attr("x", (_: any, index: number) => Math.sin(2 * Math.PI * (index / vis.axes.length)) * (this.axisLength + 10)) - .attr("y", (_: any, index: number) => -Math.cos(2 * Math.PI * (index / vis.axes.length)) * (this.axisLength + 10)) + .attr("x", (_: any, index: number) => Math.sin(2 * Math.PI * (index / this.axes.length)) * (this.axisLength + 10)) + .attr("y", (_: any, index: number) => -Math.cos(2 * Math.PI * (index / this.axes.length)) * (this.axisLength + 10)) .attr('text-anchor', 'middle') .attr('alignment-baseline', 'middle') .attr('font-size', 12) .attr('fill', 'black') .text((d: any) => d.label) - - this.drawData(); } private drawData() { diff --git a/src/main.ts b/src/main.ts index 89098df075dba371f3c01ce9d8b37d07e8cbf3f0..a82d002265020bacaa02bcc845d70f2043931a82 100644 --- a/src/main.ts +++ b/src/main.ts @@ -61,6 +61,34 @@ sizeBySetting.oninput = (event) => { } const selectedNodes: (Player & { _color: string})[] = [] +const radarChartSettings = document.querySelector('.radar-chart-settings') as HTMLDivElement +const radarChartAttributes = [ + { + key: 'Performance _ G+A', + label: numericColumns['Performance _ G+A'] + }, + { + key: 'Expected_0 _ npxG+xAG', + label: numericColumns['Expected_0 _ npxG+xAG'] + }, + { + key: 'PrgP', + label: numericColumns['PrgP'] + }, + { + key: 'Total _ Cmp%', + label: numericColumns['Total _ Cmp%'] + }, + { + key: 'Tkl+Int', + label: numericColumns['Tkl+Int'] + }, + { + key: 'Touches _ Touches', + label: numericColumns['Touches _ Touches'] + }, +] + dsv(';', 'data/output.csv').then(data => { parsedData = data.map((d: any) => { for (const column of Object.keys(numericColumns)) { @@ -156,34 +184,34 @@ function updateSelectedNodes(node: Player) { </table> `) }, - attributes: [ - { - key: 'Performance _ G+A', - label: numericColumns['Performance _ G+A'] - }, - { - key: 'Expected_0 _ npxG+xAG', - label: numericColumns['Expected_0 _ npxG+xAG'] - }, - { - key: 'PrgP', - label: numericColumns['PrgP'] - }, - { - key: 'Total _ Cmp%', - label: numericColumns['Total _ Cmp%'] - }, - { - key: 'Tkl+Int', - label: numericColumns['Tkl+Int'] - }, - { - key: 'Touches _ Touches', - label: numericColumns['Touches _ Touches'] - }, - ] + attributes: radarChartAttributes, }) radarChart.renderVis() + + // add attribute settings + for (const index in radarChartAttributes) { + const input = document.createElement('select') + + for (const column of Object.keys(numericColumns)) { + const option = document.createElement('option') + option.value = column + option.text = numericColumns[column as keyof typeof numericColumns] + input.appendChild(option) + } + input.value = radarChartAttributes[index].key + input.oninput = (event) => { + const value = (event.target as any)?.value + radarChartAttributes[index] = { + key: value, + label: numericColumns[value as keyof typeof numericColumns] + } + radarChart?.updateSelectedAttribute(radarChartAttributes) + + } + + radarChartSettings.appendChild(input) + } + } else { radarChart.updateVis(selectedNodes) } diff --git a/src/styles/index.scss b/src/styles/index.scss index f529d95e958be468f8396c23259d30d333079bd7..dcd41b8ceac6625bd36a888658163918a7640acb 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -85,17 +85,9 @@ h1 { } .bubble-chart-settings { - position: absolute; - top: 100%; - right: 0; - display: flex; - padding: 2rem; - border-radius: 0.5rem; - flex-direction: column; align-items: flex-end; justify-content: flex-end; gap: 1rem; - @include glass; > div { align-items: center; @@ -117,6 +109,21 @@ h1 { margin: 0 4rem 4rem; } } + + .chart-settings { + position: absolute; + top: 100%; + right: 0; + display: flex; + padding: 2rem; + border-radius: 0.5rem; + flex-direction: column; + @include glass; + + &:empty { + display: none; + } + } } button {