Home > Blockchain >  Concentric circles in d3 force graph
Concentric circles in d3 force graph

Time:11-17

I'm trying to create a d3 force graph where each of the nodes is two concentric circles. I can do it without any issues with only one circle. I undestand I have to group each of the two circles, but I'm not sure what I'm doing wrong.

This is what my code looks like:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>D3 Force Graph</title>
    <script src="d3.v5.9.7.js"></script>
</head>

<body>
    <div id="viz"></div>

    <script>
        // Utils
        function createLinksArray(agentNodes) {
            const n = agentNodes.length;
            let linksArray = [];
            agentNodes.map((agent, i) => {
                linksArray.push({ source: 0, target: i   1, weight: agent.packetsIn   agent.packetsOut, ...agent })
            })
            return linksArray;
        }

        // Constants ---
        const forceManyBody = -1000;
        const distance = 140;
        const styles = {
            switch: {
                size: 60,
                fill: '#bbb',
                strokeColor: 'green',
                strokeWidth: 4
            },
            agent: {
                size: 25,
                fill: 'white',
                strokeColor: '#bbb',
                strokeWidth: 2
            }
        }
        const linkStyles = {
            onlineColor: 'green',
            offlineColor: 'gray',
            strokeWidth: 2,
        }

        // Chart container dimensions ---
        let svgWidth = 600, svgHeight = 600;

        // Main SVG container ---
        let svg = d3.select('#viz')
            .append('svg')
            .attr('width', svgWidth)
            .attr('height', svgHeight)
            .append('g');

        // Data binding ---
        d3.json('data.json').then(data => {

            // Format data structure ---
            const switchNode = {
                d3id: 0,
                type: 'switch',
                name: data.selectedSwitch.sdmcSwitch.instanceName
            };
            const agentsNodes = data.agentsState.rates.agentsRates.map((x, i) => ({ d3id: i   1, type: 'agent', name: x.agentName, ...x }));
            let NODES = [switchNode, ...agentsNodes];
            let LINKS = createLinksArray(agentsNodes);

            // Scale domain and range
            const linkWeightScale = d3.scaleLinear()
                .domain([0, d3.max(LINKS.map(link => link.weight))])
                .range([1, 10]);

            // SVGs creation ---
            const linkSvg = svg
                .selectAll('path')
                .data(LINKS)
                .enter()
                .append('path')
                .attr('stroke', linkStyles.onlineColor)
                .attr('stroke-width', d => linkWeightScale(d.weight));
        

            let nodeSvg2 = svg
                .selectAll('g')
                .data(NODES)
                .enter()
                .append('g');

                nodeSvg2
                .append('circle')
                .attr('r', d => styles[d.type].size)
                .style('fill', d => styles[d.type].fill);
                nodeSvg2
                    .append('circle')
                    .attr('r', d => styles[d.type].size*0.9)
                    .attr('fill', 'green')
                    .attr('stroke', 'black');


            const textSvg = svg
                .selectAll("text")
                .data(NODES)
                .enter()
                .append("text")
                .attr("text-anchor", "middle")
                .attr("alignment-baseline", "middle")
                .style('font-size', 20)
                .style('font-family', 'sans-serif')
                .text((node) => node.name);


            // Set up graph simulation ---
            const simulation = d3.forceSimulation(NODES)
                .force("charge", d3.forceManyBody().strength(forceManyBody))
                .force("link", d3.forceLink(LINKS).id(d => d.d3id).distance(distance))
                .force("center", d3.forceCenter(svgWidth / 2, svgHeight / 2));

            simulation.on("tick", () => {
                nodeSvg2
                    .attr("cx", d => d.x).attr("cy", d => d.y);
                linkSvg
                    .attr("d", d => d3.line()([
                        [d.source.x, d.source.y],
                        [d.target.x, d.target.y]
                    ]));
                textSvg
                    .attr('x', d => d.x).attr('y', d => d.y);
            })
        });
    </script>
</body>

</html>

And the screenshot shows what's happening, all circles are piled at (0,0) What's happening?

enter image description here

CodePudding user response:

nodeSvg2 is a group so you need to supply a transform/translate.

            nodeSvg2
                .attr('transform', d => 'translate('   d.x   ','   d.y   ')')

The cx and cy attributes won't work on the group element.

  • Related