d3.js - select force layout group not update the nodes

Viewed 56

Below example should update the nodes after select the groups at the bottom. but the select works fine, the nodes on the svg not updated!

force_json()
function force_json(){
  var svg = d3.select('body').append('svg')
  .attr('width',200).attr('height',100)
  .style('border','1px solid red'),
      width = +svg.attr("width"),
      height = +svg.attr("height");

  var simulation = d3.forceSimulation()
  .force("link", d3.forceLink().id(function(d) { return d.id; }))
  .force("charge", d3.forceManyBody())
  .force("center", d3.forceCenter(width / 2, height / 2));

  var color = d3.scaleOrdinal(d3.schemeCategory10);

  var jsonobj = {
    "nodes": [
      {"id": "Myriel", "group": 1},
      {"id": "Napoleon", "group": 2},
      {"id": "Mlle.Baptistine", "group": 3},
      {"id": "Mme.Magloire", "group": 1},
    ],
    "links": [
      {"source": "Napoleon", "target": "Myriel", "value": 10},
      {"source": "Mlle.Baptistine", "target": "Myriel", "value": 8},
      {"source": "Mme.Magloire", "target": "Myriel", "value": 10},
    ]
  }

  // d3.json("miserables.json",function(error, graph) {
  //   if (error) throw error;

  // });
  process_data(jsonobj)
  function process_data(graph) {
    var currNodes = graph.nodes
    var currLinks = graph.links

    var nodesByGroup = d3.group(graph.nodes,d => d.group)

    var catMenu = d3.select("body").append('div')
    catMenu
      .append("select")
      .selectAll("option")
      .data(nodesByGroup)
      .enter()
      .append("option")
      .attr("value", function(d,i) {
      return d[0];
    })
      .text(function(d,i){
      return d[0];
    })

    var link = svg.append("g")
    .attr("class", "links")
    .selectAll("line")
    .data(currLinks)
    .enter().append("line")
    .attr('stroke','#aaa')

    var node = svg.append("g")
    .attr("class", "nodes")
    .selectAll("circle")
    .data(currNodes)
    .enter().append("circle")
    .attr("r", 5)
    .attr('pointer-events','all')
    .attr('stroke','none')
    .attr('stroke-wdith',40)
    .attr("fill", function(d) { return color(d.group);})
    .call(d3.drag()
          .on("start", dragstarted)
          .on("drag", dragged)
          .on("end", dragended));

    node.append("title")
      .text(function(d) { return d.id; });

    node.append("text")
      .attr("dx", 12)
      .attr("dy", ".35em")
      .text(function(d) {return d.id;})

    simulation
      .nodes(currNodes)
      .on("tick", ticked);

    simulation.force("link")
      .links(graph.links);

    function ticked() {
      link
        .attr("x1", function(d) { return d.source.x; })
        .attr("y1", function(d) { return d.source.y; })
        .attr("x2", function(d) { return d.target.x; })
        .attr("y2", function(d) { return d.target.y; });

      node
        .attr("cx", function(d) { return d.x; })
        .attr("cy", function(d) { return d.y; });
    }

    catMenu.on('change', function(){
      var selectedGroup = +d3.select(this)
      .select("select")
      .property("value");
      currNodes = filterNodes(selectedGroup);
    });

    function filterNodes(group) {
      var filteredNodes = nodesByGroup.get(group)
      return filteredNodes;
    }
  }

  function dragstarted(event,d) {
    if (!event.active) simulation.alphaTarget(0.3).restart();
    d.fx = d.x;
    d.fy = d.y;
  }

  function dragged(event,d) {
    d.fx = event.x;
    d.fy = event.y;
  }

  function dragended(event,d) {
    if (!event.active) simulation.alphaTarget(0);
    d.fx = null;
    d.fy = null;
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>

1 Answers

Re-render filtered nodes and links upon group selection:

force_json()

function force_json(){
  var svg = d3.select('body').append('svg')
  .attr('width',200).attr('height',200)
  .style('border','1px solid red'),
      width = +svg.attr("width"),
      height = +svg.attr("height");

  var color = d3.scaleOrdinal(d3.schemeCategory10);

  var jsonobj = {
    "nodes": [
      {"id": "Myriel", "group": 1},
      {"id": "Napoleon", "group": 2},
      {"id": "Mlle.Baptistine", "group": 3},
      {"id": "Mme.Magloire", "group": 1},
      {"id": "A", "group": 2},
      {"id": "B", "group": 3},
      {"id": "C", "group": 3}
    ],
    "links": [
      {"source": "Napoleon", "target": "Myriel", "value": 10},
      {"source": "Napoleon", "target": "A", "value": 10},
      {"source": "Mlle.Baptistine", "target": "Myriel", "value": 8},
      {"source": "Mlle.Baptistine", "target": "B", "value": 8},
      {"source": "Mme.Magloire", "target": "Myriel", "value": 10},
      {"source": "A", "target": "B", "value": 10},
      {"source": "C", "target": "B", "value": 10},
    ]
  }

  process_data(jsonobj)
  
  function process_data(graph) {
    var nodesByGroup = d3.group(graph.nodes,d => d.group)
    
    var catMenu = d3.select("body").append('div')
    catMenu
      .append("select")
      .selectAll("option")
      .data(nodesByGroup)
      .enter()
      .append("option")
      .attr("value", function(d,i) {
      return d[0];
    })
      .text(function(d,i){
      return d[0];
    })
    catMenu.select('select').append('option').text('all').attr("selected", "selected");

  const updateGraph = (nodes, links) => {
    const simulation = d3.forceSimulation()
  .force("link", d3.forceLink().id(function(d) { return d.id; }))
  .force("charge", d3.forceManyBody())
  .force("center", d3.forceCenter(width / 2, height / 2));
    
    svg.selectAll('g').remove();
    var link = svg.append("g")
    .attr("class", "links")
    .selectAll("line")
    .data(links)
    .enter().append("line")
    .attr('stroke','#aaa')

    var node = svg.append("g")
    .attr("class", "nodes")
    .selectAll("circle")
    .data(nodes)
    .enter().append("circle")
    .attr("r", 5)
    .attr('pointer-events','all')
    .attr('stroke','none')
    .attr('stroke-wdith',40)
    .attr("fill", function(d) { return color(d.group);})
    .call(d3.drag()
          .on("start", dragstarted)
          .on("drag", dragged)
          .on("end", dragended));

    node.append("title")
      .text(d => d.id);

    node.append("text")
      .attr("dx", 12)
      .attr("dy", ".35em")
      .text(function(d) {return d.id;})

    simulation
      .nodes(nodes)
      .on("tick", ticked);

    simulation.force("link")
      .links(links);

    function ticked() {
      link
        .attr("x1", function(d) { return d.source.x; })
        .attr("y1", function(d) { return d.source.y; })
        .attr("x2", function(d) { return d.target.x; })
        .attr("y2", function(d) { return d.target.y; });

      node
        .attr("cx", function(d) { return d.x; })
        .attr("cy", function(d) { return d.y; });
    }

  function dragstarted(event,d) {
    if (!event.active) simulation.alphaTarget(0.3).restart();
    d.fx = d.x;
    d.fy = d.y;
  }

  function dragged(event,d) {
    d.fx = event.x;
    d.fy = event.y;
  }

  function dragended(event,d) {
    if (!event.active) simulation.alphaTarget(0);
    d.fx = null;
    d.fy = null;
  }
}

    updateGraph(graph.nodes, graph.links);
    catMenu.on('change', function(){
      var selectedGroup = d3.select(this)
      .select("select")
      .property("value");
      
      let nodes, links;
      if (selectedGroup === 'all') {
        nodes = graph.nodes;
        links = graph.links;
      } else {
        const group = parseInt(selectedGroup);
        nodes = graph.nodes.filter(n => n.group === group);
        console.log(graph.links)
        links = graph.links.filter(link => {
        const source = nodes.find(n => n.id === link.source.id);
        const target = nodes.find(n => n.id === link.target.id);
        return source && target;
        })
      }
       updateGraph(nodes, links);
    });
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>

Related