2 * K-means and hierarchical cluster analysis and vizualization
3 * Isaak Y Tecle <iyt2@cornell.edu>
7 var solGS = solGS || function solGS() { };
10 canvas: "#cluster_canvas",
11 clusterPlotDiv: "#cluster_plot",
12 clusterMsgDiv: "#cluster_message",
13 clusterPopsDiv: "#cluster_pops_select_div",
14 clusterPopsSelectMenuId: "#cluster_pops_select",
15 clusterPopsDataDiv: "#cluster_pops_data_div",
17 getClusterArgsFromUrl: function () {
18 var page = location.pathname;
19 if (page == "/cluster/analysis/") {
20 page = "/cluster/analysis";
23 var urlArgs = page.replace("/cluster/analysis", "");
36 var args = urlArgs.split(/\/+/);
37 clusterPopId = args[1];
38 clusterType = args[3];
42 if (urlArgs.match(/traits/)) {
46 if (urlArgs.match(/selPop/)) {
47 selectionProp = args[11];
50 protocolId = args.pop();
52 if (!dataType.match(/phenotype|genotype|gebv/)) {
53 sIndexName = dataType;
54 dataType = "genotype";
61 if (clusterPopId.match(/dataset/)) {
63 datasetId = clusterPopId.replace(/dataset_/, "");
64 } else if (clusterPopId.match(/list/)) {
66 listId = clusterPopId.replace(/list_/, "");
70 cluster_pop_id: clusterPopId,
73 sindex_name: sIndexName,
74 selection_proportion: selectionProp,
77 training_traits_code: traitsCode,
78 dataset_id: datasetId,
79 data_structure: dataStr,
80 genotyping_protocol_id: protocolId,
81 cluster_type: clusterType,
85 if (clusterPopId.match(reg)) {
86 var ids = clusterPopId.split("-");
87 args["training_pop_id"] = ids[0];
88 args["selection_pop_id"] = ids[1];
96 getClusterPopId: function (selectedId, dataStr) {
100 clusterPopId = `${dataStr}_${selectedId}`;
102 clusterPopId = selectedId;
108 createClusterTypeSelect: function (rowId) {
109 var clusterTypeId = this.clusterTypeSelectId(rowId);
110 var clusterTypeGroup =
111 '<div id="cluster_type_opts"><select class="form-control" id="' +
114 '<option value="k-means">K-Means</option>' +
115 '<option value="hierarchical">Hierarchical</option>' +
118 return clusterTypeGroup;
121 clusterTypeSelectId: function (rowId) {
122 if (location.pathname.match(/cluster\/analysis/) && rowId) {
123 return `cluster_type_select_${rowId}`;
125 return "cluster_type_select";
129 clusterDataTypeSelectId: function (rowId) {
130 if (location.pathname.match(/cluster\/analysis/) && rowId) {
131 return `cluster_data_type_select_${rowId}`;
133 return "cluster_data_type_select";
137 clusterKnumSelectId: function (rowId) {
138 if (location.pathname.match(/cluster\/analysis/) && rowId) {
139 return `k_number_input_${rowId}`;
141 return "k_number_input";
145 clusterSelPropSelectId: function (rowId) {
146 if (location.pathname.match(/cluster\/analysis/) && rowId) {
147 return `selection_proportion_input_${rowId}`;
149 return "selection_proportion_input";
153 getRunClusterBtnId: function (rowId) {
154 if (location.pathname.match(/cluster\/analysis/) && rowId) {
155 return `run_cluster_${rowId}`;
157 return "run_cluster";
161 createDataTypeSelect: function (opts, rowId) {
162 var clusterDataTypeId = this.clusterDataTypeSelectId(rowId);
163 var dataTypeGroup = '<select class="form-control" id="' + clusterDataTypeId + '">';
165 for (var i = 0; i < opts.length; i++) {
166 dataTypeGroup += '<option value="' + opts[i] + '">' + opts[i] + "</option>";
168 dataTypeGroup += "</select>";
170 return dataTypeGroup;
173 getDataTypeOpts: function (args) {
180 var dataTypeOpts = [];
181 var page = location.pathname;
183 if (page.match(/breeders\/trial/)) {
184 dataTypeOpts = ["Genotype", "Phenotype"];
185 } else if (page.match(/solgs\/trait\/\d+\/population\/|solgs\/model\/combined\/trials\//)) {
186 dataTypeOpts = ["Genotype"];
192 if (popType.match(/^selection$/)) {
193 dataTypeOpts = ["Genotype", "GEBV"];
194 } else if (popType.match(/selection_index/)) {
195 dataTypeOpts = ["Genotype"];
197 dataTypeOpts = ["Genotype", "GEBV", "Phenotype"];
204 createRowElements: function (clusterPop) {
205 var popId = clusterPop.id;
206 var popName = clusterPop.name;
207 var dataStr = clusterPop.data_str;
209 var clusterPopId = solGS.cluster.getClusterPopId(popId, dataStr);
210 var clusterTypeOpts = solGS.cluster.createClusterTypeSelect(clusterPopId);
213 if (location.pathname.match(/pca\/analysis/)) {
214 dataTypes = clusterPop.data_type;
216 dataTypes = this.getDataTypeOpts();
219 var dataTypeOpts = solGS.cluster.createDataTypeSelect(dataTypes, clusterPopId);
220 var kNumId = solGS.cluster.clusterKnumSelectId(clusterPopId);
221 var runClusterBtnId = solGS.cluster.getRunClusterBtnId(clusterPopId);
223 var kNum = '<input class="form-control" type="text" placeholder="3" id="' + kNumId + '"/>';
225 var clusterArgs = JSON.stringify(clusterPop);
228 `<button type="button" id=${runClusterBtnId}` +
229 ` class="btn btn-success" data-selected-pop='${clusterArgs}'>Run cluster</button>`;
231 if (dataStr.match(/dataset/)) {
232 popName = `<a href="/dataset/${popId}">${popName}</a>`;
234 var rowData = [popName,
235 dataStr, clusterPop.owner, clusterTypeOpts,
236 dataTypeOpts, kNum, runClusterBtn, `${dataStr}_${popId}`];
241 createTable: function (tableId) {
243 '<table class="table table-striped" id="' +
249 "<th>Data structure</th>" +
250 "<th>Ownership</th>" +
251 "<th>Clustering method</th>" +
252 "<th>Data type</th>" +
253 "<th>No. of clusters (K)</th>" +
254 "<th>Run cluster</th>" +
261 clusterResult: function (clusterArgs) {
262 var clusterPopId = clusterArgs.cluster_pop_id;
263 var clusterType = clusterArgs.cluster_type;
264 var kNumber = clusterArgs.k_number;
265 var dataType = clusterArgs.data_type;
266 var selectionProp = clusterArgs.selection_proportion;
267 var selectedId = clusterArgs.selected_id;
268 var selectedName = clusterArgs.selected_name;
269 var dataStr = clusterArgs.data_structure;
271 dataType = dataType.toLowerCase();
272 clusterType = clusterType.toLowerCase();
273 var protocolId = jQuery("#cluster_div #genotyping_protocol #genotyping_protocol_id").val();
276 protocolId = solGS.genotypingProtocol.getGenotypingProtocolId("cluster_div");
279 var trainingTraitsIds = jQuery("#training_traits_ids").val();
281 if (trainingTraitsIds) {
282 trainingTraitsIds = trainingTraitsIds.split(",");
285 if (!trainingTraitsIds) {
286 var traitId = jQuery("#trait_id").val();
287 trainingTraitsIds = [traitId];
290 if (trainingTraitsIds == "") {
291 trainingTraitsIds = [];
294 var popDetails = solGS.getPopulationDetails();
303 var page = location.pathname;
304 if (!page.match(/cluster\/analysis/)) {
306 page.match(/solgs\/trait\/\d+\/population\/|solgs\/model\/combined\/populations\/|breeders\//)
308 popId = popDetails.training_pop_id;
309 popName = popDetails.training_pop_name;
310 popType = "training";
311 } else if (page.match(/solgs\/selection\/|solgs\/model\/combined\/trials\//)) {
312 popId = popDetails.selection_pop_id;
313 popName = popDetails.selection_pop_name;
314 popType = "selection";
316 popId = jQuery("#cluster_selected_pop_id").val();
317 popType = jQuery("#cluster_selected_pop_type").val();
318 popName = jQuery("#cluster_selected_pop_name").val();
324 selectedName = popName;
333 data_structure: dataStr,
335 selection_proportion: selectionProp,
339 var message = this.validateClusterParams(validateArgs);
340 var url = location.pathname;
342 var clusterMsgDiv = this.clusterMsgDiv;
344 if (message != undefined) {
345 jQuery(clusterMsgDiv).html(message).show().fadeOut(9400);
347 if (url.match(/solgs\/models\/combined\/trials\//)) {
348 if (popType.match(/training/)) {
349 popDetails["combo_pops_id"] = popId;
350 } else if (popType.match(/selection/)) {
351 popDetails["selection_pop_id"] = popId;
360 if (String(selectedId).match(/list/)) {
362 } else if (String(selectedId).match(/dataset/)) {
366 if (dataStr == "list") {
367 if (isNaN(selectedId)) {
368 listId = selectedId.replace("list_", "");
372 } else if (dataStr == "dataset") {
373 if (isNaN(selectedId)) {
374 datasetId = selectedId.replace("dataset_", "");
376 datasetId = selectedId;
379 datasetName = selectedName;
383 if (url.match(/solgs\/trait\//)) {
384 clusterPopId = popDetails.training_pop_id;
385 } else if (url.match(/solgs\/selection\//)) {
386 clusterPopId = popDetails.selection_pop_id;
387 } else if (url.match(/combined/)) {
388 clusterPopId = jQuery("#combo_pops_id").val();
392 if (popType == "selection_index") {
393 sIndexName = selectedName;
399 var fileId = clusterPopId;
400 if (location.pathname.match(/cluster\/analysis/)) {
402 "/cluster/analysis/" +
410 if (dataType.match(/genotype/i)) {
411 page = page + "/gp/" + protocolId;
414 traitsCode = jQuery("#training_traits_code").val();
416 popType.match(/selection/) &&
417 location.pathname.match(/solgs\/traits\/all\/|solgs\/models\/combined\/trials\//)
419 popDetails["selection_pop_id"] = clusterPopId;
420 fileId = popDetails.training_pop_id + "-" + clusterPopId;
424 "/cluster/analysis/" +
434 "/cluster/analysis/" +
444 page = page + "/traits/" + traitsCode;
446 page = page + "/traits/" + "undefined";
450 page = page + "/sp/" + selectionProp;
452 page = page + "/sp/" + "undefined";
455 page = page + "/gp/" + protocolId;
459 training_pop_id: popDetails.training_pop_id,
460 selection_pop_id: popDetails.selection_pop_id,
461 combo_pops_id: popDetails.combo_pops_id,
462 training_traits_ids: trainingTraitsIds,
463 training_traits_code: traitsCode,
464 cluster_pop_id: clusterPopId,
466 cluster_type: clusterType,
467 data_structure: dataStr,
468 dataset_id: datasetId,
469 dataset_name: datasetName,
472 selection_proportion: selectionProp,
473 sindex_name: sIndexName,
474 cluster_pop_name: selectedName || "",
475 genotyping_protocol_id: protocolId,
476 analysis_type: "cluster analysis",
484 checkCachedCluster: function (page, args) {
485 if (typeof args !== "string") {
486 args = JSON.stringify(args);
489 var checkCached = jQuery.ajax({
496 url: "/solgs/check/cached/result/",
503 runClusterAnalysis: function (clusterArgs) {
505 if (typeof clusterArgs == "string") {
506 clusterArgs = JSON.parse(clusterArgs);
507 clusterType = clusterArgs.cluster_type;
508 clusterPopId = clusterArgs.cluster_pop_id;
509 runClusterBtnId = this.getRunClusterBtnId(clusterPopId);
511 clusterType = clusterArgs.cluster_type;
512 clusterPopId = clusterArgs.cluster_pop_id;
513 runClusterBtnId = this.getRunClusterBtnId(clusterPopId);
516 if (typeof clusterArgs !== "string") {
517 clusterArgs = JSON.stringify(clusterArgs);
520 var runAnalysis = jQuery.ajax({
524 arguments: clusterArgs,
526 url: "/run/cluster/analysis",
533 validateClusterParams: function (valArgs) {
534 var popType = valArgs.pop_type;
535 var dataType = valArgs.data_type;
536 var selectionProp = valArgs.selection_proportion;
537 var dataStr = valArgs.data_structure;
538 var dataId = valArgs.data_id;
541 if (popType == "selection_index") {
542 if (dataType.match(/phenotype/i) || dataType.match(/gebv/i)) {
544 "K-means clustering for selection index type" + " data works with genotype data only.";
547 if (dataType.match(/genotype/i) != null && !selectionProp) {
549 "The selection proportion value is empty." +
550 " You need to define the fraction of the" +
551 " population you want to select.";
555 if (dataStr == "list") {
556 var list = new CXGN.List();
559 dataId = dataId.replace(/list_/, "");
562 var listType = list.getListType(dataId);
564 if (listType == "accessions" && dataType.match(/phenotype/i)) {
565 msg = "With list of clones, you can only cluster based on <em>genotype</em>.";
568 if (listType == "plots" && dataType.match(/genotype/i)) {
569 msg = "With list of plots, you can only cluster based on <em>phenotype</em>.";
576 createClusterDownloadLinks: function (res) {
577 var popName = res.cluster_pop_name || "";
578 var clusterPlotFileName = res.cluster_plot.split("/").pop();
583 var kclusterMeansFile;
584 var kclusterVariancesFile;
586 if (clusterPlotFileName.match(/k-means/i)) {
587 plotType = "K-means plot";
588 outFileType = "Clusters";
589 clustersFile = res.kmeans_clusters;
590 elbowPlotFile = res.elbow_plot;
591 kclusterVariancesFile = res.kcluster_variances;
592 kclusterMeansFile = res.kcluster_means;
594 plotType = "Dendrogram";
595 outFileType = "Newick tree format";
596 clustersFile = res.newick_file;
599 var clustersFileName = clustersFile.split("/").pop();
601 '<a href="' + clustersFile + '" download=' + clustersFileName + '">' + outFileType + "</a>";
604 var kclusterMeansLink;
605 var kclusterVariancesLink;
608 var elbowFileName = elbowPlotFile.split("/").pop();
609 elbowLink = '<a href="' + elbowPlotFile + '" download=' + elbowFileName + '">Elbow plot</a>';
611 if (kclusterMeansFile) {
612 var kclusterMeansFileName = kclusterMeansFile.split("/").pop();
617 kclusterMeansFileName +
618 '">Cluster means</a>';
621 var kclusterVariancesFileName = kclusterVariancesFile.split("/").pop();
622 kclusterVariancesLink =
624 kclusterVariancesFile +
626 kclusterVariancesFileName +
627 '">Cluster variances </a>';
630 var reportFile = res.cluster_report;
631 var reportFileName = reportFile.split("/").pop();
632 var dataType = res.data_type;
634 '<a href="' + reportFile + '" download=' + reportFileName + '">Analysis Report </a>';
636 var downloadLinks = `<strong>Download
637 ${popName} (${dataType})</strong>:
642 if (kclusterMeansLink) {
643 downloadLinks += " | " + kclusterMeansLink;
646 downloadLinks += " | " + kclusterVariancesLink + " | " + elbowLink;
649 var clusterPlotDiv = this.clusterPlotDiv.replace(/#/, '');
650 var plotId = res.file_id;;
651 var clusterDownloadLinkId = `download_${clusterPlotDiv}_${plotId}`;
652 var clusterPlotDownload =
653 `<a href='#' onclick='event.preventDefault();' id='${clusterDownloadLinkId}'>Cluster plot</a>`;
655 downloadLinks += " | " + clusterPlotDownload;
657 return downloadLinks;
660 displayStaticPlot: function(res, downloadLinks) {
661 var imageId = res.plot_name;
662 imageId = 'id="' + imageId + '"';
663 var plot = "<img " + imageId + ' src="' + res.cluster_plot + '">';
665 jQuery("#cluster_plot").prepend('<p style="margin-top: 20px">' + downloadLinks + "</p>");
666 jQuery("#cluster_plot").prepend(plot);
667 // // solGS.dendrogram.plot(res.json_data, '#cluster_canvas', '#cluster_plot', downloadLinks)
671 displayClusterOutput: function (res) {
672 var downloadLinks = this.createClusterDownloadLinks(res);
674 if (res.cluster_type.match(/k-means/i)) {
675 this.plotCluster(res, downloadLinks)
677 this.displayStaticPlot(res, downloadLinks);
682 getClusterPopsTable: function (tableId) {
683 var clusterTable = this.createTable(tableId);
687 runCluster: function (selectedId, selectedName, dataStr) {
688 var clusterPopId = this.getClusterPopId(selectedId, dataStr);
689 var clusterOpts = solGS.cluster.clusteringOptions(clusterPopId);
690 var clusterType = clusterOpts.cluster_type || "k-means";
691 var kNumber = clusterOpts.k_number || 3;
692 var dataType = clusterOpts.data_type || "genotype";
695 selected_id: selectedId,
696 selected_name: selectedName,
697 data_structure: dataStr,
698 cluster_pop_id: clusterPopId,
699 cluster_type: clusterType,
704 this.clusterResult(clusterArgs);
707 clusteringOptions: function (clusterPopId) {
708 var clusterTypeId = this.clusterTypeSelectId(clusterPopId);
709 var kNumId = this.clusterKnumSelectId(clusterPopId);
710 var dataTypeId = this.clusterDataTypeSelectId(clusterPopId);
711 var selectionPropId = this.clusterSelPropSelectId(clusterPopId);
713 var dataType = jQuery("#" + dataTypeId).val() || "genotype";
714 var clusterType = jQuery("#" + clusterTypeId).val() || "k-means";
715 var kNumber = jQuery("#" + kNumId).val() || 3;
716 var selectionProp = jQuery("#" + selectionPropId).val();
718 if (typeof kNumber === "string") {
719 kNumber = kNumber.replace(/\s+/g, "");
723 selectionProp = selectionProp.replace(/%/, "");
724 selectionProp = selectionProp.replace(/\s+/g, "");
729 cluster_type: clusterType,
731 selection_proportion: selectionProp,
736 displayClusterPopsTable: function (tableId, data) {
738 var table = jQuery(`#${tableId}`).DataTable({
745 'rowId': function (a) {
750 table.rows.add(data).draw();
755 getClusterPopsRows: function(clusterPops) {
757 var clusterPopsRows = [];
759 for (var i = 0; i < clusterPops.length; i++) {
760 if (clusterPops[i]) {
761 var clusterPopRow = this.createRowElements(clusterPops[i]);
762 clusterPopsRows.push(clusterPopRow);
766 return clusterPopsRows;
770 getClusterPops: function () {
771 var list = new solGSList();
772 var lists = list.getLists(["accessions", "plots", "trials"]);
773 lists = list.addDataStrAttr(lists);
774 lists = list.addDataTypeAttr(lists);
776 var datasets = solGS.dataset.getDatasetPops(["accessions", "trials"]);
777 datasets = solGS.dataset.addDataTypeAttr(datasets);
778 clusterPops = [lists, datasets];
780 return clusterPops.flat();
785 getSelectedPopClusterArgs: function (runClusterElemId) {
788 var selectedPopDiv = document.getElementById(runClusterElemId);
789 if (selectedPopDiv) {
790 var selectedPopData = selectedPopDiv.dataset;
792 clusterArgs = JSON.parse(selectedPopData.selectedPop);
793 var clusterPopId = clusterArgs.data_str + "_" + clusterArgs.id;
795 var protocolId = solGS.genotypingProtocol.getGenotypingProtocolId("cluster_div");
797 clusterArgs["analysis_type"] = "cluster analysis";
798 clusterArgs["genotyping_protocol_id"] = protocolId;
799 clusterArgs["cluster_pop_id"] = clusterPopId;
800 clusterArgs["data_structure"] = clusterArgs.data_str;
806 populateClusterMenu: function (newPop) {
807 var modelData = solGS.selectMenuModelArgs();
808 var clusterPops = [modelData];
810 if (modelData.id.match(/list/) == null) {
811 var trialSelPopsList = solGS.selectionPopulation.getPredictedTrialTypeSelectionPops();
812 if (trialSelPopsList) {
813 clusterPops.push(trialSelPopsList);
817 var menu = new SelectMenu(this.clusterPopsDiv, this.clusterPopsSelectMenuId);
820 menu.updateOptions(newPop);
822 menu.populateMenu(clusterPops);
827 cleanupFeedback: function(canvasId, runBtnId, msgDiv, message) {
828 jQuery(`${canvasId} .multi-spinner-container`).hide();
829 jQuery(runBtnId).show();
831 jQuery(msgDiv).empty();
834 jQuery(msgDiv).html(message);
839 plotCluster: function (plotData, downloadLinks) {
840 var scores = plotData.pc_scores_groups;
847 jQuery.each(scores, function (i, pc) {
852 pc1: parseFloat(pc[2]),
853 pc2: parseFloat(pc[3]),
857 pc1.push(parseFloat(pc[2]));
858 pc2.push(parseFloat(pc[3]));
860 if (!groups.includes(pc[1])) {
865 var groupsNames = groups;
876 var totalH = height + pad.top + pad.bottom + 100;
877 var totalW = width + pad.left + pad.right + 400;
879 var clusterCanvasDivId = this.canvas;
880 var clusterPlotPopId = plotData.file_id;
881 var clusterPlotDivId = `${this.clusterPlotDiv}_${clusterPlotPopId}`;
882 clusterPlotDivId = clusterPlotDivId.replace(/#/, "");
884 jQuery(clusterCanvasDivId).append("<div id=" + clusterPlotDivId + "></div>");
885 clusterPlotDivId = "#" + clusterPlotDivId;
888 .select(clusterPlotDivId)
889 .insert("svg", ":first-child")
890 .attr("width", totalW)
891 .attr("height", totalH);
893 var clusterPlot = svg.append("g")
894 .attr("id", clusterPlotDivId)
895 .attr("transform", "translate(0,0)");
897 var pc1Min = d3.min(pc1);
898 var pc1Max = d3.max(pc1);
900 var pc1Limits = d3.max([Math.abs(d3.min(pc1)), d3.max(pc1)]);
901 var pc2Limits = d3.max([Math.abs(d3.min(pc2)), d3.max(pc2)]);
903 var pc1AxisScale = d3.scaleLinear().domain([0, pc1Limits]).range([0, width / 2]);
904 var pc2AxisScale = d3.scaleLinear().domain([0, pc2Limits]).range([0, height / 2]);
906 var pc1AxisLabel = d3.scaleLinear().domain([-1 * pc1Limits, pc1Limits]).range([0, width]);
907 var pc2AxisLabel = d3.scaleLinear().domain([-1 * pc2Limits, pc2Limits]).range([height, 0]);
909 var pc1Axis = d3.axisBottom(pc1AxisLabel).tickSize(3);
910 var pc2Axis = d3.axisLeft(pc2AxisLabel).tickSize(3);
913 var axesLabelColor = "green";
915 var yAxisHeight = pad.top + height + nudgeVal;
919 .attr("class", "PC1 axis")
920 .attr("transform", "translate(" + pad.left + "," + yAxisHeight + ")")
926 .attr("transform", "rotate(90)")
927 .attr("fill", axesLabelColor)
928 .style("text-anchor", "start")
929 .style("fill", axesLabelColor);
933 .attr("class", "PC2 axis")
934 .attr("transform", "translate(" + pad.left + "," + pad.top + ")")
939 .style("fill", axesLabelColor);
941 var grpColor = d3.scaleOrdinal(d3.schemeCategory10);
943 clusterPlot.append("g")
948 .style("fill", function (d) {
949 return grpColor(groups.indexOf(d[0].group));
952 .attr("cx", function (d) {
955 return pad.left + width / 2 + pc1AxisScale(xVal);
957 return pad.left + width / 2 - -1 * pc1AxisScale(xVal);
960 .attr("cy", function (d) {
963 return pad.top + height / 2 - pc2AxisScale(yVal);
965 return pad.top + height / 2 + -1 * pc2AxisScale(yVal);
968 .on("mouseover", function (d) {
969 d3.select(this).attr("r", 5).style("fill", axesLabelColor);
973 .attr("id", "dLabel")
974 .style("fill", function(d){
975 return grpColor(groups.indexOf(d.group)); })
976 .text(d[0].name + "(" + d[0].pc1 + "," + d[0].pc2 + ")")
977 .attr("x", width + pad.left + 2 * nudgeVal)
978 .attr("y", height - 0.1 * height);
980 .on("mouseout", function (d) {
983 .style("fill", function (d) {
984 return grpColor(groups.indexOf(d[0].group));
986 d3.selectAll("text#dLabel").remove();
991 .attr("transform", "translate(" + pad.left + "," + pad.top + ")")
992 .attr("height", height + nudgeVal)
993 .attr("width", width + nudgeVal)
994 .attr("fill", "none")
995 .attr("stroke", "#523CB5")
996 .attr("stroke-width", 1)
997 .attr("pointer-events", "none");
1000 if (plotData.list_name) {
1001 popName = plotData.list_name;
1004 if (downloadLinks) {
1005 jQuery(clusterPlotDivId).append('<p style="margin-left: 40px">' + downloadLinks + "</p>");
1008 if (groupsNames && Object.keys(groupsNames).length > 1) {
1011 var legendXOrig = pad.left + 2 * nudgeVal + width;
1012 var legendYOrig = height * 0.25;
1013 var legendValues = groups;
1017 .attr("class", "cell")
1018 .attr("transform", "translate(" + legendXOrig + "," + legendYOrig + ")")
1023 .style("font-size",'12px')
1024 .style("fill", axesLabelColor);
1028 .attr("class", "cell")
1029 .attr("transform", "translate(" + legendXOrig + "," + legendYOrig + ")")
1030 .attr("height", 100)
1036 .attr("x", function (d) {
1039 .attr("y", function (d) {
1040 return 1 + d * recLH + d * 5;
1042 .attr("width", recLH)
1043 .attr("height", recLW)
1044 .style("stroke", "black")
1045 .style("fill", function (d) {
1046 return grpColor(groups.indexOf(d));
1053 "translate(" + (legendXOrig + 30) + "," + (legendYOrig + 0.5 * recLW) + ")"
1055 .attr("id", "legendtext")
1060 .attr("fill", "#523CB5")
1061 .style("fill", "#523CB5")
1063 .attr("y", function (d) {
1064 return 1 + d[0] * recLH + d[0] * 5;
1066 .text(function (d) {
1069 .attr("dominant-baseline", "middle")
1070 .attr("text-anchor", "start");
1076 jQuery.fn.doesExist = function () {
1077 return jQuery(this).length > 0;
1080 jQuery(document).ready(function () {
1081 var url = location.pathname;
1083 if (url.match(/cluster\/analysis/)) {
1084 var canvas = solGS.cluster.canvas;
1085 var clusterMsgDiv = solGS.cluster.clusterMsgDiv;
1087 var clusterArgs = solGS.cluster.getClusterArgsFromUrl();
1088 var clusterPopId = clusterArgs.cluster_pop_id;
1091 jQuery(clusterMsgDiv).text("Running cluster... please wait...it may take minutes.").show();
1092 jQuery(`${canvas} .multi-spinner-container`).show();
1094 solGS.cluster.checkCachedCluster(url, clusterArgs).done(function (res) {
1095 if (res.result == "success") {
1096 solGS.cluster.displayClusterOutput(res);
1098 jQuery(clusterMsgDiv).empty();
1099 jQuery(`${canvas} .multi-spinner-container`).hide();
1107 jQuery(document).ready(function () {
1108 var canvas = solGS.cluster.canvas;
1110 jQuery(canvas).on("click", "a", function (e) {
1111 var linkId = e.target.id;
1112 var clusterPlotId = linkId.replace(/download_/, "");
1114 if (clusterPlotId.match(/cluster_plot_/)) {
1115 saveSvgAsPng(document.getElementById(`#${clusterPlotId}`), `${clusterPlotId}.png`, { scale: 2});
1121 jQuery(document).ready(function () {
1122 jQuery("#cluster_div").on("change", "#cluster_type_opts", function () {
1123 var rowId = jQuery(this).closest("tr").attr("id");
1125 var clusterTypeId = solGS.cluster.clusterTypeSelectId(rowId);
1126 var kNumId = solGS.cluster.clusterKnumSelectId(rowId);
1127 var clusterType = jQuery("#" + clusterTypeId).val();
1128 if (clusterType.match(/hierarchical/i)) {
1129 jQuery("#k_number_div").hide();
1130 jQuery("#" + kNumId).prop("disabled", true);
1132 jQuery("#k_number_div").show();
1133 jQuery("#" + kNumId).prop("disabled", false);
1138 jQuery(document).ready(function () {
1140 jQuery("#cluster_div").on("change", "#cluster_selected_pop", function () {
1141 var rowId = jQuery(this).closest("tr").attr("id");
1143 var popType = jQuery("#cluster_selected_pop_type").val();
1144 var clusterTypeId = solGS.cluster.clusterTypeSelectId(rowId);
1146 var clusterDataType = jQuery("#" + clusterDataTypeId).val();
1150 jQuery(document).ready(function () {
1151 jQuery("#cluster_div").on("click", function (e) {
1152 var runClusterBtnId = e.target.id;
1153 if (runClusterBtnId.match(/run_cluster/)) {
1154 jQuery(clusterMsgDiv).text("Running cluster... please wait...it may take minutes.").show();
1156 jQuery(`${canvas} .multi-spinner-container`).show();
1158 var canvas = solGS.cluster.canvas;
1159 var clusterMsgDiv = solGS.cluster.clusterMsgDiv;
1160 var runClusterBtnId;
1162 var page = location.pathname;
1163 var selectedId, selectedName, dataStr;
1164 if (page.match(/cluster\/analysis/)) {
1165 var clusterArgs = solGS.cluster.getSelectedPopClusterArgs(runClusterBtnId);
1166 selectedId = clusterArgs.id;
1167 selectedName = clusterArgs.name;
1168 dataStr = clusterArgs.data_str;
1170 } else if (page.match(/breeders\/trial\//)) {
1171 selectedId = jQuery("#trial_id").val();
1172 selectedName = jQuery("#trial_name").val();
1174 selectedId = jQuery("#cluster_selected_pop_id").val();
1175 selectedName = jQuery("#cluster_selected_pop_name").val();
1176 var popType = jQuery("#cluster_selected_pop_type").val();
1179 if (popType.match(/list/)) {
1181 } else if (popType.match(/dataset/)) {
1182 dataStr = "dataset";
1187 var clusterOptsId = "cluster_options";
1188 var clusterPopId = solGS.cluster.getClusterPopId(selectedId, dataStr);
1189 var clusterOpts = solGS.cluster.clusteringOptions(clusterPopId);
1192 selected_id: selectedId,
1193 selected_name: selectedName,
1194 data_structure: dataStr,
1195 cluster_pop_id: clusterPopId,
1196 cluster_type: clusterOpts.cluster_type,
1197 data_type: clusterOpts.data_type,
1198 k_number: clusterOpts.k_number,
1199 selection_proportion: clusterOpts.selection_proportion,
1202 clusterArgs = solGS.cluster.clusterResult(clusterArgs);
1203 runClusterBtnId = solGS.cluster.getRunClusterBtnId(clusterPopId);
1204 var page = clusterArgs.analysis_page;
1206 runClusterBtnId = `#${runClusterBtnId}`;
1208 .checkCachedCluster(page, clusterArgs)
1209 .done(function (res) {
1210 if (res.result == "success") {
1211 solGS.cluster.cleanupFeedback(canvas, runClusterBtnId, clusterMsgDiv);
1212 solGS.cluster.displayClusterOutput(res);
1214 jQuery(`${canvas} .multi-spinner-container`).hide();
1215 jQuery(clusterMsgDiv).empty();
1217 "<p>This analysis may take a long time. " +
1218 "Do you want to submit the analysis and get an email when it completes?</p>";
1220 var jobSubmit = '<div id= "cluster_submit">' + title + "</div>";
1222 jQuery(jobSubmit).appendTo("body");
1224 jQuery("#cluster_submit").dialog({
1228 title: "cluster job submission",
1232 class: "btn btn-success",
1234 click: function () {
1235 jQuery(this).dialog("close");
1236 solGS.submitJob.checkUserLogin(page, clusterArgs);
1241 text: "No, I will wait till it completes.",
1242 class: "btn btn-warning",
1244 click: function () {
1245 jQuery(this).dialog("close");
1247 jQuery(runClusterBtnId).hide();
1248 jQuery(clusterMsgDiv)
1249 .text("Running cluster... please wait...it may take minutes.")
1251 jQuery(`${canvas} .multi-spinner-container`).show();
1254 .runClusterAnalysis(clusterArgs)
1255 .done(function (res) {
1256 if (res.result == "success") {
1257 if (res.pc_scores_groups) {
1258 solGS.cluster.cleanupFeedback(canvas, runClusterBtnId, clusterMsgDiv )
1259 solGS.cluster.displayClusterOutput(res);
1261 var msg = "There is no cluster groups data to plot. " +
1262 "The R clustering script did not write cluster results to output files.";
1264 solGS.cluster.cleanupFeedback(canvas, runClusterBtnId, clusterMsgDiv, msg);
1267 var msg = "Error occured running the clustering. Possibly the R script failed.";
1268 solGS.cluster.cleanupFeedback(canvas, runClusterBtnId, clusterMsgDiv, msg);
1272 var msg = "Error occured running the clustering";
1273 solGS.cluster.cleanupFeedback(canvas, runClusterBtnId, clusterMsgDiv, msg);
1280 class: "btn btn-info",
1281 id: "cancel_queue_info",
1282 click: function () {
1283 jQuery(this).dialog("close");
1284 jQuery(runClusterBtnId).show();
1291 .fail(function () { });
1296 jQuery(document).ready(function () {
1297 var page = location.pathname;
1299 if (page.match(/solgs\/traits\/all\/|solgs\/models\/combined\/trials\//)) {
1300 setTimeout(function () {
1301 solGS.cluster.populateClusterMenu();
1304 var dataTypeOpts = solGS.cluster.getDataTypeOpts();
1306 dataTypeOpts = solGS.cluster.createDataTypeSelect(dataTypeOpts);
1307 var clusterTypeOpts = solGS.cluster.createClusterTypeSelect();
1309 jQuery(document).ready(checkClusterPop);
1311 function checkClusterPop() {
1312 if (jQuery("#cluster_div #cluster_pops_select_div").is(":visible")) {
1313 jQuery("#cluster_div #cluster_options #cluster_data_type_opts").html(dataTypeOpts);
1314 jQuery("#cluster_div #cluster_options #cluster_type_opts").html(clusterTypeOpts);
1315 jQuery("#cluster_div #cluster_options").show();
1317 setTimeout(checkClusterPop, 6000);
1321 if (!page.match(/cluster\/analysis/)) {
1322 var dataTypeOpts = solGS.cluster.getDataTypeOpts();
1323 dataTypeOpts = solGS.cluster.createDataTypeSelect(dataTypeOpts);
1324 var clusterTypeOpts = solGS.cluster.createClusterTypeSelect();
1326 jQuery("#cluster_div #cluster_options #cluster_data_type_opts").html(dataTypeOpts);
1327 jQuery("#cluster_div #cluster_options #cluster_type_opts").html(clusterTypeOpts);
1328 jQuery("#cluster_div #cluster_options").show();
1333 jQuery(document).ready(function () {
1335 if (!location.pathname.match(/cluster\/analysis/)) {
1336 var clusterPopsDiv = solGS.cluster.clusterPopsDiv;
1338 jQuery(clusterPopsDiv).on("change", function () {
1339 var selectedPop = jQuery("option:selected", this).data("pop");
1341 var selectedPopId = selectedPop.id;
1342 var selectedPopName = selectedPop.name;
1343 var selectedPopType = selectedPop.type || selectedPop.pop_type;
1345 var dataTypeId = solGS.cluster.clusterDataTypeSelectId(selectedPopId);
1346 var dataType = jQuery("#" + dataTypeId).val();
1348 jQuery("#cluster_selected_pop_name").val(selectedPopName);
1349 jQuery("#cluster_selected_pop_id").val(selectedPopId);
1350 jQuery("#cluster_selected_pop_type").val(selectedPopType);
1352 var dataTypeOpts = solGS.cluster.getDataTypeOpts(selectedPop);
1354 dataTypeOpts = solGS.cluster.createDataTypeSelect(dataTypeOpts, selectedPopId);
1355 jQuery("#cluster_div #cluster_options #cluster_data_type_opts").html(dataTypeOpts);
1357 if (selectedPopType.match(/selection_index/) && dataType.match(/Genotype/i)) {
1358 jQuery("#cluster_div #cluster_options #selection_proportion_div").show();
1360 jQuery("#cluster_div #cluster_options #selection_proportion_div").hide();
1368 jQuery(document).ready(function () {
1369 if (location.pathname.match(/cluster\/analysis/)) {
1371 clusterPopsDataDiv = solGS.cluster.clusterPopsDataDiv;
1372 var tableId = 'cluster_pops_table';
1373 var clusterPopsTable = solGS.cluster.createTable(tableId)
1374 jQuery(clusterPopsDataDiv).append(clusterPopsTable).show();
1376 var clusterPops = solGS.cluster.getClusterPops()
1377 var clusterPopsRows = solGS.cluster.getClusterPopsRows(clusterPops);
1379 solGS.cluster.displayClusterPopsTable(tableId, clusterPopsRows)
1381 jQuery("#add_new_pops").show();