d3js搜索节点及其子节点

问题描述

我的项目是用文本框制作一个力导向图以搜索任何节点。当用户搜索某项内容时,我想突出显示该节点及其邻居,就像将鼠标悬停在某个节点上时here一样。

我的source code

var width = 900,height = 590;

// var zoom = d3.behavior.zoom().scaleExtent([0.1,5]).on("zoom");
var svg = d3.select("body")
  .append("svg")
  .attr("width",width)
  .attr("height",height)
  .append('g');

d3.json("https://raw.githubusercontent.com/khalidal-walid/fyp/master/countries.json",function(data) {

  // Extract the nodes and links from the data.
  var nodes = data["nodes"];
  var links = data["links"];

  //CONNECTIONS
  var hash_lookup = [];
  nodes.forEach(function(d,i) {
    hash_lookup[d.country] = d;
  });
  links.forEach(function(d,i) {
    d.source = hash_lookup[d.source];
    d.target = hash_lookup[d.target];
  });

  // Now we create a force layout object and define its properties.
  // Those include the dimensions of the visualization and the arrays of nodes and links.
  var force = d3.layout.force()
    .size([width,height])
    .nodes(d3.values(nodes))
    .links(links)
    .on('tick',tick)
    .linkDistance(100)
    .gravity(.15)
    .friction(.8)
    .linkStrength(1)
    .charge(-425)
    .chargeDistance(600)
    .start();

  //LINKS
  var link = svg.selectAll('.link')
    .data(links)
    .enter().append('line')
    .attr('class','link');

  //NODES
  var node = svg.selectAll('.node')
    .data(force.nodes())
    .enter().append('circle')
    .attr('class','node')
    .attr('r',width * 0.01)

  //LABELS
  var text_center = false;
  var nominal_text_size = 12;
  var max_text_size = 22;
  var nominal_base_node_size = 8;
  var max_base_node_size = 36;
  var size = d3.scale.pow().exponent(1)
    .domain([1,100])
    .range([8,24]);

  var text = svg.selectAll(".text")
    .data(nodes)
    .enter().append("text")
    .attr("dy",".35em")
    .style("font-size",nominal_text_size + "px")

  if (text_center)
    text.text(function(d) {
      return d.code;
    })
    .style("text-anchor","middle");

  else
    text.attr("dx",function(d) {
      return (size(d.size) || nominal_base_node_size);
    })
    .text(function(d) {
      return '\u2002' + d.code;
    });

  //ZOOM AND PAN
  // function redraw() {
  //     svg.attr("transform",//     "translate(" + d3.event.translate + ")"
  //     + " scale(" + d3.event.scale + ")");
  // }

  // var drag = force.drag()
  //   .on("dragstart",function(d) {
  //     d3.event.sourceEvent.stopPropagation();
  //   });

  //NODES IN SPACE
  function tick(e) {

    text.attr("transform",function(d) {
      return "translate(" + d.x + "," + d.y + ")";
    });

    node.attr('cx',function(d) {
        return d.x;
      })
      .attr('cy',function(d) {
        return d.y;
      })
      .call(force.drag);

    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;
      });

  };

  //AUTOCOMPLETE SEARCH
  var optArray = [];

  for (var i = 0; i < nodes.length - 1; i++) {
    optArray.push(nodes[i].code);
  }

  optArray = optArray.sort();

  window.searchNode = searchNode;

  function searchNode() {
    var selectedVal = document.getElementById('search').value;
    if (selectedVal == 'none') {} else {
      var selected = node.filter(function(d,i) {
        return d.code != selectedVal;
      });
      var selectedText = text.filter(function(d,i) {
        return d.code != selectedVal;
      });
      selected.style("opacity","0");
      selectedText.style("opacity","0");
      var link = svg.selectAll(".link")
      link.style("opacity","0");
      d3.selectAll(".node,.link,.text").transition()
        .duration(3000)
        .style("opacity",'1');

      var selectedNode = node
        .filter(function(d,i) {
          return d.code == selectedVal;
        })
        .datum();

      var scale = zoom.scale();
      var desiredPosition = {
        x: 200,y: 200
      }; // constants,set to svg center point
      zoom.translate([desiredPosition.x - selectedNode.x * scale,desiredPosition.y - selectedNode.y * scale]);
      zoom.event(svg);
    }
  }
})
.node {
  stroke: #aaa;
  stroke-width: 2px;
}

.link {
  stroke: #aaa;
  stroke-width: 2px;
}
<script src='https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js'></script>
<div class="ui-widget">
  <input id="search",size='54',autocomplete="off">
  <button type="button" onclick="searchNode()">Search</button>
</div>

解决方法

我认为以下是您想要的。我首先检查是否有任何节点与搜索词匹配,然后,如果是真的,我将找到所有不包含匹配节点的链接,并将其隐藏。同时,我填充highlightCodes,该数组包含匹配的代码和所有直接邻居的代码。然后,我隐藏所有与highlightCodes数组都不匹配的节点。

var width = 900,height = 590;

// var zoom = d3.behavior.zoom().scaleExtent([0.1,5]).on("zoom");
var svg = d3.select("body")
  .append("svg")
  .attr("width",width)
  .attr("height",height)
  .append('g');

d3.json("https://raw.githubusercontent.com/khalidal-walid/fyp/master/countries.json",function(data) {

  // Extract the nodes and links from the data.
  var nodes = data["nodes"];
  var links = data["links"];

  //CONNECTIONS
  var hash_lookup = [];
  nodes.forEach(function(d,i) {
    hash_lookup[d.country] = d;
  });
  links.forEach(function(d,i) {
    d.source = hash_lookup[d.source];
    d.target = hash_lookup[d.target];
  });

  // Now we create a force layout object and define its properties.
  // Those include the dimensions of the visualization and the arrays of nodes and links.
  var force = d3.layout.force()
    .size([width,height])
    .nodes(d3.values(nodes))
    .links(links)
    .on('tick',tick)
    .linkDistance(100)
    .gravity(.15)
    .friction(.8)
    .linkStrength(1)
    .charge(-425)
    .chargeDistance(600)
    .start();

  //LINKS
  var link = svg.selectAll('.link')
    .data(links)
    .enter()
    .append('line')
    .attr('class','link');

  //NODES
  var node = svg.selectAll('.node')
    .data(force.nodes())
    .enter()
    .append('circle')
    .attr('class','node')
    .attr('r',width * 0.01);

  //LABELS
  var text_center = false;
  var nominal_text_size = 12;
  var max_text_size = 22;
  var nominal_base_node_size = 8;
  var max_base_node_size = 36;
  var size = d3.scale.pow()
    .exponent(1)
    .domain([1,100])
    .range([8,24]);

  var text = svg.selectAll(".text")
    .data(nodes)
    .enter()
    .append("text")
    .attr("dy",".35em")
    .style("font-size",nominal_text_size + "px");

  if(text_center) {
    text.text(function(d) {
      return d.code;
    })
      .style("text-anchor","middle");
  } else {
    text.attr("dx",function(d) {
      return (size(d.size) || nominal_base_node_size);
    })
      .text(function(d) {
        return '\u2002' + d.code;
      });
  }

  //ZOOM AND PAN
  // function redraw() {
  //     svg.attr("transform",//     "translate(" + d3.event.translate + ")"
  //     + " scale(" + d3.event.scale + ")");
  // }

  // var drag = force.drag()
  //   .on("dragstart",function(d) {
  //     d3.event.sourceEvent.stopPropagation();
  //   });

  //NODES IN SPACE
  function tick(e) {

    text.attr("transform",function(d) {
      return "translate(" + d.x + "," + d.y + ")";
    });

    node.attr('cx',function(d) {
      return d.x;
    })
      .attr('cy',function(d) {
        return d.y;
      })
      .call(force.drag);

    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;
      });

  };

  //AUTOCOMPLETE SEARCH
  window.searchNode = searchNode;

  function resetSelection() {
    node.style('opacity',null);
    link.style('opacity',null);
    text.style('opacity',null);
  }

  function searchNode() {
    var selectedVal = document.getElementById('search').value;
    if(selectedVal == 'none') {
      return resetSelection();
    }

    var matchingNode = nodes.find(function(d) {
      return d.code === selectedVal;
    });

    if(matchingNode === undefined) {
      return resetSelection();
    }
    
    var matchingCode = matchingNode.code;

    var highlightCodes = [];

    link
      .filter(function(d) {
        if(d.source.code === matchingCode ||
            d.target.code === matchingCode) {
          highlightCodes.push(d.source.code,d.target.code);
          return false;
        }
        return true;
      })
      .transition()
      .duration(500)
      .style("opacity",0.3);

    d3.selectAll(".node,.text")
      .filter(function(d) {
        return highlightCodes.indexOf(d.code) === -1;
      })
      .transition()
      .duration(500)
      .style("opacity",0.3);
  }
});
.node {
  stroke: #aaa;
  stroke-width: 2px;
}

.link {
  stroke: #aaa;
  stroke-width: 2px;
}
<script src='https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js'></script>
<div class="ui-widget">
  <input id="search" size='54' autocomplete="off">
  <button type="button" onclick="searchNode()">Search</button>
</div>

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...