我希望在子节点悬停时突出显示图中父节点的链接和节点。我从“纽约时报”'Paths to the white house'获取灵感:
我已经看到了this question使用this Fiddle的答案:
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) {
// if(isConnected(d, o)) {
d3.select(this).select("circle").style("stroke-width", 6);
var nodeNeighbors = 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.index : link.source.index;
});
svg.selectAll('circle').style('stroke', 'gray');
svg.selectAll('circle').filter(function(node) {
return nodeNeighbors.indexOf(node.index) > -1;
})
// }
.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");
// }
})
.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");
// }
});
虽然他们正在使用源代码和目标代码,但我想知道使用父代和子代的网络图(强制导向图)是否也可以,以及如何做到这一点?
答案 0 :(得分:2)
通过调整this example中的功能,我做了类似的事情。诀窍是构建仅适用于您要突出显示的链接的选择。这是我的代码片段:
function linkMouseover(d){
chart.selectAll(".node").classed("active", function(p) { return d3.select(this).classed("active") || p === d.source || p === d.target; });
}
// Highlight the node and connected links on mouseover.
function nodeMouseover(d) {
chart.selectAll(".link").classed("active", function(p) { return d3.select(this).classed("active") || p.source === d || p.target === d; });
chart.selectAll(".link.active").each(function(d){linkMouseover(d)})
d3.select(this).classed("active", true);
}
这个force-directed example使用术语源和目标 - 我不认为源目标和父子之间有很多区别。您应该能够通过编辑上面的内容来使其工作,这样.each()
和.classed()
回调只能在突出显示的节点,它的(多代)子节点以及它们之间的链接上运行。
答案 1 :(得分:-2)
Complete code from fiddle:
var graph = {"nodes": [
{"name": "Betty", "size": 12, "company": "X", "location": "ChicagoIL"},
{"name": "Frank", "size": 12, "company": "Y", "location": "NewYorkNY"},
{"name": "Jim", "size": 12, "company": "Y", "location": "LosAngelesCA"},
{"name": "Sally", "size": 12, "company": "Z", "location": "TorontoON"},
{"name": "Tom", "size": 12, "company": "X", "location": "ChicagoIL"},
{"name": "GroupA", "size": 18, "company": "T", "location": "x"},
{"name": "GroupB", "size": 18, "company": "T", "location": "x"}],
"links": [
{"source": 2, "target": 5, "bond": 1},
{"source": 3, "target": 5, "bond": 2},
{"source": 4, "target": 5, "bond": 1},
{"source": 0, "target": 6, "bond": 1},
{"source": 1, "target": 6, "bond": 2},
{"source": 4, "target": 6, "bond": 1}],
"locs": [
{"lat": 40.7142, "long": -74.0064, "location": "NewYorkNY"},
{"lat": 34.0522, "long": -118.2428, "location": "LosAngelesCA"},
{"lat": 43.6481, "long": -79.4042, "location": "TorontoON"},
{"lat": 41.8500, "long": -87.6500, "location": "ChicagoIL"}]};
var width = 250;
var height = 250;
var padding = 20;
var color = d3.scale.category20();
var radius = d3.scale.sqrt()
.range([0, 7]);
var scale = d3.scale.linear();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var svg2 = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var force = d3.layout.force()
.size([width, height])
.charge(-400)
.linkStrength(3)
.linkDistance(function(d) {
return radius(d.source.size) + radius(d.target.size) + (30 / (2*d.bond-1))
; });
// network diagram stuff
force
.nodes(graph.nodes)
.links(graph.links)
.on("tick", tick)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("g")
.attr("class", "link");
link.append("line")
.style("stroke-width", function(d) { return (d.bond * 2 - 1) * 3 + "px"; });
link.filter(function(d) { return d.bond > 1; }).append("line")
.attr("class", "separator");
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);
// 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.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)) {
svg.selectAll('circle').style('stroke', 'gray');
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");
//}
});
node.append("circle")
.attr("r", function(d) { return radius(d.size); })
.style("fill", function(d) { return color(d.name); });
node.append("text")
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) { return d.name; });
var linkedByIndex = {};
graph.links.forEach(function(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 tick() {
link.selectAll("line")
.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("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")"; });
}
// map stuff
var xScale = d3.scale.linear()
.domain([d3.min(graph.locs, function(d) { return d.long; }), d3.max(graph.locs, function(d) { return d.long; })])
.range([padding, width-(4*padding)]);
var yScale = d3.scale.linear()
.domain([d3.min(graph.locs, function(d) { return d.lat; }), d3.max(graph.locs, function(d) { return d.lat; })])
.range([height-padding, padding]);
var mapit = svg2.selectAll(".mapit")
.data(graph.locs)
.enter().append("g")
.on("mouseover", function(d) {
d3.select(this).select("rect").style("stroke-width", 6);
d3.select(this).select("rect").style("stroke", "orange");
d3.select(this).select("text").style("font", "20px sans-serif");
d3.selectAll("g." + d.location + " circle").style("stroke-width", 6);
d3.selectAll("g." + d.location + " circle").style("stroke", "orange");
d3.selectAll("g." + d.location + " text").style("font", "20px sans-serif");
d3.selectAll("tr." + d.location).style("background-color", "orange");
})
.on("mouseout", function(d) {
d3.select(this).select("rect").style("stroke-width", 1.5);
d3.select(this).select("rect").style("stroke", "gray");
d3.select(this).select("text").style("font", "12px sans-serif");
d3.selectAll("g." + d.location + " circle").style("stroke-width", 1.5);
d3.selectAll("g." + d.location + " circle").style("stroke", "gray");
d3.selectAll("g." + d.location + " text").style("font", "12px sans-serif");
d3.selectAll("tr." + d.location).style("background-color", "white");
});
mapit.append("rect")
.attr("x", function(d) { return xScale(d.long); })
.attr("y", function(d) { return yScale(d.lat); })
.attr("height", 20)
.attr("width", 20)
.attr("fill", "gray")
.attr("stroke-width", "1.5")
.attr("stroke", "gray")
.attr("class", function(d) { return d.location ;});
mapit.append("text")
.attr("x", function(d) { return xScale(d.long); })
.attr("y", function(d) { return yScale(d.lat) -5; })
.attr("font-family", "sans-serif")
.attr("font-size", "12px")
.attr("fill", "black")
.text(function(d) { return d.location; })
.attr("class", function(d) { return d.location ;});
// table stuff
function tabulate(tdata, columns) {
var table = d3.select("body").append("table"),
thead = table.append("thead"),
tbody = table.append("tbody");
thead.append("tr").selectAll("th")
.data(columns)
.enter().append("th")
.text(function(column) { return column; });
var rows = tbody.selectAll("tr")
.data(tdata)
.enter().append("tr")
.attr("class", function(d) { return d.name + " " + d.location ;})
.on("mouseover", function(d){
d3.select(this).style("background-color", "orange");
d3.selectAll("g." + d.name + " circle").style("stroke-width", 6);
d3.selectAll("g." + d.name + " circle").style("stroke", "orange");
d3.selectAll("g." + d.name + " 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");
})
.on("mouseout", function(d){
d3.select(this).style("background-color", "white");
d3.selectAll("g." + d.name + " circle").style("stroke-width", 1);
d3.selectAll("g." + d.name + " circle").style("stroke", "gray");
d3.selectAll("g." + d.name + " 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");
});
var cells = rows.selectAll("td")
.data(function(row) {
return columns.map(function(column) {
return {column: column, value: row[column]};
});
})
.enter().append("td")
.text(function(d) { return d.value; });
return table;
}
var subdata = graph.nodes.filter(function(d) { return d.location!="x"; });
var myTable = tabulate(subdata, ["name", "company", "location"]);
myTable.selectAll("thead th")
.text(function(column) {
return column.charAt(0).toUpperCase() + column.substr(1);
});
myTable.selectAll("tbody tr")
.sort(function(a, b) {
return d3.ascending(a.name, b.name);
});