|
1 <!DOCTYPE html> |
|
2 <html> |
|
3 <head> |
|
4 <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/> |
|
5 <title>Parallel Coordinates</title> |
|
6 <style type="text/css"> |
|
7 |
|
8 svg { |
|
9 font: 10px sans-serif; |
|
10 } |
|
11 |
|
12 .background path { |
|
13 fill: none; |
|
14 stroke: #ccc; |
|
15 stroke-opacity: .4; |
|
16 shape-rendering: crispEdges; |
|
17 } |
|
18 |
|
19 .foreground path { |
|
20 fill: none; |
|
21 stroke: steelblue; |
|
22 stroke-opacity: .7; |
|
23 } |
|
24 |
|
25 .brush .extent { |
|
26 fill-opacity: .3; |
|
27 stroke: #fff; |
|
28 shape-rendering: crispEdges; |
|
29 } |
|
30 |
|
31 .axis line, .axis path { |
|
32 fill: none; |
|
33 stroke: #000; |
|
34 shape-rendering: crispEdges; |
|
35 } |
|
36 |
|
37 .axis text { |
|
38 text-shadow: 0 1px 0 #fff; |
|
39 } |
|
40 |
|
41 </style> |
|
42 </head> |
|
43 <body> |
|
44 <script type="text/javascript" src="../../d3.js"></script> |
|
45 <script type="text/javascript" src="../../d3.csv.js"></script> |
|
46 <script type="text/javascript"> |
|
47 |
|
48 var m = [30, 10, 10, 10], |
|
49 w = 960 - m[1] - m[3], |
|
50 h = 500 - m[0] - m[2]; |
|
51 |
|
52 var x = d3.scale.ordinal().rangePoints([0, w], 1), |
|
53 y = {}; |
|
54 |
|
55 var line = d3.svg.line(), |
|
56 axis = d3.svg.axis().orient("left"), |
|
57 background, |
|
58 foreground; |
|
59 |
|
60 var svg = d3.select("body").append("svg:svg") |
|
61 .attr("width", w + m[1] + m[3]) |
|
62 .attr("height", h + m[0] + m[2]) |
|
63 .append("svg:g") |
|
64 .attr("transform", "translate(" + m[3] + "," + m[0] + ")"); |
|
65 |
|
66 d3.csv("cars.csv", function(cars) { |
|
67 |
|
68 // Extract the list of dimensions and create a scale for each. |
|
69 x.domain(dimensions = d3.keys(cars[0]).filter(function(d) { |
|
70 return d != "name" && (y[d] = d3.scale.linear() |
|
71 .domain(d3.extent(cars, function(p) { return +p[d]; })) |
|
72 .range([h, 0])); |
|
73 })); |
|
74 |
|
75 // Add grey background lines for context. |
|
76 background = svg.append("svg:g") |
|
77 .attr("class", "background") |
|
78 .selectAll("path") |
|
79 .data(cars) |
|
80 .enter().append("svg:path") |
|
81 .attr("d", path); |
|
82 |
|
83 // Add blue foreground lines for focus. |
|
84 foreground = svg.append("svg:g") |
|
85 .attr("class", "foreground") |
|
86 .selectAll("path") |
|
87 .data(cars) |
|
88 .enter().append("svg:path") |
|
89 .attr("d", path); |
|
90 |
|
91 // Add a group element for each dimension. |
|
92 var g = svg.selectAll(".dimension") |
|
93 .data(dimensions) |
|
94 .enter().append("svg:g") |
|
95 .attr("class", "dimension") |
|
96 .attr("transform", function(d) { return "translate(" + x(d) + ")"; }); |
|
97 |
|
98 // Add an axis and title. |
|
99 g.append("svg:g") |
|
100 .attr("class", "axis") |
|
101 .each(function(d) { d3.select(this).call(axis.scale(y[d])); }) |
|
102 .append("svg:text") |
|
103 .attr("text-anchor", "middle") |
|
104 .attr("y", -9) |
|
105 .text(String); |
|
106 |
|
107 // Add and store a brush for each axis. |
|
108 g.append("svg:g") |
|
109 .attr("class", "brush") |
|
110 .each(function(d) { d3.select(this).call(y[d].brush = d3.svg.brush().y(y[d]).on("brush", brush)); }) |
|
111 .selectAll("rect") |
|
112 .attr("x", -8) |
|
113 .attr("width", 16); |
|
114 }); |
|
115 |
|
116 // Returns the path for a given data point. |
|
117 function path(d) { |
|
118 return line(dimensions.map(function(p) { return [x(p), y[p](d[p])]; })); |
|
119 } |
|
120 |
|
121 // Handles a brush event, toggling the display of foreground lines. |
|
122 function brush() { |
|
123 var actives = dimensions.filter(function(p) { return !y[p].brush.empty(); }), |
|
124 extents = actives.map(function(p) { return y[p].brush.extent(); }); |
|
125 foreground.style("display", function(d) { |
|
126 return actives.every(function(p, i) { |
|
127 return extents[i][0] <= d[p] && d[p] <= extents[i][1]; |
|
128 }) ? null : "none"; |
|
129 }); |
|
130 } |
|
131 |
|
132 </script> |
|
133 </body> |
|
134 </html> |