-
-
Save ecoffman/a652d5276f1b71b80f53 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"nodes": [ | |
{"x": 325, "y": 248, "recordtype": "e", "id": 3, "name": "My Project"}, | |
{ "x": 150, "y": 410, "recordtype": "a", "id": 0, "relationshipType": 0, "name": "Lands", "dataFile": "Lands.data.json"}, | |
{ "x": 200, "y": 364, "recordtype": "b", "id": 1, "relationshipType": 0, "name": "Climate and Energy" }, | |
{ "x": 223, "y": 365, "recordtype": "c", "id": 2, "relationshipType": 1, "name": "NAR Renewable Energy"}, | |
{ "x": 50, "y": 314, "recordtype": "d", "id": 2, "relationshipType": 0, "name": "LAR Sustainable Food" }, | |
{ "x": 75, "y": 207, "recordtype": "a", "id": 2, "relationshipType": 0, "name": "Africa Lands Strategy" }, | |
{ "x": 500, "y": 155, "recordtype": "b", "id": 1, "relationshipType": 0, "name": "Agriculture" }, | |
{ "x": 369, "y": 196, "recordtype": "b", "id": 4, "relationshipType": 1, "name": "Chesapeake Bay" }, | |
{ "x": 350, "y": 148, "recordtype": "c", "id": 4, "relationshipType": 0, "name": "Atlantic Coast" }, | |
{ "x": 100, "y": 222, "recordtype": "d", "id": 5, "relationshipType": 1, "name": "China Agriculture" }, | |
{ "x": 594, "y": 235, "recordtype": "d", "id": 5, "relationshipType": 0, "name": "Southern California" }, | |
{ "x": 298, "y": 185, "recordtype": "a", "id": 2, "relationshipType": 1, "name": "AP Fisheries" }, | |
{ "x": 633, "y": 200, "recordtype": "e", "id": 1, "relationshipType": 0, "name": "Aquaculture" }, | |
{ "x": 150, "y": 410, "recordtype": "a", "id": 0, "relationshipType": 0, "name": "Climate"}, | |
{ "x": 200, "y": 364, "recordtype": "b", "id": 1, "relationshipType": 0, "name": "Great Rivers" }, | |
{ "x": 223, "y": 365, "recordtype": "c", "id": 2, "relationshipType": 1, "name": "AP Coral Reefs" }, | |
{ "x": 500, "y": 155, "recordtype": "b", "id": 1, "relationshipType": 0, "name": "Securing Water" }, | |
{ "x": 369, "y": 196, "recordtype": "b", "id": 4, "relationshipType": 1, "name": "Amazon" }, | |
{ "x": 350, "y": 148, "recordtype": "c", "id": 4, "relationshipType": 0, "name": "Gulf of Mexico" }, | |
{ "x": 100, "y": 222, "recordtype": "d", "id": 5, "relationshipType": 1, "name": "China Climate" }, | |
{ "x": 594, "y": 235, "recordtype": "d", "id": 5, "relationshipType": 0, "name": "Colorado Energy" }, | |
{ "x": 633, "y": 200, "recordtype": "e", "id": 1, "relationshipType": 0, "name": "Global Water Markets" } | |
] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html xmlns="http://www.w3.org/1999/xhtml"> | |
<head> | |
<title>D3: Force Layout</title> | |
<style> | |
.visual-area { | |
border: 2px solid #aa88aa; | |
} | |
.link { | |
stroke: #000; | |
stroke-width: 1.5px; | |
} | |
.node { | |
cursor: move; | |
} | |
.node circle { | |
fill: #ccc; | |
stroke: #000; | |
stroke-width: 1.5px; | |
} | |
.node.fixed { | |
fill: #f00; | |
} | |
.node text { | |
pointer-events: none; | |
font: 10px sans-serif; | |
} | |
</style> | |
</head> | |
<body> | |
<h2>D3: Force Layout</h2> | |
<p> | |
Items demonstrated: | |
<ul> | |
<li>Nodes "assigned" & constrained to regions based on ID (on render & drag)</li> | |
<li>Node Repulsion</li> | |
<li>Conditional formatting of nodes & links</li> | |
<li>Draggable Nodes</li> | |
<li>Node Labels</li> | |
<li>Context Switch - click "Lands" to load alternate data (click "My Project" load initial data)</li> | |
</ul> | |
</p> | |
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> | |
<script> | |
var width = 960, | |
height = 500, | |
numRegions = 6, | |
radius = 12; | |
// Create the color scale | |
var color = d3.scale.category10() | |
.domain(d3.range(numRegions)); | |
// Create the bands | |
var regions = d3.scale.ordinal() | |
.domain(d3.range(numRegions)) | |
.rangeRoundBands([0, width]); // Round Bands for integer boundaries | |
// Set up the SVG display area | |
var svg = d3.select("body").append("svg") | |
.attr("width", width) | |
.attr("height", height) | |
.classed('visual-area', true); | |
// Draw Regions | |
for (var i = 0; i < numRegions; i++) { | |
svg.append("rect") | |
.attr("x", regions(i)) | |
.attr("y", 0) | |
.attr("width", 160) | |
.attr("height", "100%") | |
.attr("fill", color(i)); | |
} | |
// Instantiate the Force Layout object | |
var force = d3.layout.force() | |
.size([width, height]) | |
.charge(-500) | |
.chargeDistance(100) | |
.gravity(0) | |
.friction(0.1) // Makes nodes static | |
.linkDistance(50) | |
.linkStrength(0) | |
.on("tick", tick); | |
var initialDataFile = "data.json"; | |
var linkSet = null, | |
nodeSet = null; | |
function CreateForceLayout(dataFile) { | |
// Clear the slate | |
svg.selectAll(".node").remove(); | |
svg.selectAll(".link").remove(); | |
// Create data object sets | |
linkSet = svg.selectAll(".link"); | |
nodeSet = svg.selectAll(".node"); | |
// Read Data from JSON file | |
d3.json(dataFile, function (error, graph) { | |
var links = []; | |
// Save the "context" node ID for helping to place | |
// node labels | |
contextNodeId = Number(graph.nodes[0].id); | |
// Build the links | |
graph.nodes.forEach(function (d, i) { | |
d.x = findXRegion(d); | |
if (i != 0) { | |
links.push({ id: d.name + i, source: 0, target: i, relationshipType: d.relationshipType }); | |
} | |
}) | |
console.log(links); | |
force | |
.nodes(graph.nodes) | |
.links(links) | |
.start(); | |
linkSet = linkSet.data(links, function (d) { return d.id; }); | |
linkSet.enter().append("line") | |
.attr("class", "link"); | |
linkSet.exit().remove(); | |
nodeSet = nodeSet.data(graph.nodes, function (d) { return d.name; }); | |
nodeSet.enter().append("g") | |
.attr("class", "node") | |
.on("click", clickHandler) | |
.call(dragHandler); | |
nodeSet.append("circle") | |
.style("fill", function (d) { return d.id == contextNodeId ? "#fff" : "#afafaf" }) | |
.attr("r", radius); | |
// Add labels as a <svg:text> element | |
// Note: (0,0) is the center of the node | |
nodeSet.append("text") | |
.attr("dx", function (d) { | |
return (radius * ((Number(d.id) < contextNodeId) ? -1 : 1)); | |
}) | |
.attr("dy", function (d) { | |
return (radius * ((Number(d.id) < contextNodeId) ? -1 : 1)); | |
}) | |
.attr("text-anchor", function (d) { | |
return (Number(d.id) < contextNodeId) ? "end" : "start"; | |
}) | |
.text(function (d) { return d.name; }); | |
}); | |
} | |
// Run for each step of the Force Layout | |
function tick() { | |
nodeSet.attr("transform", function (d) { | |
// Calculate region's [min,max] x-coordinate | |
var regionMinX = regions(d.id); | |
var regionMaxX = regionMinX + regions.rangeBand(); | |
// Constrain the node to the region | |
var newX = d.x = Math.max(regionMinX + radius, Math.min(regionMaxX - radius, d.x)); | |
var newY = d.y = Math.max(radius, Math.min(height - radius, d.y)); | |
return "translate(" + newX + "," + newY + ")"; | |
}); | |
linkSet.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; }) | |
.attr("stroke-dasharray", function (d) { return d.relationshipType == 1 ? "10, 5" : "none" }); | |
} | |
// Calculate the node's x-coordinate based on it's ID | |
function findXRegion(d) { | |
var translationFactor = 160 * d.id; | |
var randomX = Math.floor(Math.random() * 160); | |
return randomX + translationFactor; | |
} | |
function clickHandler(d) { | |
// Prevent this from firing on drag | |
if (d3.event.defaultPrevented) return; | |
if (d.dataFile) { | |
CreateForceLayout(d.dataFile); | |
} | |
} | |
// Drag action | |
var dragHandler = force.drag().on("dragstart", dragstart); | |
function dragstart(d) { | |
// This will fix the node position after it's been dragged | |
//d3.select(this).classed("fixed", d.fixed = true); | |
} | |
CreateForceLayout(initialDataFile); | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"nodes": [ | |
{ "x": 150, "y": 410, "recordtype": "a", "id": 0, "name": "Lands" }, | |
{"x": 325, "y": 248, "recordtype": "e", "id": 3, "name": "My Project", "relationshipType": 1, "dataFile": "data.json"}, | |
{ "x": 200, "y": 364, "recordtype": "b", "id": 1, "relationshipType": 0, "name": "Climate and Energy" }, | |
{ "x": 75, "y": 207, "recordtype": "a", "id": 2, "relationshipType": 0, "name": "Africa Lands Strategy" }, | |
{ "x": 350, "y": 148, "recordtype": "c", "id": 4, "relationshipType": 0, "name": "Atlantic Coast" }, | |
{ "x": 298, "y": 185, "recordtype": "a", "id": 2, "relationshipType": 1, "name": "AP Fisheries" }, | |
{ "x": 350, "y": 148, "recordtype": "c", "id": 4, "relationshipType": 0, "name": "Gulf of Mexico" }, | |
{ "x": 594, "y": 235, "recordtype": "d", "id": 5, "relationshipType": 0, "name": "Colorado Energy" } | |
] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment