D3 Node radius depends on number of links : weight property

14,529

d3.v4 does not support weight property. So I think you will have to calculate the node weight by yourselves. try this way.

node.append("circle")
   .attr("r", function(d) {      
     d.weight = link.filter(function(l) {
       return l.source.index == d.index || l.target.index == d.index
     }).size();      
     var minRadius = 10;
     return minRadius + (d.weight * 2);
   });

In d3.v3, we have weight property and can be used as shown below.

node.append("circle")
  .attr("r", function(d) {
    var minRadius = 10;
    return minRadius + (d.weight * 2);
  });

Fiddle Example - https://jsfiddle.net/gilsha/9d6edrte/

Share:
14,529
Inchara Raveendra
Author by

Inchara Raveendra

Updated on August 21, 2022

Comments

  • Inchara Raveendra
    Inchara Raveendra over 1 year

    I am trying to create a force directed graph with D3. As for now, the radius of the node depends on a key-value pair in JSON ( d.size )

    I'm aware of the d3.weight property which can be used to count the number of links and associate with radius attribute of the circle, but I somehow could not get it to work.

    Please help me with this.

    Find the code below:

    d3.json('graph.json', (error, graph) => {
      const width = 1200;
      const height = 900;
    
      const simulation = d3.forceSimulation()
        .nodes(graph.nodes)
        .force('link', d3.forceLink().id(d => d.id))
        .force('charge', d3.forceManyBody().strength([-605]))
        .force('center', d3.forceCenter(width / 2, height / 2))
        .on('tick', ticked);
    
      simulation.force('link')
        .links(graph.links)
        .distance([140]);
    
      const R = 30;
    
      const svg = d3.select('body').append('svg')
        .attr('width', width)
        .attr('height', height);
    
      // add defs-marker
      // add defs-markers
      svg.append('svg:defs').selectAll('marker')
        .data([{ id: 'end-arrow', opacity: 1 }, { id: 'end-arrow-fade', opacity: 0.1 }])
        .enter().append('marker')
          .attr('id', d => d.id)
          .attr('viewBox', '0 0 10 10')
          .attr('refX', 2 * R)
          .attr('refY', 5)
          .attr('markerWidth', 4)
          .attr('markerHeight', 4)
          .attr('orient', 'auto')
          .append('svg:path')
            .attr('d', 'M0,0 L0,10 L10,5 z')
            .style('opacity', d => d.opacity);
    
      let link = svg.selectAll('line')
        .data(graph.links)
        .enter().append('line');
    
      link  
        .attr('class', 'link')
        .attr('marker-end', 'url(#end-arrow)')
        .on('mouseout', fade(1));
    
      let node = svg.selectAll('.node')
        .data(graph.nodes)
        .enter().append('g')
        .attr('class', 'node');
    
      node.append('circle')
        .attr('r', function (d) {
                    return (d.size * 12);
                })
        .on('mouseover', fade(0.1))
        .on('mouseout', fade(1))
        .call(d3.drag()
          .on("start", dragstarted)
          .on("drag", dragged)
          .on("end", dragended));
    
      node.append('text')
        .attr('x', 0)
        .attr('dy', '.35em')
        .text(d => d.name);
    
      function ticked() {
        link
          .attr('x1', d => d.source.x)
          .attr('y1', d => d.source.y)
          .attr('x2', d => d.target.x)
          .attr('y2', d => d.target.y);
    
        node
          .attr('transform', d => `translate(${d.x},${d.y})`);
      }
    
    
      function dragstarted(d) {
        if (!d3.event.active) simulation.alphaTarget(0.3).restart();
        d.fx = d.x;
        d.fy = d.y;
      }
    
      function dragged(d) {
        d.fx = d3.event.x;
        d.fy = d3.event.y;
      }
    
      function dragended(d) {
        if (!d3.event.active) simulation.alphaTarget(0);
        d.fx = null;
        d.fy = null;
      }
    
      const linkedByIndex = {};
      graph.links.forEach(d => {
        linkedByIndex[`${d.source.index},${d.target.index}`] = 1;
      });
    
      function isConnected(a, b) {
        return linkedByIndex[`${a.index},${b.index}`] || linkedByIndex[`${b.index},${a.index}`] || a.index === b.index;
      }
    
      function fade(opacity) {
        return d => {
          node.style('stroke-opacity', function (o) {
            const thisOpacity = isConnected(d, o) ? 1 : opacity;
            this.setAttribute('fill-opacity', thisOpacity);
            return thisOpacity;
          });
    
          link.style('stroke-opacity', o => (o.source === d || o.target === d ? 1 : opacity));
          link.attr('marker-end', o => (opacity === 1 || o.source === d || o.target === d ? 'url(#end-arrow)' : 'url(#end-arrow-fade)'));
        };
      }
    })
    

    The JSON structure is as below:

    {
        "nodes": [
            {
                "name": "A",
                "id": 0,
                "size": 1
            },
            {
                "name": "D",
                "id": 1,
                "size": 2
            },
            {
                "name": "K",
                "id": 2,
                "size": 3
            }
        ],
        "links": [
            {
                "source": 0,     //id of the soure application
                "target": 1      //id of the destination application
            },
            {
                "source": 0,
                "target": 2
            },
            {
                "source": 3,
                "target": 4
            }
        ]
    }
    
    • Gerardo Furtado
      Gerardo Furtado about 7 years
      "I'm aware of the d3.weight property which can be used to count the number of links"... well, there is no d3.weight in D3 v4.x.
    • Inchara Raveendra
      Inchara Raveendra about 7 years
      @GerardoFurtado Thank you for that information! It really helped :)
  • Inchara Raveendra
    Inchara Raveendra about 7 years
    Thanks for your response. The first solution did not work for me unforunately!
  • Gilsha
    Gilsha about 7 years
    I created a fiddle and seems to work fine with the random data. jsfiddle.net/gilsha/n4m1r8nb/214
  • Inchara Raveendra
    Inchara Raveendra about 7 years
    I have another issue with the code. If there exists a node which is not connected to any other node, it will not appear (slides away from the screen). How do i make those nodes stay in view or maybe float next to the other nodes without an interconnection?
  • Gilsha
    Gilsha about 7 years
    Should be a problem with the charge you apply to the force layout. Try adjusting that.
  • Inchara Raveendra
    Inchara Raveendra about 7 years