Skip to content
Snippets Groups Projects
Commit a6a65a6e authored by Leander's avatar Leander
Browse files

wip: try to fix issue where nodes are assigned to the wrong group

parent 983a024c
No related branches found
No related tags found
No related merge requests found
...@@ -23,10 +23,9 @@ ...@@ -23,10 +23,9 @@
<label for="group-by-setting">Gruppieren nach:</label> <label for="group-by-setting">Gruppieren nach:</label>
<select id="group-by-setting" name="group-by-setting"> <select id="group-by-setting" name="group-by-setting">
<option value="default">Keine</option> <option value="team" selected>Team</option>
<option value="nation">Nation</option> <option value="nation">Nation</option>
<option value="pos">Position</option> <option value="pos">Position</option>
<option value="team">Team</option>
<option value="league">Liga</option> <option value="league">Liga</option>
</select> </select>
</div> </div>
......
import * as d3 from "d3"; import * as d3 from "d3";
import {HierarchyNode} from "d3"; import {HierarchyNode, NumberValue, ScaleOrdinal, ScaleSequential} from "d3";
import {ChartConfig, ChartConfigParam} from "@/charts/chart.ts"; import {ChartConfig, ChartConfigParam} from "@/charts/chart.ts";
import SearchableChart from "@/charts/SearchableChart.ts"; import SearchableChart from "@/charts/SearchableChart.ts";
...@@ -7,7 +7,7 @@ import SearchableChart from "@/charts/SearchableChart.ts"; ...@@ -7,7 +7,7 @@ import SearchableChart from "@/charts/SearchableChart.ts";
export type BubbleChartConfig = ChartConfig & { export type BubbleChartConfig = ChartConfig & {
groupAccessor: (d: any) => string, groupAccessor: (d: any) => string,
sizeAccessor: (d: any) => number, sizeAccessor: (d: any) => number,
colorAccessor: (d: any) => number | null, colorAccessor: (d: any) => string | number | null,
zoomExtent: [number, number], zoomExtent: [number, number],
onZoom?: (event: any) => void onZoom?: (event: any) => void
} }
...@@ -21,6 +21,7 @@ export default class BubbleChart extends SearchableChart { ...@@ -21,6 +21,7 @@ export default class BubbleChart extends SearchableChart {
config: BubbleChartConfig config: BubbleChartConfig
packRoot: HierarchyNode<any> | null = null packRoot: HierarchyNode<any> | null = null
highlightedNode: HierarchyNode<any> | null = null highlightedNode: HierarchyNode<any> | null = null
colorScale: ScaleSequential<any> | ScaleOrdinal<any, any> | null = null
constructor(data: any[], _config: BubbleChartConfigParam) { constructor(data: any[], _config: BubbleChartConfigParam) {
super(data, _config as ChartConfigParam) super(data, _config as ChartConfigParam)
...@@ -80,6 +81,8 @@ export default class BubbleChart extends SearchableChart { ...@@ -80,6 +81,8 @@ export default class BubbleChart extends SearchableChart {
}); });
vis.chart.call(vis.zoom) vis.chart.call(vis.zoom)
vis.updateColorScale()
} }
updateVis(data: any[]): void { updateVis(data: any[]): void {
...@@ -100,14 +103,20 @@ export default class BubbleChart extends SearchableChart { ...@@ -100,14 +103,20 @@ export default class BubbleChart extends SearchableChart {
const node = vis.chart const node = vis.chart
.selectAll("g") .selectAll("g")
.data(vis.packRoot?.descendants()) .data(vis.packRoot?.descendants())
.join(
(enter: any) => enter.append("g"),
(update: any) => update
.transition() .transition()
.duration(1000) .duration(1000)
.attr("transform", (d: any) => `translate(${d.x},${d.y})`) .attr("transform", (d: any) => `translate(${d.x},${d.y})`),
(exit: any) => exit.remove()
)
node.selectAll('circle') node.selectAll('circle')
.transition() .transition()
.duration(1000) .duration(1000)
.attr('fill', (d: any) => vis.getFillForNode(d)) .attr('fill', (d: any) => vis.getFillForNode(d))
.attr('r', (d: any) => d.r)
} }
renderVis(): void { renderVis(): void {
...@@ -122,14 +131,13 @@ export default class BubbleChart extends SearchableChart { ...@@ -122,14 +131,13 @@ export default class BubbleChart extends SearchableChart {
.attr("transform", (d: any) => `translate(${d.x},${d.y})`) .attr("transform", (d: any) => `translate(${d.x},${d.y})`)
node node
.filter((d: any) => !d.children)
.append('circle') .append('circle')
.attr('fill', (d: any) => vis.getFillForNode(d)) .attr('fill', (d: any) => vis.getFillForNode(d))
.attr('stroke', 'none') .attr('stroke', 'none')
.attr('stroke-width', 2) .attr('stroke-width', 2)
.attr('r', (d: any) => d.r) .attr('r', (d: any) => d.r)
.attr('data-leaf', (d: any) => d.children ? 'false' : 'true')
.attr('data-player', (d: any) => d.data.player) .attr('data-player', (d: any) => d.data.player)
node.selectAll('circle[data-leaf="true"]')
.on('mouseover', (_: Event, d: any) => { .on('mouseover', (_: Event, d: any) => {
vis.renderTooltip(d.data) vis.renderTooltip(d.data)
}) })
...@@ -138,8 +146,7 @@ export default class BubbleChart extends SearchableChart { ...@@ -138,8 +146,7 @@ export default class BubbleChart extends SearchableChart {
.style('left', (event.layerX + vis.config.tooltipPadding) + 'px') .style('left', (event.layerX + vis.config.tooltipPadding) + 'px')
.style('top', (event.layerY + vis.config.tooltipPadding) + 'px') .style('top', (event.layerY + vis.config.tooltipPadding) + 'px')
}) })
.on('mouseleave', (_: Event, d: any) => { .on('mouseleave', (_: Event) => {
console.log(d.data)
d3.select('#tooltip').style('display', 'none'); d3.select('#tooltip').style('display', 'none');
}) })
} }
...@@ -168,10 +175,9 @@ export default class BubbleChart extends SearchableChart { ...@@ -168,10 +175,9 @@ export default class BubbleChart extends SearchableChart {
this.data, this.data,
(d: any) => this.config.groupAccessor(d) (d: any) => this.config.groupAccessor(d)
) )
console.log(groupedData)
this.packRoot = d3.pack() this.packRoot = d3.pack()
.size([this.width(), this.height()]) .size([this.width(), this.height()])
.padding(4) .padding(2)
( (
(d3.hierarchy(groupedData) as HierarchyNode<any>) (d3.hierarchy(groupedData) as HierarchyNode<any>)
.sum((d: any) => this.config.sizeAccessor(d)) .sum((d: any) => this.config.sizeAccessor(d))
...@@ -180,7 +186,8 @@ export default class BubbleChart extends SearchableChart { ...@@ -180,7 +186,8 @@ export default class BubbleChart extends SearchableChart {
private getFillForNode(node: HierarchyNode<any>) { private getFillForNode(node: HierarchyNode<any>) {
if (node.children) return "transparent" if (node.children) return "transparent"
return this.config.colorAccessor(node) ?? 'var(--primary)' if (!this.colorScale) return 'var(--primary)'
return this.colorScale(this.config.colorAccessor(node) as NumberValue)
} }
search(input: string): void { search(input: string): void {
...@@ -193,6 +200,24 @@ export default class BubbleChart extends SearchableChart { ...@@ -193,6 +200,24 @@ export default class BubbleChart extends SearchableChart {
} }
} }
updateColorScale() {
// The color scale is either a sequential scale or an ordinal scale
// depending on what type the colorAccessor returns
const sampleValue = this.config.colorAccessor(this.data[0])
if (!sampleValue) return () => 'var(--primary)'
if (typeof sampleValue === 'number') {
this.colorScale = d3.scaleSequential(d3.interpolateBlues)
.domain(
d3.extent(
this.data,
this.config.colorAccessor as (d: any) => number | null
) as [number, number]
)
} else {
this.colorScale = d3.scaleOrdinal(d3.schemeCategory10)
}
}
get zoomLevel() { get zoomLevel() {
return this.zoom.scale() return this.zoom.scale()
} }
......
...@@ -37,7 +37,8 @@ dsv(';', 'data/output.csv').then(data => { ...@@ -37,7 +37,8 @@ dsv(';', 'data/output.csv').then(data => {
onZoom: (event) => { onZoom: (event) => {
if (sliderBlocked) return if (sliderBlocked) return
zoomSlider.value = event.transform.k.toString() zoomSlider.value = event.transform.k.toString()
} },
groupAccessor: (d: any) => d['team'],
}) })
bubbleChart.renderVis() bubbleChart.renderVis()
......
...@@ -77,25 +77,37 @@ h1 { ...@@ -77,25 +77,37 @@ h1 {
display: flex; display: flex;
gap: 1rem; gap: 1rem;
.left, .right { .left, .right {
position: relative; position: relative;
flex: 1; flex: 1;
text-align: center; text-align: center;
}
.left {
flex: 2;
#bubble-chart-wrapper {
position: relative;
}
#zoom-wrapper { .bubble-chart-settings {
position: absolute; position: absolute;
bottom: 1rem; bottom: 1rem;
right: 0; right: 0;
width: 400px; width: 400px;
display: flex; display: flex;
flex-direction: column;
align-items: flex-end;
justify-content: flex-end;
gap: 1rem;
> div {
align-items: center; align-items: center;
justify-content: flex-end; justify-content: flex-end;
gap: 1rem; gap: 1rem;
} }
} }
.left {
flex: 2;
} }
.right { .right {
...@@ -118,7 +130,7 @@ button { ...@@ -118,7 +130,7 @@ button {
} }
} }
input[type="text"] { select, input[type="text"] {
border-radius: .5rem; border-radius: .5rem;
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1); box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
padding: 0.5rem; padding: 0.5rem;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment