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