Skip to content

Instantly share code, notes, and snippets.

@johangithub
Last active July 24, 2016 02:00
Show Gist options
  • Save johangithub/37e35b3a10a97c5ec42c to your computer and use it in GitHub Desktop.
Save johangithub/37e35b3a10a97c5ec42c to your computer and use it in GitHub Desktop.
Officer AFSC Grouping using collapsible indented tree
license: gpl-3.0
height:800
border: no

Collapsible indented tree shows the hierarchical structure of data in a condensed manner. Usually it takes 200+ page document called Air Force Officer Classification Directory (AFOCD) to get a sense of what the Air Force Specialty Code structure looks like.

This is one of the first attemps using d3.js with examples from mbostock/1093025 and larskotthoff/702289. What I took away from larksfotthoff/702289 is how to start all the tree collapsed. I also learned how to dynamically size the frame using

d3.select(self.frameElement).transition()
.duration(duration)
.style("height", height + 50 + "px");

I'm not proud to admit that I typed out the JSON file for this one. I definitely know better now.

{
"name": "Active Duty Officer",
"children": [
{
"name": "Nonrated Line",
"children": [
{
"name": "Nonrated Operations",
"children": [
{"name": "13C Special Tactics"},
{"name": "13D Combat Rescue"},
{"name": "13L Air Liaison"},
{"name": "13M Airfield Operations"},
{"name": "13N Nuclear and Missile Operations"},
{"name": "13S Space Operations"},
{"name": "14N Intelligence"},
{"name": "15W Weather"},
{"name": "17D Cyber Operations"}
]
},
{
"name": "Acquisition",
"children": [
{"name": "61A Operations Research Analyst"},
{"name": "61B Behavioral Scientist"},
{"name": "61C Chemist/Biologist"},
{"name": "61D Physicist/Nuclear Engineer"},
{"name": "62E Developmental Engineer"},
{"name": "63A Acquisition Manager"},
{"name": "64P Contracting"},
{"name": "65F Financial Management"}
]
},
{
"name": "Support",
"children": [
{"name": "31P Security Forces"},
{"name": "32E Civil Engineer"},
{"name": "35B Band"},
{"name": "35P Public Affairs"},
{"name": "38P Personnel"}
]
},
{
"name": "Logistics",
"children": [
{"name": "21A Aircraft Maintenance"},
{"name": "21M Munitions and Missile Maintenance"},
{"name": "21R Logistics Readiness"}
]
},
{
"name": "Special Investigations",
"children": [
{"name": "71S Special Investigations"}
]
}
]
},
{
"name": "Rated",
"children": [
{
"name": "Pilot",
"children": [
{"name": "11M Mobility"},
{"name": "11F Fighter"},
{"name": "11S Speical Operations"},
{"name": "11R Reconnaissance"},
{"name": "11B Bomber"},
{"name": "11H Rescue"},
{"name": "11K Instructor"},
{"name": "11E Experimental Test"}
]
},
{
"name": "Combat Systems",
"children": [
{"name": "12R Reconnaissance"},
{"name": "12B Bomber"},
{"name": "12S Speical Operations"},
{"name": "12F Fighter"},
{"name": "12M Mobility"},
{"name": "12H Rescue"},
{"name": "12E Experimental Test"}
]
},
{
"name": "Air Battle Manager",
"children": [
{"name": "13BXB AWACS"},
{"name": "13BXK JSTARS"},
{"name": "13B Ground TAC"}
]
},
{
"name": "Remotely Piloted Aircraft",
"children": [
{"name": "18A Attack"},
{"name": "18R Reconnaissance"},
{"name": "18S Speical Operations"},
{"name": "18E Experimental Test"}
]
},
{
"name": "Student",
"children": [
{"name": "92T0 Pilot Trainnee"},
{"name": "92T1 Combat Systems Trainnee"},
{"name": "92T2 Air Battle Manager Trainnee"},
{"name": "92T3 RPA Pilot Trainnee"}
]
}
]
},
{
"name": "Medical",
"children": [
{
"name": "Health Services",
"children": [
{"name": "41A Health Services Administrator"}
]
},
{
"name": "Biomedical Clinician",
"children": [
{"name": "42B Physical Therapist"},
{"name": "42E Optometrist"},
{"name": "42F Podiatrist"},
{"name": "42G Physician Assistant"},
{"name": "42N Audiology/Speech Pathologist"},
{"name": "42P Clinical Psychologist"},
{"name": "42S Clinical Social Worker"},
{"name": "42T Occupational Therapist"}
]
},
{
"name": "Biomedical Specialist",
"children": [
{"name": "43A Aerospace & Operational Physiologist"},
{"name": "43B Biomedical Scientist"},
{"name": "43D Dietitian"},
{"name": "43E Bioenvironmental Engineer"},
{"name": "43H Public Health"},
{"name": "43M Medical Entomologist"},
{"name": "43P Pharmacist"},
{"name": "43T Biomedical Laboratory"}
]
},
{
"name": "Physician",
"children": [
{"name": "44A Chief, Hospital/Clinic Services"},
{"name": "44B Preventice Medicine"},
{"name": "44D Pathologist"},
{"name": "44E Emergency Services Physician"},
{"name": "44F Family Physician"},
{"name": "44G General Practice Physician"},
{"name": "44H Nuclear Medicine Physician"},
{"name": "44J Clinical Geneticist"},
{"name": "44K Pediatrician"},
{"name": "44M Internist"},
{"name": "44N Neurologist"},
{"name": "44P Psychiatrist"},
{"name": "44O Physician"},
{"name": "44R Diagnostic Radiologist"},
{"name": "44S Dermatologist"},
{"name": "44T Radiotherapist"},
{"name": "44U Occupational Medicine"},
{"name": "44Y Critical Care Medicine"},
{"name": "44Z Allergist"}
]
},
{
"name": "Surgery",
"children": [
{"name": "45A Anesthesiologist"},
{"name": "45B Orthopedic Surgeon"},
{"name": "45E Opthalmologist"},
{"name": "45G OB/GYN"},
{"name": "45N Otorhinolaryngologist"},
{"name": "45P Physicial Medicine Physician"},
{"name": "45S Surgeon"},
{"name": "45U Urologist"}
]
},
{
"name": "Nurse",
"children": [
{"name": "46A Nurse Administrator"},
{"name": "46F Flight Nurse"},
{"name": "46N Clinical Nurse"},
{"name": "46P Mental Health Nurse"},
{"name": "46S Operating Room Nurse"},
{"name": "46Y Privileged Advanced Practice Nurse"}
]
},
{
"name": "Dental",
"children": [
{"name": "47B Orthodontist"},
{"name": "47D Oral and Maxillofacial Pathologist"},
{"name": "47E Endodontist"},
{"name": "47G Dentist"},
{"name": "47H Periodontist"},
{"name": "47K Pediatric Dentist"},
{"name": "47P Prosthodontist"},
{"name": "47S Oral and Maxillofacial Surgeon"}
]
},
{
"name": "Aerospace Medicine",
"children": [
{"name": "48A Aerospace Medicine Specialist"},
{"name": "48G General Medical Officer, Flight Surgeon"},
{"name": "48R Residency Trained Flight Surgeon"},
{"name": "48V Pilot-Physician"}
]
}
]
},
{
"name": "JAG/Chaplain",
"children": [
{"name": "51J Judge Advocate"},
{"name": "52R Chaplain"}
]
},
{
"name": "General Officer",
"children": [
{"name": "O-7 Brigadier General"},
{"name": "O-8 Major General"},
{"name": "O-9 Lieutenant General"},
{"name": "O-10 General"}
]
}
]
}
<!DOCTYPE html>
<!--
Example derived from
http://bl.ocks.org/mbostock/1093025
and
http://bl.ocks.org/larskotthoff/7022289
-->
<html>
<head>
<meta charset="utf-8">
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
<style type="text/css">
.node rect {
cursor: pointer;
fill: #fff;
fill-opacity: .5;
stroke: #3182bd;
stroke-width: 1.5px;
overflow: auto
}
.node text {
font: 10px sans-serif;
pointer-events: none;
}
path.link {
fill: none;
stroke: #9ecae1;
stroke-width: 1.5px;
}
</style>
</head>
<body>
<div id="chart"></div>
<script type="text/javascript">
var w = 960,
h = 800,
i = 0,
barHeight = 20,
barWidth = w * .8,
duration = 400,
root;
var tree = d3.layout.tree()
.size([h, 100]);
var diagonal = d3.svg.diagonal()
.projection(function(d) {
return [d.y, d.x];
});
var vis = d3.select("#chart").append("svg:svg")
.attr("width", w)
.attr("height", h)
.append("svg:g")
.attr("transform", "translate(20,30)");
function moveChildren(node) {
if (node.children) {
node.children.forEach(function(c) {
moveChildren(c);
});
node._children = node.children;
node.children = null;
}
}
d3.json("adofficer_collapsible_tree.json", function(json) {
json.x0 = 0;
json.y0 = 0;
moveChildren(json);
update(root = json);
});
function update(source) {
// Compute the flattened node list. TODO use d3.layout.hierarchy.
var nodes = tree.nodes(root);
var height = Math.max(500, nodes.length * barHeight );
d3.select("svg").transition()
.duration(duration)
.attr("height", height+20);
d3.select(self.frameElement).transition()
.duration(duration)
.style("height", height + 50 + "px");
// Compute the "layout".
nodes.forEach(function(n, i) {
n.x = i * barHeight;
});
// Update the nodes…
var node = vis.selectAll("g.node")
.data(nodes, function(d) {
return d.id || (d.id = ++i);
});
var nodeEnter = node.enter().append("svg:g")
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + source.y0 + "," + source.x0 + ")";
})
.style("opacity", 1e-6);
// Enter any new nodes at the parent's previous position.
nodeEnter.append("svg:rect")
.attr("y", -barHeight / 2)
.attr("height", barHeight)
.attr("width", barWidth)
.style("fill", color)
.on("click", click);
nodeEnter.append("svg:text")
.attr("dy", 3.5)
.attr("dx", 5.5)
.text(function(d) {
return d.name;
});
// Transition nodes to their new position.
nodeEnter.transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + d.y + "," + d.x + ")";
})
.style("opacity", 1);
node.transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + d.y + "," + d.x + ")";
})
.style("opacity", 1)
.select("rect")
.style("fill", color);
// Transition exiting nodes to the parent's new position.
node.exit().transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + source.y + "," + source.x + ")";
})
.style("opacity", 1e-6)
.remove();
// Update the links…
var link = vis.selectAll("path.link")
.data(tree.links(nodes), function(d) {
return d.target.id;
});
// Enter any new links at the parent's previous position.
link.enter().insert("svg:path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {
x: source.x0,
y: source.y0
};
return diagonal({
source: o,
target: o
});
})
.transition()
.duration(duration)
.attr("d", diagonal);
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {
x: source.x,
y: source.y
};
return diagonal({
source: o,
target: o
});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
// Toggle children on click.
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
function color(d) {
return d._children ? "#3182bd" : d.children ? "#c6dbef" : "#fd8d3c";
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment