Merge branch 'master' of github.com:mbostock/d3
This commit is contained in:
Коммит
32c06207a2
|
@ -0,0 +1,20 @@
|
|||
#chart {
|
||||
width: 960px;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
.black path {
|
||||
fill: none;
|
||||
stroke: #ccc;
|
||||
stroke-width: 3px;
|
||||
}
|
||||
|
||||
.white path {
|
||||
fill: #fff;
|
||||
stroke: #fff;
|
||||
}
|
||||
|
||||
.grey path {
|
||||
fill: #ccc;
|
||||
stroke: #666;
|
||||
}
|
|
@ -2,86 +2,14 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
||||
<title>Non-Contiguous Cartogram</title>
|
||||
<script type="text/javascript" src="../../d3.js"></script>
|
||||
<script type="text/javascript" src="../../d3.geo.js"></script>
|
||||
<script type="text/javascript" src="../../d3.geom.js"></script>
|
||||
<style type="text/css">
|
||||
|
||||
svg {
|
||||
width: 960px;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
.black path {
|
||||
fill: none;
|
||||
stroke: #ccc;
|
||||
stroke-width: 3px;
|
||||
}
|
||||
|
||||
.white path {
|
||||
fill: #fff;
|
||||
stroke: #fff;
|
||||
}
|
||||
|
||||
.grey path {
|
||||
fill: #ccc;
|
||||
stroke: #666;
|
||||
}
|
||||
|
||||
</style>
|
||||
<link type="text/css" rel="stylesheet" href="cartogram.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
|
||||
var data; // loaded asynchronously
|
||||
|
||||
var svg = d3.select("body")
|
||||
.append("svg:svg");
|
||||
|
||||
d3.json("../../data/us-states.json", function(json) {
|
||||
var path = d3.geo.path();
|
||||
|
||||
// Synthesize a random data variable to visualize…
|
||||
// Note: This has a baked-in sqrt transform!
|
||||
json.features.forEach(function(f) {
|
||||
f.properties.value = Math.sqrt(.2 + .8 * Math.random());
|
||||
});
|
||||
|
||||
// A thick black stroke for the exterior.
|
||||
svg.append("svg:g")
|
||||
.attr("class", "black")
|
||||
.selectAll("path")
|
||||
.data(json.features)
|
||||
.enter().append("svg:path")
|
||||
.attr("d", path);
|
||||
|
||||
// A white overlay to hide interior black strokes.
|
||||
svg.append("svg:g")
|
||||
.attr("class", "white")
|
||||
.selectAll("path")
|
||||
.data(json.features)
|
||||
.enter().append("svg:path")
|
||||
.attr("d", path);
|
||||
|
||||
// The polygons, scaled!
|
||||
svg.append("svg:g")
|
||||
.attr("class", "grey")
|
||||
.selectAll("path")
|
||||
.data(json.features)
|
||||
.enter().append("svg:path")
|
||||
.attr("transform", function(d) {
|
||||
var centroid = path.centroid(d),
|
||||
x = centroid[0],
|
||||
y = centroid[1];
|
||||
return "translate(" + x + "," + y + ")"
|
||||
+ "scale(" + d.properties.value + ")"
|
||||
+ "translate(" + -x + "," + -y + ")";
|
||||
})
|
||||
.attr("stroke-width", function(d) { return 1 / d.properties.value; })
|
||||
.attr("d", path);
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
<div id="chart"></div>
|
||||
<script type="text/javascript" src="cartogram.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
var data; // loaded asynchronously
|
||||
|
||||
var svg = d3.select("#chart")
|
||||
.append("svg:svg");
|
||||
|
||||
d3.json("us-states.json", function(json) {
|
||||
var path = d3.geo.path();
|
||||
|
||||
// Synthesize a random data variable to visualize…
|
||||
// Note: This has a baked-in sqrt transform!
|
||||
json.features.forEach(function(f) {
|
||||
f.properties.value = Math.sqrt(.2 + .8 * Math.random());
|
||||
});
|
||||
|
||||
// A thick black stroke for the exterior.
|
||||
svg.append("svg:g")
|
||||
.attr("class", "black")
|
||||
.selectAll("path")
|
||||
.data(json.features)
|
||||
.enter().append("svg:path")
|
||||
.attr("d", path);
|
||||
|
||||
// A white overlay to hide interior black strokes.
|
||||
svg.append("svg:g")
|
||||
.attr("class", "white")
|
||||
.selectAll("path")
|
||||
.data(json.features)
|
||||
.enter().append("svg:path")
|
||||
.attr("d", path);
|
||||
|
||||
// The polygons, scaled!
|
||||
svg.append("svg:g")
|
||||
.attr("class", "grey")
|
||||
.selectAll("path")
|
||||
.data(json.features)
|
||||
.enter().append("svg:path")
|
||||
.attr("transform", function(d) {
|
||||
var centroid = path.centroid(d),
|
||||
x = centroid[0],
|
||||
y = centroid[1];
|
||||
return "translate(" + x + "," + y + ")"
|
||||
+ "scale(" + d.properties.value + ")"
|
||||
+ "translate(" + -x + "," + -y + ")";
|
||||
})
|
||||
.attr("stroke-width", function(d) { return 1 / d.properties.value; })
|
||||
.attr("d", path);
|
||||
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
../../data/us-states.json
|
|
@ -178,6 +178,7 @@ function drag(n) {
|
|||
var m = d3.svg.mouse(vis[0][0]);
|
||||
active.x = m[0];
|
||||
active.y = m[1];
|
||||
force.resume(); // restart annealing
|
||||
});
|
||||
|
||||
return false;
|
||||
|
@ -221,8 +222,7 @@ function init() {
|
|||
.nodes(net.nodes)
|
||||
.links(net.links)
|
||||
.size({x: w, y: h})
|
||||
.nodeDistance(100)
|
||||
.linkDistance(60)
|
||||
.distance(60)
|
||||
.start();
|
||||
|
||||
hullg.selectAll("path.hull").remove();
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
circle.node {
|
||||
fill: lightsteelblue;
|
||||
stroke: steelblue;
|
||||
stroke: #fff;
|
||||
stroke-width: 1.5px;
|
||||
}
|
||||
|
||||
line.link {
|
||||
stroke: #333;
|
||||
stroke: #999;
|
||||
stroke-opacity: .6;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
var w = 960,
|
||||
h = 500;
|
||||
h = 500,
|
||||
fill = d3.scale.category20();
|
||||
|
||||
var vis = d3.select("#chart")
|
||||
.append("svg:svg")
|
||||
|
@ -28,7 +29,8 @@ d3.json("miserables.json", function(json) {
|
|||
.attr("class", "node")
|
||||
.attr("cx", function(d) { return d.x; })
|
||||
.attr("cy", function(d) { return d.y; })
|
||||
.attr("r", 4.5);
|
||||
.attr("r", 5)
|
||||
.style("fill", function(d) { return fill(d.group); });
|
||||
|
||||
vis.attr("opacity", 0)
|
||||
.transition()
|
||||
|
|
|
@ -3,85 +3,53 @@ function layout_force() {
|
|||
var force = {},
|
||||
event = d3.dispatch("tick"),
|
||||
size = {x: 1, y: 1},
|
||||
alpha = .1,
|
||||
nodeDistance = 60,
|
||||
linkDistance = 30,
|
||||
alpha = .5,
|
||||
distance = 30,
|
||||
interval,
|
||||
nodes,
|
||||
links;
|
||||
|
||||
// TODO
|
||||
// slow the interval as the graph stabilizes
|
||||
// allow the nodes to be dragged interactively
|
||||
links,
|
||||
distances;
|
||||
|
||||
function tick() {
|
||||
var n = nodes.length,
|
||||
m = links.length,
|
||||
var n = distances.length,
|
||||
i, // current index
|
||||
j, // current index
|
||||
o, // current link
|
||||
s, // current source
|
||||
t, // current target
|
||||
l, // current distance
|
||||
x,
|
||||
y;
|
||||
x, // x-distance
|
||||
y; // y-distance
|
||||
|
||||
// repel nodes
|
||||
// gauss-seidel relaxation
|
||||
for (i = 0; i < n; ++i) {
|
||||
s = nodes[i];
|
||||
for (j = i + 1; j < n; ++j) {
|
||||
t = nodes[j];
|
||||
x = t.x - s.x;
|
||||
y = t.y - s.y;
|
||||
l = Math.sqrt(x * x + y * y);
|
||||
if (l < nodeDistance) {
|
||||
l = alpha * (l - nodeDistance) / l;
|
||||
x *= l;
|
||||
y *= l;
|
||||
if (s.fixed) {
|
||||
if (t.fixed) continue;
|
||||
t.x -= x;
|
||||
t.y -= y;
|
||||
} else if (t.fixed) {
|
||||
s.x += x;
|
||||
s.y += y;
|
||||
} else {
|
||||
s.x += x;
|
||||
s.y += y;
|
||||
t.x -= x;
|
||||
t.y -= y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// position constraint for links
|
||||
for (i = 0; i < m; ++i) {
|
||||
o = links[i];
|
||||
o = distances[i];
|
||||
s = o.source;
|
||||
t = o.target;
|
||||
x = t.x - s.x;
|
||||
y = t.y - s.y;
|
||||
l = Math.sqrt(x * x + y * y);
|
||||
if (l <= 0) l = 0.01;
|
||||
l = alpha * (l - linkDistance) / l;
|
||||
x *= l;
|
||||
y *= l;
|
||||
if (s.fixed) {
|
||||
if (t.fixed) continue;
|
||||
t.x -= x;
|
||||
t.y -= y;
|
||||
} else if (t.fixed) {
|
||||
s.x += x;
|
||||
s.y += y;
|
||||
} else {
|
||||
s.x += x;
|
||||
s.y += y;
|
||||
t.x -= x;
|
||||
t.y -= y;
|
||||
if (l = Math.sqrt(x * x + y * y)) {
|
||||
l = alpha / (o.distance * o.distance) * (l - distance * o.distance) / l;
|
||||
x *= l;
|
||||
y *= l;
|
||||
if (s.fixed) {
|
||||
if (t.fixed) continue;
|
||||
t.x -= x;
|
||||
t.y -= y;
|
||||
} else if (t.fixed) {
|
||||
s.x += x;
|
||||
s.y += y;
|
||||
} else {
|
||||
s.x += x;
|
||||
s.y += y;
|
||||
t.x -= x;
|
||||
t.y -= y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// simulated annealing, basically
|
||||
if ((alpha *= .99) < 1e-6) force.stop();
|
||||
|
||||
event.tick.dispatch({type: "tick"});
|
||||
}
|
||||
|
||||
|
@ -108,44 +76,75 @@ function layout_force() {
|
|||
return force;
|
||||
};
|
||||
|
||||
force.nodeDistance = function(d) {
|
||||
if (!arguments.length) return nodeDistance;
|
||||
nodeDistance = d;
|
||||
return force;
|
||||
};
|
||||
|
||||
force.linkDistance = function(d) {
|
||||
if (!arguments.length) return linkDistance;
|
||||
linkDistance = d;
|
||||
force.distance = function(d) {
|
||||
if (!arguments.length) return distance;
|
||||
distance = d;
|
||||
return force;
|
||||
};
|
||||
|
||||
force.start = function() {
|
||||
var i,
|
||||
j,
|
||||
k,
|
||||
n = nodes.length,
|
||||
m = links.length,
|
||||
w = size.x,
|
||||
h = size.y,
|
||||
o;
|
||||
|
||||
var paths = [];
|
||||
for (i = 0; i < n; ++i) {
|
||||
o = nodes[i];
|
||||
o.x = o.x || Math.random() * w;
|
||||
o.y = o.y || Math.random() * h;
|
||||
o.fixed = 0;
|
||||
paths[i] = [];
|
||||
for (j = 0; j < n; ++j) {
|
||||
paths[i][j] = Infinity;
|
||||
}
|
||||
paths[i][i] = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < m; ++i) {
|
||||
o = links[i];
|
||||
paths[o.source][o.target] = 1;
|
||||
paths[o.target][o.source] = 1;
|
||||
o.source = nodes[o.source];
|
||||
o.target = nodes[o.target];
|
||||
}
|
||||
|
||||
// Floyd-Warshall
|
||||
for (k = 0; k < n; ++k) {
|
||||
for (i = 0; i < n; ++i) {
|
||||
for (j = 0; j < n; ++j) {
|
||||
paths[i][j] = Math.min(paths[i][j], paths[i][k] + paths[k][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
distances = [];
|
||||
for (i = 0; i < n; ++i) {
|
||||
for (j = i + 1; j < n; ++j) {
|
||||
distances.push({
|
||||
source: nodes[i],
|
||||
target: nodes[j],
|
||||
distance: paths[i][j] * paths[i][j]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
distances.sort(function(a, b) {
|
||||
return a.distance - b.distance;
|
||||
});
|
||||
|
||||
if (interval) clearInterval(interval);
|
||||
interval = setInterval(tick, 24);
|
||||
return force;
|
||||
};
|
||||
|
||||
force.resume = function() {
|
||||
if (interval) clearInterval(interval);
|
||||
interval = setInterval(tick, 24);
|
||||
alpha = .1;
|
||||
if (!interval) interval = setInterval(tick, 24);
|
||||
return force;
|
||||
};
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
<li><a href="bar/bar.html">bar</a></li>
|
||||
<li><a href="calendar/dji.html">calendar-dji</a></li>
|
||||
<li><a href="calendar/vix.html">calendar-vix</a></li>
|
||||
<li><a href="cartogram/cartogram.html">cartogram</a></li>
|
||||
<li><a href="choropleth/choropleth.html">choropleth</a></li>
|
||||
<li><a href="chord/chord.html">chord</a></li>
|
||||
<li><a href="contour/contour.html">contour</a></li>
|
||||
|
|
|
@ -2,11 +2,6 @@
|
|||
font: 10px sans-serif;
|
||||
}
|
||||
|
||||
rect {
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
line {
|
||||
stroke: black;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ var n = 4, // number of layers
|
|||
|
||||
var p = 20,
|
||||
w = 960,
|
||||
h = 500 - p,
|
||||
h = 500 - .5 - p,
|
||||
mx = m,
|
||||
my = d3.max(data, function(d) {
|
||||
return d3.max(d, function(d) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче