An example of d3.layout.orbit that visualizes the D3 API.
When you mouseover a node, it displays its label and centers the orbit layout on that node. It also draws spokes to children and parents where applicable.
An example of d3.layout.orbit that visualizes the D3 API.
When you mouseover a node, it displays its label and centers the orbit layout on that node. It also draws spokes to children and parents where applicable.
{ | |
"name": "d3", | |
"children": [ | |
{"name": "version"}, | |
{"name": "ascending"}, | |
{"name": "descending"}, | |
{"name": "min"}, | |
{"name": "max"}, | |
{"name": "extent"}, | |
{"name": "sum"}, | |
{"name": "mean"}, | |
{"name": "quantile"}, | |
{"name": "median"}, | |
{"name": "variance"}, | |
{"name": "deviation"}, | |
{"name": "bisectLeft"}, | |
{"name": "bisectRight"}, | |
{"name": "bisect"}, | |
{"name": "bisector"}, | |
{"name": "shuffle"}, | |
{"name": "permute"}, | |
{"name": "pairs"}, | |
{"name": "zip"}, | |
{"name": "transpose"}, | |
{"name": "keys"}, | |
{"name": "values"}, | |
{"name": "entries"}, | |
{"name": "merge"}, | |
{"name": "range"}, | |
{"name": "map"}, | |
{"name": "nest"}, | |
{"name": "set"}, | |
{ | |
"name": "behavior", | |
"children": [ | |
{"name": "drag"}, | |
{"name": "zoom"} | |
] | |
}, | |
{"name": "rebind"}, | |
{"name": "dispatch"}, | |
{"name": "event"}, | |
{"name": "requote"}, | |
{ | |
"name": "selection", | |
"children": [{"name": "enter"}] | |
}, | |
{ | |
"name": "ns", | |
"children": [ | |
{ | |
"name": "prefix", | |
"children": [ | |
{"name": "svg"}, | |
{"name": "xhtml"}, | |
{"name": "xlink"}, | |
{"name": "xml"}, | |
{"name": "xmlns"} | |
] | |
}, | |
{"name": "qualify"} | |
] | |
}, | |
{"name": "select"}, | |
{"name": "selectAll"}, | |
{"name": "mouse"}, | |
{"name": "touch"}, | |
{"name": "touches"}, | |
{"name": "interpolateZoom"}, | |
{"name": "color"}, | |
{"name": "hsl"}, | |
{"name": "hcl"}, | |
{"name": "lab"}, | |
{"name": "rgb"}, | |
{"name": "functor"}, | |
{"name": "xhr"}, | |
{"name": "dsv"}, | |
{ | |
"name": "csv", | |
"children": [ | |
{"name": "parse"}, | |
{"name": "parseRows"}, | |
{"name": "format"}, | |
{"name": "formatRows"} | |
] | |
}, | |
{ | |
"name": "tsv", | |
"children": [ | |
{"name": "parse"}, | |
{"name": "parseRows"}, | |
{"name": "format"}, | |
{"name": "formatRows"} | |
] | |
}, | |
{ | |
"name": "timer", | |
"children": [{"name": "flush"}] | |
}, | |
{"name": "round"}, | |
{"name": "formatPrefix"}, | |
{ | |
"name": "time", | |
"children": [ | |
{ | |
"name": "year", | |
"children": [ | |
{"name": "round"}, | |
{"name": "ceil"}, | |
{"name": "offset"}, | |
{ | |
"name": "range", | |
"children": [{"name": "utc"}] | |
}, | |
{ | |
"name": "utc", | |
"children": [ | |
{"name": "round"}, | |
{"name": "ceil"}, | |
{"name": "offset"}, | |
{"name": "range"} | |
] | |
} | |
] | |
}, | |
{ | |
"name": "years", | |
"children": [{"name": "utc"}] | |
}, | |
{ | |
"name": "day", | |
"children": [ | |
{"name": "round"}, | |
{"name": "ceil"}, | |
{"name": "offset"}, | |
{ | |
"name": "range", | |
"children": [{"name": "utc"}] | |
}, | |
{ | |
"name": "utc", | |
"children": [ | |
{"name": "round"}, | |
{"name": "ceil"}, | |
{"name": "offset"}, | |
{"name": "range"} | |
] | |
} | |
] | |
}, | |
{ | |
"name": "days", | |
"children": [{"name": "utc"}] | |
}, | |
{"name": "dayOfYear"}, | |
{ | |
"name": "sunday", | |
"children": [ | |
{"name": "round"}, | |
{"name": "ceil"}, | |
{"name": "offset"}, | |
{ | |
"name": "range", | |
"children": [{"name": "utc"}] | |
}, | |
{ | |
"name": "utc", | |
"children": [ | |
{"name": "round"}, | |
{"name": "ceil"}, | |
{"name": "offset"}, | |
{"name": "range"} | |
] | |
} | |
] | |
}, | |
{ | |
"name": "sundays", | |
"children": [{"name": "utc"}] | |
}, | |
{"name": "sundayOfYear"}, | |
{ | |
"name": "monday", | |
"children": [ | |
{"name": "round"}, | |
{"name": "ceil"}, | |
{"name": "offset"}, | |
{ | |
"name": "range", | |
"children": [{"name": "utc"}] | |
}, | |
{ | |
"name": "utc", | |
"children": [ | |
{"name": "round"}, | |
{"name": "ceil"}, | |
{"name": "offset"}, | |
{"name": "range"} | |
] | |
} | |
] | |
}, | |
{ | |
"name": "mondays", | |
"children": [{"name": "utc"}] | |
}, | |
{"name": "mondayOfYear"}, | |
{ | |
"name": "tuesday", | |
"children": [ | |
{"name": "round"}, | |
{"name": "ceil"}, | |
{"name": "offset"}, | |
{ | |
"name": "range", | |
"children": [{"name": "utc"}] | |
}, | |
{ | |
"name": "utc", | |
"children": [ | |
{"name": "round"}, | |
{"name": "ceil"}, | |
{"name": "offset"}, | |
{"name": "range"} | |
] | |
} | |
] | |
}, | |
{ | |
"name": "tuesdays", | |
"children": [{"name": "utc"}] | |
}, | |
{"name": "tuesdayOfYear"}, | |
{ | |
"name": "wednesday", | |
"children": [ | |
{"name": "round"}, | |
{"name": "ceil"}, | |
{"name": "offset"}, | |
{ | |
"name": "range", | |
"children": [{"name": "utc"}] | |
}, | |
{ | |
"name": "utc", | |
"children": [ | |
{"name": "round"}, | |
{"name": "ceil"}, | |
{"name": "offset"}, | |
{"name": "range"} | |
] | |
} | |
] | |
}, | |
{ | |
"name": "wednesdays", | |
"children": [{"name": "utc"}] | |
}, | |
{"name": "wednesdayOfYear"}, | |
{ | |
"name": "thursday", | |
"children": [ | |
{"name": "round"}, | |
{"name": "ceil"}, | |
{"name": "offset"}, | |
{ | |
"name": "range", | |
"children": [{"name": "utc"}] | |
}, | |
{ | |
"name": "utc", | |
"children": [ | |
{"name": "round"}, | |
{"name": "ceil"}, | |
{"name": "offset"}, | |
{"name": "range"} | |
] | |
} | |
] | |
}, | |
{ | |
"name": "thursdays", | |
"children": [{"name": "utc"}] | |
}, | |
{"name": "thursdayOfYear"}, | |
{ | |
"name": "friday", | |
"children": [ | |
{"name": "round"}, | |
{"name": "ceil"}, | |
{"name": "offset"}, | |
{ | |
"name": "range", | |
"children": [{"name": "utc"}] | |
}, | |
{ | |
"name": "utc", | |
"children": [ | |
{"name": "round"}, | |
{"name": "ceil"}, | |
{"name": "offset"}, | |
{"name": "range"} | |
] | |
} | |
] | |
}, | |
{ | |
"name": "fridays", | |
"children": [{"name": "utc"}] | |
}, | |
{"name": "fridayOfYear"}, | |
{ | |
"name": "saturday", | |
"children": [ | |
{"name": "round"}, | |
{"name": "ceil"}, | |
{"name": "offset"}, | |
{ | |
"name": "range", | |
"children": [{"name": "utc"}] | |
}, | |
{ | |
"name": "utc", | |
"children": [ | |
{"name": "round"}, | |
{"name": "ceil"}, | |
{"name": "offset"}, | |
{"name": "range"} | |
] | |
} | |
] | |
}, | |
{ | |
"name": "saturdays", | |
"children": [{"name": "utc"}] | |
}, | |
{"name": "saturdayOfYear"}, | |
{ | |
"name": "week", | |
"children": [ | |
{"name": "round"}, | |
{"name": "ceil"}, | |
{"name": "offset"}, | |
{ | |
"name": "range", | |
"children": [{"name": "utc"}] | |
}, | |
{ | |
"name": "utc", | |
"children": [ | |
{"name": "round"}, | |
{"name": "ceil"}, | |
{"name": "offset"}, | |
{"name": "range"} | |
] | |
} | |
] | |
}, | |
{ | |
"name": "weeks", | |
"children": [{"name": "utc"}] | |
}, | |
{"name": "weekOfYear"}, | |
{ | |
"name": "format", | |
"children": [ | |
{ | |
"name": "utc", | |
"children": [{"name": "multi"}] | |
}, | |
{"name": "multi"}, | |
{ | |
"name": "iso", | |
"children": [ | |
{"name": "parse"}, | |
{"name": "toString"} | |
] | |
} | |
] | |
}, | |
{ | |
"name": "second", | |
"children": [ | |
{"name": "round"}, | |
{"name": "ceil"}, | |
{"name": "offset"}, | |
{ | |
"name": "range", | |
"children": [{"name": "utc"}] | |
}, | |
{ | |
"name": "utc", | |
"children": [ | |
{"name": "round"}, | |
{"name": "ceil"}, | |
{"name": "offset"}, | |
{"name": "range"} | |
] | |
} | |
] | |
}, | |
{ | |
"name": "seconds", | |
"children": [{"name": "utc"}] | |
}, | |
{ | |
"name": "minute", | |
"children": [ | |
{"name": "round"}, | |
{"name": "ceil"}, | |
{"name": "offset"}, | |
{ | |
"name": "range", | |
"children": [{"name": "utc"}] | |
}, | |
{ | |
"name": "utc", | |
"children": [ | |
{"name": "round"}, | |
{"name": "ceil"}, | |
{"name": "offset"}, | |
{"name": "range"} | |
] | |
} | |
] | |
}, | |
{ | |
"name": "minutes", | |
"children": [{"name": "utc"}] | |
}, | |
{ | |
"name": "hour", | |
"children": [ | |
{"name": "round"}, | |
{"name": "ceil"}, | |
{"name": "offset"}, | |
{ | |
"name": "range", | |
"children": [{"name": "utc"}] | |
}, | |
{ | |
"name": "utc", | |
"children": [ | |
{"name": "round"}, | |
{"name": "ceil"}, | |
{"name": "offset"}, | |
{"name": "range"} | |
] | |
} | |
] | |
}, | |
{ | |
"name": "hours", | |
"children": [{"name": "utc"}] | |
}, | |
{ | |
"name": "month", | |
"children": [ | |
{"name": "round"}, | |
{"name": "ceil"}, | |
{"name": "offset"}, | |
{ | |
"name": "range", | |
"children": [{"name": "utc"}] | |
}, | |
{ | |
"name": "utc", | |
"children": [ | |
{"name": "round"}, | |
{"name": "ceil"}, | |
{"name": "offset"}, | |
{"name": "range"} | |
] | |
} | |
] | |
}, | |
{ | |
"name": "months", | |
"children": [{"name": "utc"}] | |
}, | |
{ | |
"name": "scale", | |
"children": [{"name": "utc"}] | |
} | |
] | |
}, | |
{"name": "locale"}, | |
{"name": "format"}, | |
{ | |
"name": "geo", | |
"children": [ | |
{"name": "stream"}, | |
{"name": "area"}, | |
{"name": "bounds"}, | |
{"name": "centroid"}, | |
{"name": "clipExtent"}, | |
{ | |
"name": "conicEqualArea", | |
"children": [{"name": "raw"}] | |
}, | |
{"name": "albers"}, | |
{"name": "albersUsa"}, | |
{"name": "path"}, | |
{"name": "transform"}, | |
{"name": "projection"}, | |
{"name": "projectionMutator"}, | |
{ | |
"name": "equirectangular", | |
"children": [{"name": "raw"}] | |
}, | |
{"name": "rotation"}, | |
{"name": "circle"}, | |
{"name": "distance"}, | |
{"name": "graticule"}, | |
{"name": "greatArc"}, | |
{"name": "interpolate"}, | |
{"name": "length"}, | |
{ | |
"name": "azimuthalEqualArea", | |
"children": [ | |
{ | |
"name": "raw", | |
"children": [{"name": "invert"}] | |
} | |
] | |
}, | |
{ | |
"name": "azimuthalEquidistant", | |
"children": [ | |
{ | |
"name": "raw", | |
"children": [{"name": "invert"}] | |
} | |
] | |
}, | |
{ | |
"name": "conicConformal", | |
"children": [{"name": "raw"}] | |
}, | |
{ | |
"name": "conicEquidistant", | |
"children": [{"name": "raw"}] | |
}, | |
{ | |
"name": "gnomonic", | |
"children": [ | |
{ | |
"name": "raw", | |
"children": [{"name": "invert"}] | |
} | |
] | |
}, | |
{ | |
"name": "mercator", | |
"children": [ | |
{ | |
"name": "raw", | |
"children": [{"name": "invert"}] | |
} | |
] | |
}, | |
{ | |
"name": "orthographic", | |
"children": [ | |
{ | |
"name": "raw", | |
"children": [{"name": "invert"}] | |
} | |
] | |
}, | |
{ | |
"name": "stereographic", | |
"children": [ | |
{ | |
"name": "raw", | |
"children": [{"name": "invert"}] | |
} | |
] | |
}, | |
{ | |
"name": "transverseMercator", | |
"children": [ | |
{ | |
"name": "raw", | |
"children": [{"name": "invert"}] | |
} | |
] | |
} | |
] | |
}, | |
{ | |
"name": "geom", | |
"children": [ | |
{"name": "hull"}, | |
{"name": "polygon"}, | |
{"name": "voronoi"}, | |
{"name": "delaunay"}, | |
{"name": "quadtree"} | |
] | |
}, | |
{"name": "interpolateRgb"}, | |
{"name": "interpolateObject"}, | |
{"name": "interpolateNumber"}, | |
{"name": "interpolateString"}, | |
{"name": "interpolate"}, | |
{"name": "interpolators"}, | |
{"name": "interpolateArray"}, | |
{"name": "ease"}, | |
{"name": "interpolateHcl"}, | |
{"name": "interpolateHsl"}, | |
{"name": "interpolateLab"}, | |
{"name": "interpolateRound"}, | |
{"name": "transform"}, | |
{"name": "interpolateTransform"}, | |
{ | |
"name": "layout", | |
"children": [ | |
{"name": "bundle"}, | |
{"name": "chord"}, | |
{"name": "force"}, | |
{"name": "hierarchy"}, | |
{"name": "partition"}, | |
{"name": "pie"}, | |
{"name": "stack"}, | |
{"name": "histogram"}, | |
{"name": "pack"}, | |
{"name": "tree"}, | |
{"name": "cluster"}, | |
{"name": "treemap"} | |
] | |
}, | |
{ | |
"name": "random", | |
"children": [ | |
{"name": "normal"}, | |
{"name": "logNormal"}, | |
{"name": "bates"}, | |
{"name": "irwinHall"} | |
] | |
}, | |
{ | |
"name": "scale", | |
"children": [ | |
{"name": "linear"}, | |
{"name": "log"}, | |
{"name": "pow"}, | |
{"name": "sqrt"}, | |
{"name": "ordinal"}, | |
{"name": "category10"}, | |
{"name": "category20"}, | |
{"name": "category20b"}, | |
{"name": "category20c"}, | |
{"name": "quantile"}, | |
{"name": "quantize"}, | |
{"name": "threshold"}, | |
{"name": "identity"} | |
] | |
}, | |
{ | |
"name": "svg", | |
"children": [ | |
{"name": "arc"}, | |
{ | |
"name": "line", | |
"children": [{"name": "radial"}] | |
}, | |
{ | |
"name": "area", | |
"children": [{"name": "radial"}] | |
}, | |
{"name": "chord"}, | |
{ | |
"name": "diagonal", | |
"children": [{"name": "radial"}] | |
}, | |
{"name": "symbol"}, | |
{"name": "symbolTypes"}, | |
{"name": "axis"}, | |
{"name": "brush"} | |
] | |
}, | |
{"name": "transition"}, | |
{"name": "text"}, | |
{"name": "json"}, | |
{"name": "html"}, | |
{"name": "xml"}, | |
{"name": "hexbin"} | |
] | |
} |
d3.layout.orbit = function() { | |
var currentTickStep = 0; | |
var orbitNodes; | |
var orbitSize = [1,1]; | |
var nestedNodes; | |
var flattenedNodes = []; | |
var tickRadianStep = 0.004363323129985824; | |
var orbitDispatch = d3.dispatch('tick'); | |
var tickInterval; | |
var orbitalRings = []; | |
var orbitDepthAdjust = function() {return 2.95}; | |
var childrenAccessor = function(d) {return d.children}; | |
var tickRadianFunction = function() {return 1}; | |
var fixedOrbitArray = [99]; | |
var orbitMode = "flat"; | |
function _orbitLayout() { | |
return _orbitLayout; | |
} | |
_orbitLayout.mode = function(_mode) { | |
//Atomic, Solar, other? | |
if (!arguments.length) return orbitMode; | |
if (_mode == "solar") { | |
fixedOrbitArray = [1] | |
} | |
if (_mode == "atomic") { | |
fixedOrbitArray = [2,8] | |
} | |
if (_mode == "flat") { | |
fixedOrbitArray = [99] | |
} | |
orbitMode = _mode; | |
if (Array.isArray(_mode)) { | |
fixedOrbitArray = _mode; | |
orbitMode = "custom"; | |
} | |
return this | |
} | |
_orbitLayout.start = function() { | |
//activate animation here | |
tickInterval = setInterval( | |
function() { | |
currentTickStep++; | |
flattenedNodes.forEach(function(_node){ | |
if (_node.parent) { | |
_node.x = _node.parent.x + ( (_node.ring) * Math.sin( _node.angle + (currentTickStep * tickRadianStep * tickRadianFunction(_node))) ); | |
_node.y = _node.parent.y + ( (_node.ring) * Math.cos( _node.angle + (currentTickStep * tickRadianStep * tickRadianFunction(_node))) ); | |
} | |
}) | |
orbitalRings.forEach(function(_ring) { | |
_ring.x = _ring.source.x; | |
_ring.y = _ring.source.y; | |
}) | |
orbitDispatch.tick(); | |
}, | |
10); | |
} | |
_orbitLayout.stop = function() { | |
//deactivate animation here | |
clearInterval(tickInterval); | |
} | |
_orbitLayout.speed = function(_degrees) { | |
if (!arguments.length) return tickRadianStep / (Math.PI / 360); | |
tickRadianStep = tickRadianStep = _degrees * (Math.PI / 360); | |
return this; | |
} | |
_orbitLayout.size = function(_value) { | |
if (!arguments.length) return orbitSize; | |
orbitSize = _value; | |
return this; | |
//change size here | |
} | |
_orbitLayout.revolution = function(_function) { | |
//change ring size reduction (make that into dynamic function) | |
if (!arguments.length) return tickRadianFunction; | |
tickRadianFunction = _function; | |
return this | |
} | |
_orbitLayout.orbitSize = function(_function) { | |
//change ring size reduction (make that into dynamic function) | |
if (!arguments.length) return orbitDepthAdjust; | |
orbitDepthAdjust = _function; | |
return this | |
} | |
_orbitLayout.orbitalRings = function() { | |
//return an array of data corresponding to orbital rings | |
if (!arguments.length) return orbitalRings; | |
return this; | |
} | |
_orbitLayout.nodes = function(_data) { | |
if (!arguments.length) return flattenedNodes; | |
nestedNodes = _data; | |
calculateNodes(); | |
return this; | |
} | |
_orbitLayout.children = function(_function) { | |
if (!arguments.length) return childrenAccessor; | |
//Probably should use d3.functor to turn a string into an object key | |
childrenAccessor = _function; | |
return this; | |
} | |
d3.rebind(_orbitLayout, orbitDispatch, "on"); | |
return _orbitLayout; | |
function calculateNodes() { | |
orbitalRings = []; | |
orbitNodes = nestedNodes; | |
orbitNodes.x = orbitSize[0] / 2; | |
orbitNodes.y = orbitSize[1] / 2; | |
orbitNodes.ring = orbitSize[0] / 2; | |
orbitNodes.depth = 0; | |
flattenedNodes.push(orbitNodes); | |
traverseNestedData(orbitNodes); | |
function traverseNestedData(_node) { | |
if(childrenAccessor(_node)) { | |
var y = 0; | |
var totalChildren = childrenAccessor(_node).length; | |
var _rings = 0; | |
var _total_positions = 0; | |
var _p = 0; | |
while (_total_positions < totalChildren) { | |
if (fixedOrbitArray[_p]) { | |
_total_positions += fixedOrbitArray[_p]; | |
} | |
else { | |
_total_positions += fixedOrbitArray[fixedOrbitArray.length - 1]; | |
} | |
_p++; | |
_rings++; | |
} | |
while (y < totalChildren) { | |
var _pos = 0; | |
var _currentRing = 0; | |
var _p = 0; | |
var _total_positions = 0; | |
while (_total_positions <= y) { | |
if (fixedOrbitArray[_p]) { | |
_total_positions += fixedOrbitArray[_p]; | |
} | |
else { | |
_total_positions += fixedOrbitArray[fixedOrbitArray.length-1]; | |
} | |
_p++; | |
_currentRing++; | |
} | |
var ringSize = fixedOrbitArray[fixedOrbitArray.length-1]; | |
if (fixedOrbitArray[_currentRing-1]) { | |
ringSize = fixedOrbitArray[_currentRing-1]; | |
} | |
if (_node.parent) { | |
var _ring = {source: _node, x: _node.x, y: _node.y, r: _node.parent.ring / orbitDepthAdjust(_node) * (_currentRing / _rings)}; | |
} | |
else { | |
var _ring = {source: _node, x: _node.x, y: _node.y, r: (orbitSize[0] / 2) * (_currentRing / _rings)}; | |
} | |
var thisPie = d3.layout.pie().value(function(d) {return childrenAccessor(d) ? 4 : 1}); | |
var piedValues = thisPie(childrenAccessor(_node).filter(function(d,i) {return i >= y && i <= y+ringSize-1})); | |
for (var x = y; x<y+ringSize && x<totalChildren;x++) { | |
childrenAccessor(_node)[x].angle = ((piedValues[x - y].endAngle - piedValues[x - y].startAngle) / 2) + piedValues[x - y].startAngle; | |
childrenAccessor(_node)[x].parent = _node; | |
childrenAccessor(_node)[x].depth = _node.depth + 1; | |
childrenAccessor(_node)[x].x = childrenAccessor(_node)[x].parent.x + ( (childrenAccessor(_node)[x].parent.ring / 2) * Math.sin( childrenAccessor(_node)[x].angle ) ); | |
childrenAccessor(_node)[x].y = childrenAccessor(_node)[x].parent.y + ( (childrenAccessor(_node)[x].parent.ring / 2) * Math.cos( childrenAccessor(_node)[x].angle ) ); | |
childrenAccessor(_node)[x].ring = _ring.r; | |
flattenedNodes.push(childrenAccessor(_node)[x]); | |
traverseNestedData(childrenAccessor(_node)[x]); | |
} | |
orbitalRings.push(_ring); | |
y+=ringSize; | |
} | |
} | |
} | |
} | |
} |
<html xmlns="http://www.w3.org/1999/xhtml"> | |
<head> | |
<title>Orbit Layout Modes</title> | |
<meta charset="utf-8" /> | |
</head> | |
<style> | |
#viz, svg { | |
width: 100%; | |
height: 100%; | |
} | |
text { | |
pointer-events: none; | |
} | |
#buttons { | |
position: fixed; | |
top:0; | |
left:0; | |
} | |
circle.ring { | |
fill: none; | |
stroke: black; | |
stroke-width: 1px; | |
stroke-opacity: .15; | |
} | |
</style> | |
<script> | |
function makeViz() { | |
d3.json("d3.json", function(data) {drawOrbit(data)}); | |
} | |
function drawOrbit(_data) { | |
var center = {}; | |
var recenter = false; | |
for (var x=0;x<_data.children.length;x++) { | |
_data.children[x].size = _data.children[x].children ? _data.children[x].children.length : 0; | |
} | |
_data.children.sort(function(a,b) { | |
if (a.size > b.size) { | |
return 1; | |
} | |
if (a.size < b.size) { | |
return -1; | |
} | |
return 0; | |
}) | |
sizeScale = d3.scale.linear().domain([0,1,5,10,20]).range([4,6,8,10,12]).clamp(true); | |
colorScale = d3.scale.linear().domain([0,1,2,3,4]).range(["rgb(161,208,120)","rgb(247,148,72)","rgb(225,203,208)","rgb(174,223,228)","rgb(245,132,102)"]); | |
planetColors = {Mercury: "gray", Venus: "#d6bb87", Earth: "#677188", Mars: "#7c5541", Jupiter: "#a36a3e", Saturn: "#e9ba85", Uranus: "#73cbf0", Neptune: "#6383d1"} | |
orbit = d3.layout.orbit().size([800,800]) | |
.revolution(customRevolution) | |
.orbitSize(function(d) {return d.depth >= 2 ? 6 : 4}) | |
.speed(.25) | |
.mode([35,36,8,3,1]) | |
.nodes(_data); | |
center = orbit.nodes()[0]; | |
d3.select("svg") | |
.append("g") | |
.attr("class", "viz") | |
.attr("transform", "translate(50,50)") | |
.selectAll("g.node").data(orbit.nodes()) | |
.enter() | |
.append("g") | |
.attr("class", "node") | |
.attr("transform", function(d) {return "translate(" +d.x +"," + d.y+")"}) | |
.on("mouseover", nodeOver) | |
.on("click", recenter) | |
d3.selectAll("g.node") | |
.append("circle") | |
.attr("class", "satellite") | |
.attr("r", function(d) {return sizeScale(d.children ? d.children.length : 0)}) | |
.style("fill", function(d) {return colorScale(d.depth)}) | |
.style("stroke", "brown") | |
.style("stroke-width", "1px") | |
d3.selectAll("g.node").filter(function(d) {return d.depth == 1}) | |
.append("text") | |
.text(function(d) {return d.depth == 0 ? "Sun" : d.key}) | |
.attr("y", 20) | |
.style("text-anchor", "middle") | |
d3.select("g.viz") | |
.selectAll("circle.ring") | |
.data(orbit.orbitalRings()) | |
.enter() | |
.insert("circle", "g") | |
.attr("class", "ring") | |
.attr("r", function(d) {return d.r}) | |
.attr("cx", function(d) {return d.x}) | |
.attr("cy", function(d) {return d.y}) | |
orbit.on("tick", orbitTick); | |
orbit.start(); | |
function orbitTick() { | |
var newX = 200- center.x; | |
var newY = 200 - center.y; | |
d3.select("g.viz") | |
.attr("transform", "scale("+(1 + (center.depth *.1)) +") translate(" + newX + "," + newY + ")") | |
d3.selectAll("g.node") | |
.attr("transform", function(d) {return "translate(" +d.x +"," + d.y+")"}); | |
d3.selectAll("circle.ring") | |
.attr("cx", function(d) {return d.x}) | |
.attr("cy", function(d) {return d.y}); | |
d3.selectAll("line.visible") | |
.attr("x1", function(p) {return p.source.x}) | |
.attr("x2", function(p) {return p.target.x}) | |
.attr("y1", function(p) {return p.source.y}) | |
.attr("y2", function(p) {return p.target.y}) | |
} | |
function changeCenter() { | |
recenter = false; | |
orbit.stop(); | |
var newX = 200 - center.x; | |
var newY = 200 - center.y; | |
d3.select("g.viz") | |
.transition() | |
.duration(1000) | |
.attr("transform", "scale("+(1 + (center.depth *.1)) +") translate(" + newX + "," + newY + ")") | |
.each("end", function() {orbit.start()}) | |
} | |
function customRevolution(d) | |
{ | |
if (d.name == "time") { | |
return d.depth * .25; | |
} | |
if (d.name == "geo") { | |
return -d.depth * .25; | |
} | |
return d.depth | |
} | |
function nodeOver(d) { | |
orbit.stop(); | |
center = d; | |
changeCenter(); | |
d3.selectAll("text.sat").remove(); | |
d3.selectAll("line.visible").remove(); | |
if (d.children) { | |
var lines = d.children.map(function(p) {return {source: d, target: p}}) | |
d3.select("g.viz").selectAll("line.visible") | |
.data(lines) | |
.enter() | |
.insert("line", "g") | |
.attr("x1", function(p) {return p.source.x}) | |
.attr("x2", function(p) {return p.target.x}) | |
.attr("y1", function(p) {return p.source.y}) | |
.attr("y2", function(p) {return p.target.y}) | |
.attr("class", "visible") | |
.style("stroke", "rgb(73,106,154)") | |
.style("stroke-width", 2) | |
} | |
if (d.parent) { | |
d3.select("g.viz").selectAll("line.fake") | |
.data([{source:d, target: d.parent}]) | |
.enter() | |
.insert("line", "g") | |
.attr("x1", function(p) {return p.source.x}) | |
.attr("x2", function(p) {return p.target.x}) | |
.attr("y1", function(p) {return p.source.y}) | |
.attr("y2", function(p) {return p.target.y}) | |
.attr("class", "visible") | |
.style("stroke", "rgb(165,127,124)") | |
.style("stroke-width", 3) | |
} | |
d3.selectAll("g.node") | |
.filter(function(p) {return p == d || p == d.parent || (d.children ? d.children.indexOf(p) > -1 : false)}) | |
.append("text") | |
.text(function(p) {return p.name}) | |
.style("text-anchor", "middle") | |
.attr("y", 15) | |
.attr("class", "sat") | |
.style("fill", "none") | |
.style("stroke", "white") | |
.style("stroke-width", 3) | |
.style("stroke-opacity", .7); | |
d3.selectAll("g.node") | |
.filter(function(p) {return p == d || p == d.parent || (d.children ? d.children.indexOf(p) > -1 : false)}) | |
.append("text") | |
.text(function(p) {return p.name}) | |
.style("text-anchor", "middle") | |
.attr("y", 15) | |
.attr("class", "sat"); | |
d3.selectAll("g.node > circle").style("stroke", "brown").style("stroke-width", 1); | |
d3.select(this).select("circle").style("stroke", "black").style("stroke-width", 3); | |
} | |
} | |
</script> | |
<body onload="makeViz()"> | |
<div id="viz"><svg></svg><div id="buttons"></div></div> | |
<footer> | |
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8" type="text/javascript"></script> | |
<script src="d3.layout.orbit.js" charset="utf-8" type="text/javascript"></script> | |
</footer> | |
</body> | |
</html> |