14

我有一个网络图(力导向图)、一个散点图和一个表,它们都是相互连接的(参见jsFiddle)。我的互连按照我希望它们用于鼠标悬停事件的方式工作。我想修改我的代码,以便当我将鼠标悬停在网络图中的一个节点上时,不仅突出显示鼠标悬停的节点(以及它在散点图和表格中的连接),而且它的直接相邻节点也突出显示(以及作为它们在散点图和表格中的连接)。

我在D3 力有向图中查看了突出显示选定节点、其链接及其子节点中的信息以寻求帮助。一路上的某个地方(不完全确定在哪里)我找到了一个帮助定义连接节点的函数示例,isConnected().

function isConnected(a, b) {
    return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index] || a.index == b.index;
    }

我想将此功能合并到我的鼠标悬停事件中,也许带有一个if()语句,以便我可以做所有我想要的“突出显示”。但是,我是 D3 和 js 的新手,不知道如何设置它。

下面是我想修改的代码片段(来自jsFiddle )。对于其他示例的任何建议或指示,我将不胜感激。

var node = svg.selectAll(".node")
    .data(graph.nodes)
    .enter().append("g")
    .attr("class", function(d) { return "node " + d.name + " " + d.location; })
    .call(force.drag)
    .on("mouseover", function(d) { 
        // I would like to insert an if statement to do all of these things to the connected nodes
        // if(isConnected(d, o)) {
            d3.select(this).select("circle").style("stroke-width", 6); 
            d3.select(this).select("circle").style("stroke", "orange"); 
            d3.select(this).select("text").style("font", "20px sans-serif");
            d3.selectAll("rect." + d.location).style("stroke-width", 6);
            d3.selectAll("rect." + d.location).style("stroke", "orange");
            d3.selectAll("text." + d.location).style("font", "20px sans-serif");
            d3.selectAll("tr." + d.name).style("background-color", "orange");
            //}
        })
    .on("mouseout",  function(d) { 
        // if(isConnected(d, o)) {
            d3.select(this).select("circle").style("stroke-width", 1.5); 
            d3.select(this).select("circle").style("stroke", "gray"); 
            d3.select(this).select("text").style("font", "12px sans-serif");
            d3.selectAll("rect." + d.location).style("stroke-width", 1.5);
            d3.selectAll("rect." + d.location).style("stroke", "gray");
            d3.selectAll("text." + d.location).style("font", "12px sans-serif");
            d3.selectAll("tr." + d.name).style("background-color", "white");
            //}
        });
4

2 回答 2

5

在另一种情况下,我会将我的可视对象放入图形数据结构中并对其进行导航以有效地更新适当的项目。但这是 d3,所以我们将做同样的事情,但我们将使用 d3 选择而不是我们创建的图形数据结构(它可以像图形,但为此它们看起来更像数组)。从算法上讲,这种方法不会那么有效,但我们的图很小。

因此,向后工作我将需要一个仅包含所选节点的相邻节点的选择。我将通过选择所有圆圈然后使用 d3 选择过滤器方法将其减少到只有那些是邻居的圆圈来做到这一点。

当然,我需要邻居列表,但是一些不错的 js 数组方法可以轻松解决这个问题。最终的相关代码(在鼠标悬停中)甚至没有那么长 - 但我添加了一堆评论:

// Figure out the neighboring node id's with brute strength because the graph is small
var nodeNeighbors = graph.links.filter(function(link) {
    // Filter the list of links to only those links that have our target 
    // node as a source or target
    return link.source.index === d.index || link.target.index === d.index;})
.map(function(link) {
    // Map the list of links to a simple array of the neighboring indices - this is
    // technically not required but makes the code below simpler because we can use         
    // indexOf instead of iterating and searching ourselves.
    return link.source.index === d.index ? link.target.index : link.source.index; });

// Reset all circles - we will do this in mouseout also
svg.selectAll('circle').style('stroke', 'gray');

// now we select the neighboring circles and apply whatever style we want. 
// Note that we could also filter a selection of links in this way if we want to 
// Highlight those as well
svg.selectAll('circle').filter(function(node) {
    // I filter the selection of all circles to only those that hold a node with an
    // index in my listg of neighbors
    return nodeNeighbors.indexOf(node.index) > -1;
})
.style('stroke', 'orange');

你也可以试试小提琴

我认为与此处相关的重要 d3 概念是,当您将数据与元素相关联时(通常在选择上使用 data() 或 datum() 方法),那么该数据会与该元素保持一致,并且任何未来的选择都将始终使用它。

要链接其他方面,您可以以类似的方式提取这些属性并通过 d3 链接它们。例如,对于可以添加到鼠标悬停的位置矩形:

var nodeLocations = graph.links.filter(function(link) {
        return link.source.index === d.index || link.target.index === d.index;})
    .map(function(link) {
        return link.source.index === d.index ? link.target.location : link.source.location; });

d3.selectAll("rect").filter(function(node) { return nodeLocations.indexOf(node.location) > -1; }) .style("stroke", "cyan");
于 2013-06-21T17:34:30.457 回答
0

我构建的这个东西是通过 Ego Network 功能实现的:

https://gist.github.com/emeeks/4588962

将 .on("mouseover", findEgo) 添加到您的节点,只要您有某种识别 uid 属性,以下应该可以工作,如果不方便,您可以在加载节点时生成该属性。这有点矫枉过正,因为它允许 n 度自我网络,并为其他网络分析功能创建聚合表,但基本功能将为您提供您想要的,您或其他用户可能会发现这方面很有用:

 function findEgo(d) {  
  var computedEgoArray = findEgoNetwork(d.id, 1, false,"individual");
  d3.selectAll("circle.node").style("fill", function(p) {return p.id == d.id ? "purple" : computedEgoArray.indexOf(p.id) > -1 ? "blue" : "pink"})
 }

 function findEgoNetwork(searchNode, egoNetworkDegree, isDirected, searchType) {
  var egoNetwork = {};
  for (x in nodes) {
  if (nodes[x].id == searchNode || searchType == "aggregate") {
   egoNetwork[nodes[x].id] = [nodes[x].id];
   var z = 0;
   while (z < egoNetworkDegree) {
    var thisEgoRing = egoNetwork[nodes[x].id].slice(0);
    for (y in links) {
     if (thisEgoRing.indexOf(links[y].source.id) > -1 && thisEgoRing.indexOf(links[y].target.id) == -1) {
     egoNetwork[nodes[x].id].push(links[y].target.id)
     }
    else if (isDirected == false && thisEgoRing.indexOf(links[y].source.id) == -1 && thisEgoRing.indexOf(links[y].target.id) > -1) {
    egoNetwork[nodes[x].id].push(links[y].source.id)
    }
 }
 z++;
 }
 }
 }
 if (searchType == "aggregate") {
 //if it's checking the entire network, pass back the entire object of arrays
 return egoNetwork;
 }
 else {
 //Otherwise only give back the array that corresponds with the search node
 return egoNetwork[searchNode];
 }
 }
于 2013-06-08T02:43:50.157 回答