I've been trying to solve this graph problem for a couple weeks now... still pretty new to D3.js, so things that seem like they might be simple still elude me.
Here's an illustration of what I'm trying to do:
The Goals:
I want to show relationships between Industry Nodes/Size Nodes and Product Nodes.
When I hover a Product node, I want to highlight the Link, Source (Industry or Size) and Target (Product) of each relevant relationship.
When I hover an Industry or Size Node, I want to highlight it's links to all relevant Products.
The Questions
How do I draw the links? I know it somehow involves using d3.map... but can't figure it out.
How do I highlight the nodes and links (goals 2 and 3)?
If there is a better, more efficient way of getting this layout and behavior, please let me know - trying hard to learn the tricks!
The Fiddle renders the basic layout from a simplified set of data: http://jsfiddle.net/9hGbD/
The Data currently looks like this:
var data = {
"Product": [
{
"type": "product",
"name": "Product 1"
},
{
"type": "product",
"name": "Product 2"
},
{
"type": "product",
"name": "Product 3"
},
{
"type": "product",
"name": "Product 4"
},
{
"type": "product",
"name": "Product 5"
}
],
"Industry": [
{
"type": "industry",
"name": "Industry 1"
},
{
"type": "industry",
"name": "Industry 2"
},
{
"type": "industry",
"name": "Industry 3"
},
{
"type": "industry",
"name": "Industry 4"
},
{
"type": "industry",
"name": "Industry 5"
}
],
"Size": [
{
"type": "size",
"name": "Size 1"
},
{
"type": "size",
"name": "Size 2"
},
{
"type": "size",
"name": "Size 3"
},
{
"type": "size",
"name": "Size 4"
},
{
"type": "size",
"name": "Size 5"
}
],
"links": [
{
"source": "Industry 1",
"target": "Product 1"
},
{
"source": "Industry 3",
"target": "Product 1"
},
{
"source": "Industry 5",
"target": "Product 1"
},
{
"source": "Industry 2",
"target": "Product 2"
},
...etc..
]
};
The javascript I'm using looks like this:
function renderRelationshipGraph(){
var width = 800,
boxWidth = 200,
boxHeight = 20,
gap = 4,
margin = {top: 16, right: 16, bottom: 16, left: 16},
height = (data.Product.length * (boxHeight + gap)) + margin.top + margin.bottom;
var pNodes = [];
var iNodes = [];
var sNodes = [];
var links = [];
data.Product.forEach(function(d, i) {
d.x = ((width-margin.left-margin.right)/3)/2 - boxWidth/2;
d.y = margin.top + (boxHeight+ 4)*i;
pNodes.push(d);
});
data.Industry.forEach(function(d, i) {
d.x = 0;
d.y = margin.top + (boxHeight+ 4)*i;
iNodes.push(d);
});
data.Size.forEach(function(d, i) {
d.x = ((width-margin.left-margin.right)/3) - boxWidth;
d.y = margin.top + (boxHeight+ 4)*i;
sNodes.push(d);
});
var svg = d3.select("#graph").append("svg")
.attr("width", width)
.attr("height", height)
.append("g");
svg.append("g")
.attr("class", "industries");
svg.append("g")
.attr("class", "products")
.attr("transform", "translate("+ (width-margin.left-margin.right)/3 + ", 0)");
svg.append("g")
.attr("class", "sizes")
.attr("transform", "translate("+ 2*((width-margin.left-margin.right)/3) + ", 0)");
var products = svg.select(".products");
var product = products.selectAll("g")
.data(pNodes)
.enter()
.append("g")
.attr("class", "unit");
product.append("rect")
.attr("x", function(d) {return d.x;})
.attr("y", function(d) {return d.y;})
.attr("width", boxWidth)
.attr("height", boxHeight)
.attr("class", "product")
.attr("rx", 6)
.attr("ry", 6)
.on("mouseover", function() { d3.select(this).classed("active", true); })
.on("mouseout", function() { d3.select(this).classed("active", false); });
product.append("text")
.attr("class", "label")
.attr("x", function(d) {return d.x + 14;})
.attr("y", function(d) {return d.y + 15;})
.text(function(d) {return d.name;});
var industries = svg.select(".industries");
var industry = industries.selectAll("g")
.data(iNodes)
.enter()
.append("g")
.attr("class", "unit");
industry.append("rect")
.attr("x", function(d) {return d.x;})
.attr("y", function(d) {return d.y;})
.attr("width", boxWidth)
.attr("height", boxHeight)
.attr("class", "industry")
.attr("rx", 6)
.attr("ry", 6)
.on("mouseover", function() { d3.select(this).classed("active", true); })
.on("mouseout", function() { d3.select(this).classed("active", false); });
industry.append("text")
.attr("class", "label")
.attr("x", function(d) {return d.x + 14;})
.attr("y", function(d) {return d.y + 15;})
.text(function(d) {return d.name;});
var sizes = svg.select(".sizes");
var size = sizes.selectAll("g")
.data(sNodes)
.enter()
.append("g")
.attr("class", "unit");
size.append("rect")
.attr("x", function(d) {return d.x;})
.attr("y", function(d) {return d.y;})
.attr("width", boxWidth)
.attr("height", boxHeight)
.attr("class", "size")
.attr("rx", 6)
.attr("ry", 6)
.on("mouseover", function() { d3.select(this).classed("active", true); })
.on("mouseout", function() { d3.select(this).classed("active", false); });
size.append("text")
.attr("class", "label")
.attr("x", function(d) {return d.x + 14;})
.attr("y", function(d) {return d.y + 15;})
.text(function(d) {return d.name;});
}
renderRelationshipGraph();
Thanks for the help on this!