Still a work in progress. Need to decide on the appropriate API for representing
tree structures; not sure I want to go the heavyweight pv.dom route. May also
need rounding to exact pixel values.
This commit is contained in:
Michael Bostock 2011-02-28 15:04:20 -08:00
Родитель 1d9ed5ae72
Коммит 6b8839eab6
3 изменённых файлов: 510 добавлений и 0 удалений

284
examples/treemap/flare.json Normal file
Просмотреть файл

@ -0,0 +1,284 @@
{
"analytics": {
"cluster": {
"AgglomerativeCluster": 3938,
"CommunityStructure": 3812,
"HierarchicalCluster": 6714,
"MergeEdge": 743
},
"graph": {
"BetweennessCentrality": 3534,
"LinkDistance": 5731,
"MaxFlowMinCut": 7840,
"ShortestPaths": 5914,
"SpanningTree": 3416
},
"optimization": {
"AspectRatioBanker": 7074
}
},
"animate": {
"Easing": 17010,
"FunctionSequence": 5842,
"interpolate": {
"ArrayInterpolator": 1983,
"ColorInterpolator": 2047,
"DateInterpolator": 1375,
"Interpolator": 8746,
"MatrixInterpolator": 2202,
"NumberInterpolator": 1382,
"ObjectInterpolator": 1629,
"PointInterpolator": 1675,
"RectangleInterpolator": 2042
},
"ISchedulable": 1041,
"Parallel": 5176,
"Pause": 449,
"Scheduler": 5593,
"Sequence": 5534,
"Transition": 9201,
"Transitioner": 19975,
"TransitionEvent": 1116,
"Tween": 6006
},
"data": {
"converters": {
"Converters": 721,
"DelimitedTextConverter": 4294,
"GraphMLConverter": 9800,
"IDataConverter": 1314,
"JSONConverter": 2220
},
"DataField": 1759,
"DataSchema": 2165,
"DataSet": 586,
"DataSource": 3331,
"DataTable": 772,
"DataUtil": 3322
},
"display": {
"DirtySprite": 8833,
"LineSprite": 1732,
"RectSprite": 3623,
"TextSprite": 10066
},
"flex": {
"FlareVis": 4116
},
"physics": {
"DragForce": 1082,
"GravityForce": 1336,
"IForce": 319,
"NBodyForce": 10498,
"Particle": 2822,
"Simulation": 9983,
"Spring": 2213,
"SpringForce": 1681
},
"query": {
"AggregateExpression": 1616,
"And": 1027,
"Arithmetic": 3891,
"Average": 891,
"BinaryExpression": 2893,
"Comparison": 5103,
"CompositeExpression": 3677,
"Count": 781,
"DateUtil": 4141,
"Distinct": 933,
"Expression": 5130,
"ExpressionIterator": 3617,
"Fn": 3240,
"If": 2732,
"IsA": 2039,
"Literal": 1214,
"Match": 3748,
"Maximum": 843,
"methods": {
"add": 593,
"and": 330,
"average": 287,
"count": 277,
"distinct": 292,
"div": 595,
"eq": 594,
"fn": 460,
"gt": 603,
"gte": 625,
"iff": 748,
"isa": 461,
"lt": 597,
"lte": 619,
"max": 283,
"min": 283,
"mod": 591,
"mul": 603,
"neq": 599,
"not": 386,
"or": 323,
"orderby": 307,
"range": 772,
"select": 296,
"stddev": 363,
"sub": 600,
"sum": 280,
"update": 307,
"variance": 335,
"where": 299,
"xor": 354,
"_": 264
},
"Minimum": 843,
"Not": 1554,
"Or": 970,
"Query": 13896,
"Range": 1594,
"StringUtil": 4130,
"Sum": 791,
"Variable": 1124,
"Variance": 1876,
"Xor": 1101
},
"scale": {
"IScaleMap": 2105,
"LinearScale": 1316,
"LogScale": 3151,
"OrdinalScale": 3770,
"QuantileScale": 2435,
"QuantitativeScale": 4839,
"RootScale": 1756,
"Scale": 4268,
"ScaleType": 1821,
"TimeScale": 5833
},
"util": {
"Arrays": 8258,
"Colors": 10001,
"Dates": 8217,
"Displays": 12555,
"Filter": 2324,
"Geometry": 10993,
"heap": {
"FibonacciHeap": 9354,
"HeapNode": 1233
},
"IEvaluable": 335,
"IPredicate": 383,
"IValueProxy": 874,
"math": {
"DenseMatrix": 3165,
"IMatrix": 2815,
"SparseMatrix": 3366
},
"Maths": 17705,
"Orientation": 1486,
"palette": {
"ColorPalette": 6367,
"Palette": 1229,
"ShapePalette": 2059,
"SizePalette": 2291
},
"Property": 5559,
"Shapes": 19118,
"Sort": 6887,
"Stats": 6557,
"Strings": 22026
},
"vis": {
"axis": {
"Axes": 1302,
"Axis": 24593,
"AxisGridLine": 652,
"AxisLabel": 636,
"CartesianAxes": 6703
},
"controls": {
"AnchorControl": 2138,
"ClickControl": 3824,
"Control": 1353,
"ControlList": 4665,
"DragControl": 2649,
"ExpandControl": 2832,
"HoverControl": 4896,
"IControl": 763,
"PanZoomControl": 5222,
"SelectionControl": 7862,
"TooltipControl": 8435
},
"data": {
"Data": 20544,
"DataList": 19788,
"DataSprite": 10349,
"EdgeSprite": 3301,
"NodeSprite": 19382,
"render": {
"ArrowType": 698,
"EdgeRenderer": 5569,
"IRenderer": 353,
"ShapeRenderer": 2247
},
"ScaleBinding": 11275,
"Tree": 7147,
"TreeBuilder": 9930
},
"events": {
"DataEvent": 2313,
"SelectionEvent": 1880,
"TooltipEvent": 1701,
"VisualizationEvent": 1117
},
"legend": {
"Legend": 20859,
"LegendItem": 4614,
"LegendRange": 10530
},
"operator": {
"distortion": {
"BifocalDistortion": 4461,
"Distortion": 6314,
"FisheyeDistortion": 3444
},
"encoder": {
"ColorEncoder": 3179,
"Encoder": 4060,
"PropertyEncoder": 4138,
"ShapeEncoder": 1690,
"SizeEncoder": 1830
},
"filter": {
"FisheyeTreeFilter": 5219,
"GraphDistanceFilter": 3165,
"VisibilityFilter": 3509
},
"IOperator": 1286,
"label": {
"Labeler": 9956,
"RadialLabeler": 3899,
"StackedAreaLabeler": 3202
},
"layout": {
"AxisLayout": 6725,
"BundledEdgeRouter": 3727,
"CircleLayout": 9317,
"CirclePackingLayout": 12003,
"DendrogramLayout": 4853,
"ForceDirectedLayout": 8411,
"IcicleTreeLayout": 4864,
"IndentedTreeLayout": 3174,
"Layout": 7881,
"NodeLinkTreeLayout": 12870,
"PieLayout": 2728,
"RadialTreeLayout": 12348,
"RandomLayout": 870,
"StackedAreaLayout": 9121,
"TreeMapLayout": 9191
},
"Operator": 2490,
"OperatorList": 5248,
"OperatorSequence": 4190,
"OperatorSwitch": 2581,
"SortOperator": 2023
},
"Visualization": 16540
}
}

174
examples/treemap/layout.js Normal file
Просмотреть файл

@ -0,0 +1,174 @@
// Squarified Treemaps by Mark Bruls, Kees Huizing, and Jarke J. van Wijk
function layout_treemap() {
var children = layout_treemapChildren,
value = layout_treemapValue,
size = [1, 1]; // width, height
// Recursively compute the node depth and value.
// Also converts the data representation into a standard tree structure.
// Also sorts child nodes by descending value to optimize squarification.
function sum(data, depth, nodes) {
var datas = children.call(treemap, data, depth),
node = {depth: depth, data: data};
nodes.push(node);
if (datas) {
var i = -1,
n = datas.length,
c = node.children = [],
v = 0,
j = depth + 1;
while (++i < n) {
d = sum(datas[i], j, nodes);
if (d.value > 0) { // ignore NaN, negative, etc.
c.push(d);
v += d.value;
}
}
node.value = v;
} else {
node.value = value.call(treemap, data, depth);
}
if (!depth) scale(node, size[0] * size[1] / node.value); // root
return node;
}
// Recursively compute the node area based on value & scale.
function scale(node, k) {
var children = node.children;
node.area = node.value * k;
if (children) {
var i = -1,
n = children.length;
while (++i < n) scale(children[i], k);
}
}
// Arranges the specified children into squarified rows.
function squarify(children, node) {
var rect = {x: node.x, y: node.y, dx: node.dx, dy: node.dy},
row = [],
child,
best = Infinity, // the best row score so far
score, // the current row score
u = Math.min(rect.dx, rect.dy), // initial orientation
n;
children = children.slice(); // copy-on-write
children.sort(function(a, b) { return b.area - a.area; });
row.area = 0;
while ((n = children.length) > 0) {
row.push(child = children[n - 1]);
row.area += child.area;
if ((score = worst(row, u)) <= best) { // continue with this orientation
children.pop();
best = score;
} else { // abort, and try a different orientation
row.area -= row.pop().area;
position(row, u, rect);
u = Math.min(rect.dx, rect.dy);
row.length = row.area = 0;
best = Infinity;
}
}
if (row.length) {
position(row, u, rect);
row.length = row.area = 0;
}
}
// Computes the score for the specified row, as the worst aspect ratio.
function worst(row, u) {
var s = row.area,
r,
rmax = 0,
rmin = Infinity,
i = -1,
n = row.length;
while (++i < n) {
r = row[i].area;
if (r < rmin) rmin = r;
if (r > rmax) rmax = r;
}
s *= s;
u *= u;
return Math.max((u * rmax) / s, s / (u * rmin));
}
// Positions the specified row of nodes. Modifies `rect`.
function position(row, u, rect) {
var i = -1,
n = row.length,
x = rect.x,
y = rect.y,
v = u ? row.area / u : 0,
o;
if (u == rect.dx) { // horizontal subdivision
while (++i < n) {
o = row[i];
o.x = x;
o.y = y;
o.dy = v;
x += o.dx = o.area / v;
}
rect.y += v;
rect.dy -= v;
} else { // vertical subdivision
while (++i < n) {
o = row[i];
o.x = x;
o.y = y;
o.dx = v;
y += o.dy = o.area / v;
}
rect.x += v;
rect.dx -= v;
}
}
// Recursively computes the treemap layout for the node and its children.
function layout(node) {
var children = node.children;
if (children) {
squarify(children, node);
children.forEach(layout);
}
}
function treemap(d) {
var nodes = [],
root = sum(d, 0, nodes);
root.x = 0;
root.y = 0;
root.dx = size[0];
root.dy = size[1];
layout(root);
return nodes;
}
treemap.children = function(x) {
if (!arguments.length) return children;
children = x;
return treemap;
};
treemap.value = function(x) {
if (!arguments.length) return value;
value = x;
return treemap;
};
treemap.size = function(x) {
if (!arguments.length) return size;
size = x;
return treemap;
};
return treemap;
}
function layout_treemapChildren(d) {
return d.children;
}
function layout_treemapValue(d) {
return d.value;
}

Просмотреть файл

@ -0,0 +1,52 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>Treemap</title>
<script type="text/javascript" src="../../d3.js"></script>
<script type="text/javascript" src="layout.js"></script>
<style type="text/css">
body {
font: 10px sans-serif;
}
.cell {
position: absolute;
background: lightsteelblue;
border: solid 1px white;
overflow: hidden;
text-overflow: ellipsis;
}
</style>
</head>
<body>
<script type="text/javascript">
var w = 960,
h = 500;
var div = d3.select("body").append("div")
.style("position", "relative");
var treemap = layout_treemap()
.size([w, h])
.children(function(d, i) { return typeof d.value == "object" && d3.entries(d.value); })
.value(function(d) { return d.value; });
d3.json("flare.json", function(json) {
div.data([{key: "flare", value: json}]).selectAll("div")
.data(treemap)
.enter().append("div")
.attr("class", "cell")
.style("left", function(d) { return d.x + "px"; })
.style("top", function(d) { return d.y + "px"; })
.style("width", function(d) { return d.dx + "px"; })
.style("height", function(d) { return d.dy + "px"; })
.text(function(d) { return d.data.key; });
});
</script>
</body>
</html>