Reputation: 13709
I'm trying to use d3js to lay out a diagram of servers at locations. Here's a sample extract of the input data.
{"type": "customer", "name": "Acme", "children": [
{
"type": "site",
"name": "Wichita",
"children": [
{
"type": "server",
"name": "server1"
},
{
"type": "server",
"name": "server2"
}
]
},
{
"type": "site",
"name": "Springfield",
"children": [
{
"type": "server",
"name": "server1"
},
{
"type": "server",
"name": "server2"
},
{
"type": "server",
"name": "server3"
}
]
}
]}
The sites will have varying numbers of servers. Each server should be a rectangle within a rectangle representing a site. I can create the site and server rectangles individually, but I think I want the server nodes bound under their parent site's node for proper grouping, particulary if we later import the svg to Visio. The input json is in the var treeData. Also, I'm having trouble getting the positioning correct, which it seems might be easier with the proper grouping.
var viz = d3.select("#viz").append("svg:svg")
.attr("width", 1200)
.attr("height", layoutHeight)
.append("svg:g");
var tree = d3.layout.tree().size([layoutHeight, 300]);
var nodes = tree.nodes(treeData);
viz.append("svg:text")
.attr("dx", "0")
.attr("dy", 20)
.attr("font-size", 24)
.attr("text-anchor", "start")
.text(json.customerName);
var siteNodes = viz.selectAll("g.node")
.data(nodes)
.enter().append("svg:g")
.filter(function(d) { return d.type === "site" })
.attr("transform", function(d) { return "translate(" + d.y + "," + (d.x * 1.5 - (d.children.length * serverHeight * 1.1 / 2)) + ")"; });
siteNodes.append("svg:rect")
.attr("width", 300)
.attr("height", function(d) { return serverHeight * (d.children.length + 1); })
.attr("stroke", "black")
.attr("stroke-width", "1")
.attr("fill", "white");
var serverNodes = viz.selectAll("g.node")
.data(nodes)
.enter().append("svg:g")
.filter(function(d) { return d.type === "server" })
.attr("transform", function(d) { return "translate(" + (d.y - 125) + "," + d.x + ")"; });
serverNodes.append("svg:rect")
.attr("width", 250)
.attr("height", serverHeight)
.attr("font-size", 10)
.attr("stroke", "black")
.attr("stroke-width", "1")
.attr("fill-opacity", "0.1")
.attr("fill", "blue");
I'm new to d3js, and I suspect a major factor is that I'm not quite yet thinking about d3js in the proper way.
Upvotes: 0
Views: 449
Reputation: 13709
Here's the solution I worked out with the help of knowledge stockpile and this discussion on Google:
I changed some field names in the data, so that it now looks more like:
var treeData = {type: "customer", name: "Acme", children: [
{type: "site", name: "Wichita", servers: [
{type: "server", name: "server1", status: "Powered On"},
{type: "server", name: "server2", status: "Powered On"}]},
{type: "site", name: "Springfield", servers: [
{type: "server", name: "server1", status: "Powered On"},
{type: "server", name: "server2", status: "Powered On"},
{type: "server", name: "server3", status: "Powered On"}]
}
]};
Calculate the space needed for the layout:
var serverHeight = 50;
var serverWidth = 250;
var siteWidth = serverWidth + 50;
var layoutVerticalOffset = 20;
var layoutHeight = 150 + (maxServerCount + 2) * serverHeight * 1.5;
var layoutWidth = treeData.children.length * siteWidth * 1.5;
var viz = d3.select("#viz").append("svg:svg")
.attr("height", layoutHeight)
.attr("width", layoutWidth + siteWidth)
.append("svg:g");
var tree = d3.layout.tree().size([layoutWidth, 0]);
I added a class to the site nodes:
var siteNodes = viz.selectAll("g.node")
.data(nodes)
.enter().append("svg:g")
.filter(function(d) { return d.type === "site" })
.attr("class", "site")
.attr("transform", function(d) {
return "translate(" + d.x + "," + 100 + ")";
});
and use that class for selection when building the server nodes:
var serverNodes = siteNodes.selectAll(".site")
.data(function(d) { return d.servers; })
.enter().append("svg:g")
.attr("class", "server")
.attr("transform", function(d, i) {
return "translate(" + 25 + "," + (serverHeight / 2 + i * serverHeight * 1.5) + ")";
});
The result is that the servers are represented by g-nodes properly nested within the site nodes:
<g class="site" transform="translate(300,35)">
<rect width="300" height="165" stroke="black" stroke-width="1" fill="white"/>
<text dx="0" dy="12" font-size="18" text-anchor="end">Site</text>
<text dx="0" dy="34" font-size="18" text-anchor="end">Wichita</text>
<g class="server" transform="translate(25,25)">
<rect width="250" height="50" font-size="10" stroke="black" stroke-width="1" fill-opacity="0.1" fill="blue"/>
</g>
<g class="server" transform="translate(25,100)">
<rect width="250" height="50" font-size="10" stroke="black" stroke-width="1" fill-opacity="0.1" fill="blue"/>
</g>
</g>
This forms the proper grouping, which is important for the export, and simplifies the calculations of server graphics by making them relative to the site graphic.
Upvotes: 0
Reputation: 5015
I had to make a few assumptions about values since you did not provide the complete code. The changes I made should be easy to follow. Here is the fiddle to help you out. The transformation you are using for the sites is not clear to me and I put an approximation to get you going. You will definitely need to tweak it. Hope this helps.
.attr("transform", function(d) { return "translate(" + (d.y) + "," + (d.x * 1.25 - (d.children.length * serverHeight * 0.9)) + ")"; });
Upvotes: 1