0
|
1 // Copyright (c) 2016 Northrop Grumman.
|
|
2 // All rights reserved.
|
|
3
|
|
4 /*
|
|
5 * Initialize variables for parallelCoordinates display
|
|
6 */
|
|
7 var pcApp = pcApp || {};
|
|
8
|
|
9 pcApp.allSamples = [];
|
|
10 pcApp.selectedSamples = [];
|
|
11 pcApp.origData;
|
|
12 pcApp.flowData;
|
|
13 pcApp.updatedData;
|
|
14 pcApp.headers = [];
|
|
15 pcApp.foreground;
|
|
16 pcApp.background;
|
|
17
|
|
18 var displayAll = function() {
|
|
19 displayParallelPlot();
|
|
20 }
|
|
21 /*
|
|
22 * Display the Population Legend
|
|
23 */
|
|
24 var displaySmpTable = function() {
|
|
25 $('#popTablePC tbody').empty();
|
|
26 pcApp.origData.map(function(d) {
|
|
27 $('#popTablePC tbody').append('<tr><td align="center">'
|
|
28 + '<input type="checkbox" id="' + d.SampleName + '" '
|
|
29 + 'checked class="popSelectPC" value='
|
|
30 + d.SampleNumber + '/></td><td title="' + newSmpNames[d.SampleName]
|
|
31 + '">' + newSmpNames[d.SampleName]
|
|
32 + '</td><td><span style="background-color:'
|
|
33 + color_palette[0][d.SampleNumber + 1][0]
|
|
34 + '"> </span></td></tr>');
|
|
35 });
|
|
36
|
|
37 $('#popSelectAllPC').click(function() {
|
|
38 var checkAll = $("#popSelectAllPC").prop('checked');
|
|
39 if (checkAll) {
|
|
40 $(".popSelectPC").prop("checked", true);
|
|
41 } else {
|
|
42 $(".popSelectPC").prop("checked", false);
|
|
43 }
|
|
44 pcApp.selectedSamples = [];
|
|
45 $('.popSelectPC').each(function() {
|
|
46 if (this.checked) {
|
|
47 pcApp.selectedSamples.push(parseInt(this.value));
|
|
48 }
|
|
49 })
|
|
50 displayTableGrid();
|
|
51 if (checkAll) {
|
|
52 displayParallelPlot();
|
|
53 } else {
|
|
54 updateParallelForeground();
|
|
55 }
|
|
56 });
|
|
57
|
|
58 $('.popSelectPC').click(function() {
|
|
59 if ($('.popSelectPC').length == $(".popSelectPC:checked").length) {
|
|
60 $('#popSelectAllPC').prop("checked",true);
|
|
61 } else {
|
|
62 $('#popSelectAllPC').prop("checked",false);
|
|
63 }
|
|
64 pcApp.selectedSamples = [];
|
|
65 $('.popSelectPC').each(function() {
|
|
66 if (this.checked) {
|
|
67 pcApp.selectedSamples.push(parseInt(this.value));
|
|
68 }
|
|
69 })
|
|
70 displayTableGrid();
|
|
71 updateParallelForeground();
|
|
72 });
|
|
73 updateSmpTable();
|
|
74 };
|
|
75
|
|
76 var updateSmpTable = function() {
|
|
77 $('.popSelectPC').each(function() {
|
|
78 var smp = parseInt(this.value);
|
|
79 if ($.inArray(smp,pcApp.selectedSamples) > -1) {
|
|
80 this.checked = true;
|
|
81 } else {
|
|
82 this.checked = false;
|
|
83 }
|
|
84 })
|
|
85 }
|
|
86
|
|
87 var displayTableGrid = function() {
|
|
88 var colTable = [],
|
|
89 colNames = [],
|
|
90 pctargets = [],
|
|
91 updatedHeaders = [],
|
|
92 displayData = [],
|
|
93 targetCol = 0,
|
|
94 textCol = [],
|
|
95 colOrder = [],
|
|
96 tableHTML = [];
|
|
97
|
|
98 $("#tableDivPC").empty();
|
|
99 pcApp.updatedData = $.extend(true,[],pctablecontent);
|
|
100 pcApp.updatedData.forEach(function(d, idx){
|
|
101 d.SampleName = idx + 1;
|
|
102 delete(d.FileID);
|
|
103 });
|
|
104
|
|
105 updatedHeaders = Object.keys(pcApp.updatedData[0]);
|
|
106 displayData = pcApp.updatedData.filter(function(d,i) {
|
|
107 if ($.inArray(i,pcApp.selectedSamples) > -1) {
|
|
108 return d;
|
|
109 }
|
|
110 });
|
|
111
|
|
112 targetCol = updatedHeaders.length - 2;
|
|
113 updatedHeaders.forEach(function(d,i){
|
|
114 colTable.push("<th>" + d + "</th>");
|
|
115 colNames.push({"data":d});
|
|
116 if (i < targetCol){
|
|
117 pctargets.push(i);
|
|
118 }
|
|
119 });
|
|
120 textCol = [targetCol, targetCol + 1];
|
|
121 colOrder = textCol.concat(pctargets);
|
|
122 tableHTML = [
|
|
123 '<table id="pcTable" class="pctable display compact nowrap" cellspacing="0" width="100%">',
|
|
124 '<thead>',
|
|
125 '<tr>',
|
|
126 colTable.join("\n"),
|
|
127 '</tr>',
|
|
128 '</thead>',
|
|
129 '</table>',
|
|
130 ];
|
|
131
|
|
132 $('#tableDivPC').html(tableHTML.join("\n"));
|
|
133 var pcTable = $('#pcTable').DataTable({
|
|
134 columns: colNames,
|
|
135 data: displayData,
|
|
136 order: [[ targetCol, "asc" ]],
|
|
137 pageLength: 10,
|
|
138 //paging: false,
|
|
139 scrollY: 250,
|
|
140 scrollCollapse: true,
|
|
141 scrollX: true,
|
|
142 dom: '<"top"B>t<"bottom"lip><"clear">',
|
|
143 columnDefs: [{
|
|
144 targets: pctargets,
|
|
145 className: "dt-body-right",
|
|
146 render: function(data,type,row){
|
|
147 return parseFloat(data).toFixed(2) + '%';
|
|
148 }
|
|
149 }, {
|
|
150 targets: [targetCol, targetCol+1],
|
|
151 className: "dt-body-center"
|
|
152 }, {
|
|
153 targets:[targetCol],
|
|
154 render: function(data, type, row){
|
|
155 return 'Sample' + parseInt(data);
|
|
156 }
|
|
157 }],
|
|
158 buttons: [
|
|
159 'copy', 'pdfHtml5','csvHtml5', 'colvis'
|
|
160 ],
|
|
161 colReorder: {order:colOrder},
|
|
162 select: true
|
|
163 });
|
|
164
|
|
165 $('#pcTable').on('mouseover', 'tr', function() {
|
|
166 var data = pcTable.row(this).data();
|
|
167 if (data != undefined) {
|
|
168 var smp = parseInt(data.SampleName) - 1;
|
|
169 pcApp.selectedSamples = [smp];
|
|
170 updateParallelForeground();
|
|
171 }
|
|
172 });
|
|
173 $('#pcTable').on('mouseleave', 'tr', function() {
|
|
174 pcApp.selectedSamples = [];
|
|
175 $('.popSelectPC').each(function() {
|
|
176 if (this.checked) {
|
|
177 pcApp.selectedSamples.push(parseInt(this.value));
|
|
178 }
|
|
179 updateParallelForeground();
|
|
180 })
|
|
181 });
|
|
182 };
|
|
183 /*
|
|
184 * Display The Main Plot
|
|
185 */
|
|
186 var displayParallelPlot = function() {
|
|
187 var margin = {top: 30, right: 10, bottom: 10, left: 10},
|
|
188 h = 300,
|
|
189 w = $("#plotDivPC").width(),
|
|
190 width = w - margin.left - margin.right,
|
|
191 height = h - margin.top - margin.bottom,
|
|
192 y = {},
|
|
193 dragging = {},
|
|
194 ymax = 0;
|
|
195
|
|
196 $("#plotDivPC").empty();
|
|
197 $("#plotDivPC").height(h);
|
|
198 var svg = d3.select("#plotDivPC").append("svg")
|
|
199 .attr("width", width + margin.left + margin.right)
|
|
200 .attr("height", height + margin.top + margin.bottom)
|
|
201 .append("g")
|
|
202 .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
|
|
203
|
|
204 var x = d3.scale.ordinal().rangePoints([0, width], 1);
|
|
205 var line = d3.svg.line();
|
|
206 var axis = d3.svg.axis()
|
|
207 .orient("left")
|
|
208 .tickFormat(d3.format("d"))
|
|
209 .ticks(5);
|
|
210
|
|
211 for (var m = 0, n = pcApp.flowData.length; m < n; m++){
|
|
212 for (var p in pcApp.flowData[m]){
|
|
213 if (+pcApp.flowData[m][p] > ymax){
|
|
214 ymax = +pcApp.flowData[m][p];
|
|
215 }
|
|
216 }
|
|
217 }
|
|
218
|
|
219 // Y axis label
|
|
220 svg.append("text")
|
|
221 .attr("class", "ylabel")
|
|
222 .attr("transform", "rotate(-90)")
|
|
223 .attr("y", 0 - margin.left)
|
|
224 .attr("x",0 - (height / 2))
|
|
225 .attr("dy", "1em")
|
|
226 .style("text-anchor", "middle")
|
|
227 .text("Fraction of population in sample");
|
|
228
|
|
229 var dimensions = d3.keys(pcApp.flowData[0]).filter(function(d) {
|
|
230 return (y[d] = d3.scale.linear()
|
|
231 .domain([0,parseInt(ymax)+1])
|
|
232 .range([height, 0]));
|
|
233 });
|
|
234 x.domain(dimensions);
|
|
235
|
|
236 function path(d) {
|
|
237 return line(dimensions.map(function(p) { return [x(p), y[p](d[p])]; }));
|
|
238 }
|
|
239
|
|
240 function position(d) {
|
|
241 var v = dragging[d];
|
|
242 return v == null ? x(d) : v;
|
|
243 }
|
|
244
|
|
245 function transition(g) {
|
|
246 return g.transition().duration(500);
|
|
247 }
|
|
248
|
|
249 function brush() {
|
|
250 var actives = dimensions.filter(function(p) { return !y[p].brush.empty(); });
|
|
251 var extents = actives.map(function(p) { return y[p].brush.extent(); });
|
|
252 var selectedSamples = pcApp.origData.filter(function(d) {
|
|
253 var smp = parseInt(d.SampleNumber);
|
|
254 var tf = actives.every(function(p,i) {
|
|
255 return extents[i][0] <= pcApp.flowData[smp][p] &&
|
|
256 pcApp.flowData[smp][p] <= extents[i][1];
|
|
257 });
|
|
258 if (tf) {
|
|
259 return smp.toString();
|
|
260 }
|
|
261 });
|
|
262 pcApp.selectedSamples = selectedSamples.map(function(d) {
|
|
263 return parseInt(d.SampleNumber);
|
|
264 });
|
|
265
|
|
266 updateParallelForeground();
|
|
267 updateSmpTable()
|
|
268 displayTableGrid();
|
|
269 }
|
|
270 // Display paths in light gray color, to use as reference
|
|
271 pcApp.background = svg.append("g")
|
|
272 .attr("class", "background")
|
|
273 .selectAll("path")
|
|
274 .data(pcApp.flowData)
|
|
275 .enter().append("path")
|
|
276 .attr("d", path);
|
|
277
|
|
278 // Add foreground lines for focus, color by population.
|
|
279 pcApp.foreground = svg.append("g")
|
|
280 .attr("class", "foreground")
|
|
281 .selectAll("path")
|
|
282 .data(pcApp.origData)
|
|
283 .enter().append("path")
|
|
284 .attr("d", function(d) {
|
|
285 var smp = d.SampleNumber;
|
|
286 return path(pcApp.flowData[smp]); })
|
|
287 .attr("stroke",function(d){
|
|
288 var smp = d.SampleNumber + 1;
|
|
289 return color_palette[0][smp][0];})
|
|
290 .attr("stroke-width", 1);
|
|
291
|
|
292 // Add a group element for each dimension.
|
|
293 var g = svg.selectAll(".dimension")
|
|
294 .data(dimensions)
|
|
295 .enter().append("g")
|
|
296 .attr("class", "dimension")
|
|
297 .attr("transform", function(d) { return "translate(" + x(d) + ")"; })
|
|
298 .call(d3.behavior.drag()
|
|
299 .origin(function(d) { return {x: x(d)}; })
|
|
300 .on("dragstart", function(d) {
|
|
301 dragging[d] = x(d);
|
|
302 pcApp.background.attr("visibility", "hidden");})
|
|
303 .on("drag", function(d) {
|
|
304 dragging[d] = Math.min(width, Math.max(0, d3.event.x));
|
|
305 pcApp.foreground.attr("d", path);
|
|
306 dimensions.sort(function(a, b) { return position(a) - position(b); });
|
|
307 x.domain(dimensions);
|
|
308 g.attr("transform", function(d) { return "translate(" + position(d) + ")"; }); })
|
|
309 .on("dragend", function(d) {
|
|
310 delete dragging[d];
|
|
311 transition(d3.select(this)).attr("transform", "translate(" + x(d) + ")");
|
|
312 transition(pcApp.foreground).attr("d", path);
|
|
313 pcApp.background
|
|
314 .attr("d", path)
|
|
315 .transition()
|
|
316 .delay(500)
|
|
317 .duration(0)
|
|
318 .attr("visibility", null);
|
|
319 }));
|
|
320
|
|
321 // Add an axis and title.
|
|
322 g.append("g")
|
|
323 .attr("class", "axis")
|
|
324 .each(function(d) { d3.select(this).call(axis.scale(y[d])); });
|
|
325 g.append("g")
|
|
326 .attr("class", "xlabel")
|
|
327 .append("text")
|
|
328 .style("text-anchor", "middle")
|
|
329 .attr("y", -9)
|
|
330 .text(function(d) { return d; });
|
|
331
|
|
332 // Add and store a brush for each axis.
|
|
333 g.append("g")
|
|
334 .attr("class", "brush")
|
|
335 .each(function(d) { d3.select(this).call(y[d].brush = d3.svg.brush().y(y[d]).on("brush", brush)); })
|
|
336 .selectAll("rect")
|
|
337 .attr("x", -8)
|
|
338 .attr("width", 16);
|
|
339
|
|
340 // Control line opacity.
|
|
341 $('#PCline_opacity').on('change', (function() {
|
|
342 var val = $(this).val();
|
|
343 $('#plotDivPC .foreground path').css('stroke-opacity', val.toString());
|
|
344 $('#pc_opacity').html((Math.round(val*10000)/100) + "%");
|
|
345 }));
|
|
346 };
|
|
347
|
|
348 var updateParallelForeground = function() {
|
|
349 pcApp.foreground[0].map(function(d) {
|
|
350 var smp = parseInt(d['__data__']['SampleNumber'])
|
|
351 if ($.inArray(smp,pcApp.selectedSamples) < 0) {
|
|
352 d.style.display = "none";
|
|
353 } else {
|
|
354 d.style.display = null;
|
|
355 }
|
|
356 });
|
|
357 };
|
|
358 /*
|
|
359 * Retrieve the data, then call display functions
|
|
360 */
|
|
361 var displayParallelCoordinates = function() {
|
|
362 /* var inputFile = "./csOverview.tsv";
|
|
363 d3.tsv(inputFile, function(error, data) {
|
|
364 if (error) {
|
|
365 alert("Problem Retrieving Data");
|
|
366 return;
|
|
367 }
|
|
368 */
|
|
369 pcApp.origData = $.extend(true,[], pctablecontent);
|
|
370 pcApp.headers = Object.keys(pcApp.origData[0]);
|
|
371 pcApp.headers.splice(pcApp.headers.indexOf("FileID"), 1);
|
|
372 pcApp.origData.forEach(function(d,idx){
|
|
373 d.SampleNumber = idx;
|
|
374 // delete d.FileID;
|
|
375 })
|
|
376 /*
|
|
377 * For the plot use only the proportion of each
|
|
378 * population per sample. Store in flowData
|
|
379 */
|
|
380 pcApp.flowData = $.extend(true,[],pctablecontent);
|
|
381 pcApp.flowData.forEach(function(d,idx){
|
|
382 delete d.SampleName;
|
|
383 delete d.FileID;
|
|
384 delete d.Comment;
|
|
385 });
|
|
386 for (var i = 0, j = pcApp.flowData.length; i < j ; i++) {
|
|
387 pcApp.allSamples.push(i);
|
|
388 pcApp.selectedSamples.push(i);
|
|
389 }
|
|
390 displaySmpTable();
|
|
391 displayTableGrid();
|
|
392 displayParallelPlot();
|
|
393
|
|
394 $("#resetPCDisplay").on("click",function() {
|
|
395 var opcty = ".8";
|
|
396 for (var i = 0, j = pcApp.flowData.length; i < j; i++) {
|
|
397 pcApp.allSamples.push(i);
|
|
398 pcApp.selectedSamples.push(i);
|
|
399 }
|
|
400 $("#smpSelectAllPC").prop('checked',true);
|
|
401 $(".smpSelectPC").prop("checked",true);
|
|
402
|
|
403 $('#plotDivPC .foreground path').css('stroke-opacity', opcty);
|
|
404 $('#pc_opacity').html("80%");
|
|
405 $('#PCline_opacity').val(0.8);
|
|
406
|
|
407 displaySmpTable();
|
|
408 displayTableGrid();
|
|
409 displayParallelPlot();
|
|
410 });
|
|
411
|
|
412 $(window).on('resize',function() {
|
|
413 waitForFinalEvent(function() {
|
|
414 displayAll();
|
|
415 }, 500, "resizePC");
|
|
416 });
|
|
417 // });
|
|
418 };
|