diff --git a/README.md b/README.md index 67bc76a..545c0a7 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ Zenodo (https://zenodo.org/record/5517607). ## License This project is licensed under the MIT License - for details please refer to the [LICENSE](./LICENSE) file. + ======= THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, diff --git a/app/static/js/controller.js b/app/static/js/controller.js index 654c782..b274a37 100644 --- a/app/static/js/controller.js +++ b/app/static/js/controller.js @@ -13,47 +13,10 @@ function readFileAsync(file) { }) }; -function createSignatureMatrix(rows, current_genes) { - // console.log(rows); - // genes = current_genes; - var nGenes = current_genes.length; - var nClusters = rows.length; - var nChecked = 0; - - for (var i = 0; i < nClusters; i++) { - nChecked += ((rows[i]).visible & (rows[i]).checked) - } - - console.log(nChecked); - - signatureBuffer = tf.buffer([nChecked, nGenes]); - - clusterLabels = []; - - var n = 0; - - for (var i = 0; i < nClusters; i++) { - if ((rows[i]).visible & (rows[i]).checked) { - line = ((rows[i]).expressions) - - for (var j = 0; j < nGenes; j++) { - signatureBuffer.set(parseFloat(line[j]), n, j); - } - n++; - clusterLabels.push(((rows[i]).celltype)); - } - } - signatureBuffer = signatureBuffer.toTensor(); - - return [signatureBuffer, clusterLabels]; - -}; function processSignatures(allText) { - console.log('...loading...'); - var genes; var clusterLabels; var signatureBuffer; @@ -80,24 +43,15 @@ function processSignatures(allText) { signatureBuffer = signatureBuffer.toTensor(); return [signatureBuffer, clusterLabels, genes]; - }; function processCoordinates(allText) { - - // var t0 = performance.now() var allTextLines = allText.split(/\r\n|\n/); - var len = allTextLines.length; - ZGenes = Array(len); - X = Array(len); - Y = Array(len); - - allGenes = []; - - // ZGenes = []; //Array(len); - // X = [];//Array(len); - // Y = []//Array(len); + genes = []; + ZGenes = []; + X = []; + Y = []; var x = 0; var y = 0; var line; @@ -113,21 +67,17 @@ function processCoordinates(allText) { xmax = Math.max(x, xmax); ymax = Math.max(y, ymax); - X[i] = x;//.push(x); - Y[i] = y;//.push(y); - ZGenes[i] = line[0];//.push(line[0]); - - if (!allGenes.includes(line[0])) { - allGenes.push(line[0]); - }; + X.push(x); + Y.push(y); + ZGenes.push(line[0]); + if (!genes.includes(line[0])) genes.push(line[0]); } - // console.log(performance.now() -t0); var edgeRatio = Math.ceil(xmax / ymax); var width = Math.ceil(500); var height = Math.ceil(width / edgeRatio); - return [X, Y, ZGenes, allGenes, xmax, ymax, edgeRatio, width, height]; + return [X, Y, ZGenes, genes, xmax, ymax, edgeRatio, width, height]; }; function reloadPage() { @@ -135,29 +85,31 @@ function reloadPage() { // document.getElementById('btn-coordinates-hidden').scrollIntoView(); location.reload(); }; + function main() { { var genes = []; - var allTissues = []; - var allSpecies = []; - var signatureContent = []; var clusterLabels; var signatureMatrix; + var coordinatesLoaded = false; + var signaturesLoaded = false; + var X; // mRNA x coordinates var Y; // mRNA y coordinates var ZGenes; // gene information var xmax; // highest coordinate var ymax; // lowest coordinate - var sigma = 1; // KDE kernel width + var sigma = 1; // KDE kernel width - var height = 300; // vf height (pixels) - var width; // vf width (pixels) + var height = 500; // vf height (pixels) + var width = 0; // vf width (pixels) var edgeRatio = 1;// radion height/width - var threshold = 10; + var threshold = 2; // cell/ecm cutoff var vf; // tensor vectorfield var vfNorm; // tensor vfNorm + var scale = 1; var parameterWindow = [250, 250]; var parameterWidth = 50; @@ -166,200 +118,278 @@ function main() { var parameterZ = []; var vfParameter; var vfNormParameter; - var pointerCoordinates = parameterWindow; function getClusterLabel(i) { return clusterLabels[i]; } + } async function importSignatures(path) { - // console.log('loading signatures'); + console.log('loading signatures'); document.getElementById("signature-loader").style.display = "block"; //display waiting symbol + var fileToLoad = document.getElementById("btn-signatures-hidden").files[0]; [signatureMatrix, clusterLabels, genes] = (processSignatures(await readFileAsync(fileToLoad))); - + setVfSizeIndicator(width, height, genes); plotSignatures('signatures-preview', genes, clusterLabels, signatureMatrix.arraySync()).then(function () { document.getElementById("signature-loader").style.display = "none"; }); + signaturesLoaded = true; + $('#errSign').remove(); }; + async function importCoordinates(path) { + $('#errCoords').remove(); - async function downloadSignatures(current_genes) { + vf = null; + X = []; // mRNA x coordinates + Y = []; // mRNA y coordinates + ZGenes = []; // gene information + xmax = []; // highest coordinate + ymax = []; // lowest coordinate + console.log(ZGenes); + console.log('loading coordinates'); + document.getElementById("coordinate-loader").style.display = "block"; //display waiting symbol - $.ajax({ - type: "POST", - url: "http://127.0.0.1:5000/genes", - contentType: "application/json", - data: JSON.stringify({ "genes": current_genes }), - dataType: "json", - success: function (data) { - console.log('downloaded...'); - current_genes; - signatureContent = (processDownloadedSignatures(data)); - // console.log(signatureContent); - createTableHeader(current_genes, allTissues, allSpecies); - updateTable(signatureContent, allTissues, allSpecies); - [signatureMatrix, clusterLabels] = createSignatureMatrix(data, current_genes); - //console.log(signatureMatrix); - }, - error: function (err) { - // console.log(err); - return (err); - } + var fileToLoad = document.getElementById("btn-coordinates-hidden").files[0]; + + + [X, Y, ZGenes, coordGenes, xmax, ymax, edgeRatio, width, height] = processCoordinates(await readFileAsync(fileToLoad)); + if (!genes.length) genes = coordGenes; //use genes from the coordinate file for now, e.g. kde + + edgeRatio = xmax / ymax; + width = Math.ceil(height * edgeRatio); + setVfSizeIndicator(width, height, genes); + + plotCoordinates('coordinates-preview', X, Y, ZGenes, { 'showlegend': true, }).then(function () { + document.getElementById("coordinate-loader").style.display = "none"; }); + coordinatesLoaded = true; + }; + + function allowDrop(ev) { + ev.preventDefault(); + console.log('kewl!') } - function processDownloadedSignatures(data) { - allTissues = []; - allSpecies = []; - signatureContent = [] - - for (var i = 0; i < data.length; i++) { - entry = data[i]; - - [cellType, tissueType, species] = (data[i]).tissue.split('|'); - // console.log(tissueType, allTissues) - if (!allTissues.includes(tissueType)) { allTissues.push(tissueType) }; - if (!allSpecies.includes(species)) { allSpecies.push(species) }; - - entry.label = (data[i]).tissue; - entry.tissue = tissueType; - entry.celltype = cellType; - entry.species = species; - entry.checked = true; - entry.visible = true; - signatureContent.push(entry); - } - return signatureContent; + function dropCoords(ev) { + ev.preventDefault(); + let path = ev.dataTransfer.items[0].getAsFile() + importCoordinates(path) } - function createTableHeader(genes, tissues, species) { - var header = document.getElementById('sheet-header') - // row = header.insertRow(-1); - typeCell = header.insertCell(0); - - typeCell = header.insertCell(1); - typeCell.innerHTML = "Cell Type"; - typeCell.classList.add('header-cell'); - - tissueCell = header.insertCell(2); - tissueCell.innerHTML = "Tissue" - tissueCell.classList.add('header-cell'); - - var selTissue = document.createElement("select"); - selTissue.id = "tissueSelect" - selTissue.classList.add('selectpicker') - selTissue.setAttribute('multiple', ''); - selTissue.onchange = updateSignatureContent - - for (var i = 0; i < tissues.length; i++) { - //Add the options - // console.log(selTissue); - selTissue.options[selTissue.options.length] = new Option(tissues[i]); + function dropSignatures(ev) { + ev.preventDefault(); + let path = ev.dataTransfer.items[0].getAsFile() + importSignatures(path) + } + + + function updateScale(event) { + let valOld = scale; + let val = event.srcElement.valueAsNumber; + if (val <= 0) { + val = 1; + event.srcElement.value = '-' + } + else { + scale = val; } - tissueCell.appendChild(selTissue); - speciesCell = header.insertCell(3); - speciesCell.innerHTML = "Species" - speciesCell.classList.add('header-cell'); + rescale = valOld / val; + scale = val; + + xmax = xmax * rescale; + ymax = ymax * rescale; + + // xmax = xmax*rescale; + X = X.map(function (x) { return x * rescale; }) + Y = Y.map(function (x) { return x * rescale; }) + + plotCoordinates('coordinates-preview', X, Y, ZGenes, { 'showlegend': true, }); + + console.log(rescale); + + } + + function updateVfNormScalebar(event) { - var selSpecies = document.createElement("select"); - selSpecies.id = "speciesSelect" - selSpecies.classList.add('selectpicker') - selSpecies.setAttribute('multiple', ''); - selSpecies.onchange = updateSignatureContent - for (var i = 0; i < species.length; i++) { - //Add the options - // console.log(selSpecies); - selSpecies.options[selSpecies.options.length] = new Option(species[i]); + div = $('#vf-norm-preview')[0]; + + console.log(event["xaxis.autorange"]); + + if (!event["xaxis.autorange"]) { + + var xrange = event["xaxis.range"]; + var yrange = event["yaxis.range"]; + } - speciesCell.appendChild(selSpecies) - - // console.log(genes); - for (var g = 0; g < genes.length; g++) { - cell = header.insertCell(g + 4); - cell.innerHTML = genes[g] - cell.classList.add('header-cell'); - cell.classList.add('rotate'); + else { + var xrange = [0, width]; + var yrange = [0, height]; } - $('select').selectpicker(); - $("#tissueSelect").selectpicker('val', tissues[0]) - $("#speciesSelect").selectpicker('val', species[0]) + starty = yrange[0] + (yrange[1] - yrange[0]) / 8 + endy = yrange[0] + (yrange[1] - yrange[0]) / 7 - } + umPerPx = xmax / width; + start = xrange[0] + (xrange[1] - xrange[0]) / 8 + end = xrange[0] + (xrange[1] - xrange[0]) / 3 + center = start + (end - start) / 2; - function updateSignatureContent(event) { - markedTissues = ($("#tissueSelect").val()); - markedSpecies = ($("#speciesSelect").val()); - if (markedTissues != null & markedSpecies != null) { - - for (var i = 0; i < signatureContent.length; i++) { - // console.log(signatureContent[i]); - if ((markedSpecies.includes((signatureContent[i]).species)) & - (markedTissues.includes((signatureContent[i]).tissue))) { - (signatureContent[i]).visible = true; - } - else { - (signatureContent[i]).visible = false; - (signatureContent[i]).checked = true; - } - } - updateTable(signatureContent); + var length = (end - start) * umPerPx; + var decimals = Math.ceil(Math.log10(length)) - 1; + var inter = length / (Math.pow(10, decimals)); + + var lengthRound = Math.ceil(inter) * Math.pow(10, decimals); + + + end = start + lengthRound / umPerPx + + text = lengthRound + " μm"; - [signatureMatrix, clusterLabels] = createSignatureMatrix(signatureContent, genes) + console.log(center, div.layout.shapes[0]) + + length = Math.ceil(Math.random() * 40); + + var rect = div.layout.shapes[0]; + rect.x0 = center - lengthRound / 2 / umPerPx * 1.1; + rect.x1 = center + lengthRound / 2 / umPerPx * 1.1; + + var horizontal = div.layout.shapes[1]; + horizontal.x0 = center - lengthRound / 2 / umPerPx; + horizontal.x1 = center + lengthRound / 2 / umPerPx; + + var capLeft = div.layout.shapes[2]; + capLeft.x0 = center - lengthRound / 2 / umPerPx; + capLeft.x1 = center - lengthRound / 2 / umPerPx; + + var capRight = div.layout.shapes[3]; + capRight.x0 = center + lengthRound / 2 / umPerPx; + capRight.x1 = center + lengthRound / 2 / umPerPx; + + var textAnnot = div.layout.annotations[0]; + textAnnot.x = center; + textAnnot.text = text; + + layout = { + 'shapes': [rect, horizontal, capLeft, capRight], + 'annotations': [textAnnot] } + Plotly.update('vf-norm-preview', {}, layout); } - function onlyUnique(value, index, self) { - return self.indexOf(value) === index; - } - async function importCoordinates(path) { - // console.log(ZGenes); - // console.log('loading coordinates'); - document.getElementById("coordinate-loader").style.display = "block"; //display waiting symbol + function updateCtMapScalebar(event) { - var fileToLoad = document.getElementById("btn-coordinates-hidden").files[0]; + div = $('#celltypes-preview')[0]; - [X, Y, ZGenes, genes, xmax, ymax, edgeRatio, width, height] = processCoordinates(await readFileAsync(fileToLoad)); - // console.log(genes); + console.log(event); + if (!event["xaxis.autorange"]) { - // var current_genes = ZGenes.filter(onlyUnique); + var xrange = [event["xaxis.range[0]"], event["xaxis.range[1]"]] + var yrange = [event["yaxis.range[0]"], event["yaxis.range[1]"]] + + } + else { + var xrange = [0, width]; + var yrange = [0, height]; + } + + + starty = yrange[0] + (yrange[1] - yrange[0]) / 8 + endy = yrange[0] + (yrange[1] - yrange[0]) / 7 + + umPerPx = xmax / width; + + start = xrange[0] + (xrange[1] - xrange[0]) / 8 + end = xrange[0] + (xrange[1] - xrange[0]) / 3 + center = start + (end - start) / 2; + + var length = (end - start) * umPerPx; + var decimals = Math.ceil(Math.log10(length)) - 1; + var inter = length / (Math.pow(10, decimals)); + + var lengthRound = Math.ceil(inter) * Math.pow(10, decimals); + + + end = start + lengthRound / umPerPx + + text = lengthRound + " μm"; + + console.log(div.layout) + + length = Math.ceil(Math.random() * 40); + + var rect = div.layout.shapes[0]; + rect.x0 = center - lengthRound / 2 / umPerPx * 1.1; + rect.x1 = center + lengthRound / 2 / umPerPx * 1.1; + + var horizontal = div.layout.shapes[1]; + horizontal.x0 = center - lengthRound / 2 / umPerPx; + horizontal.x1 = center + lengthRound / 2 / umPerPx; + + var capLeft = div.layout.shapes[2]; + capLeft.x0 = center - lengthRound / 2 / umPerPx; + capLeft.x1 = center - lengthRound / 2 / umPerPx; + + var capRight = div.layout.shapes[3]; + capRight.x0 = center + lengthRound / 2 / umPerPx; + capRight.x1 = center + lengthRound / 2 / umPerPx; + + var textAnnot = div.layout.annotations[0]; + textAnnot.x = center; + textAnnot.text = text; + + layout = { + 'shapes': [rect, horizontal, capLeft, capRight], + 'annotations': [textAnnot] + } + Plotly.update('celltypes-preview', {}, layout); + } - downloadSignatures(genes); - edgeRatio = xmax / ymax; - width = Math.ceil(height * edgeRatio); - console.log('plotting...'); - plotCoordinates('coordinates-preview', X, Y, ZGenes).then(function () { - document.getElementById("coordinate-loader").style.display = "none"; - }); - }; function runFullKDE() { - //[vf, vfNorm] = runKDE(X, Y, ZGenes, genes, xmax, ymax, sigma, width, height); - runFullKDEviaPOST() - //console.log(X) - //plotVfNorm('vf-norm-preview', vfNorm.arraySync()); - }; + $('#errCoords').remove(); + $('#errVF').remove(); + if (coordinatesLoaded) { + + try { + $('#errMemory').remove(); + runFullKDEviaPOST() + // [vf, vfNorm] = runKDE(X, Y, ZGenes, genes, xmax, ymax, sigma / xmax * height, width, height); + + // umPerPx = xmax / width; + // plotVfNorm('vf-norm-preview', vfNorm.arraySync(), generateScalebar(width / 10, width / 3, umPerPx)); + // document.getElementById('vf-norm-preview').on('plotly_relayout', updateVfNormScalebar); + } + catch (ex) { + printErr('#vf-norm-preview', 'errMemory', "Memory exceeded. Please use a smaller vector field size.") + console.log(ex); + } + } + else { + printErr('#vf-norm-preview', 'errCoords', "Please load a coordinate file first.") + } + }; function runFullKDEviaPOST() { $(document).on({ ajaxStart: function(){ @@ -378,9 +408,9 @@ function main() { paramGenes: genes, paramXmax: xmax, paramYmax: ymax, - paramSigma: sigma, - paramWidth: width, - paramHeight: height + paramSigma: sigma/xmax *height, + paramWidth: height, + paramHeight: width }); //console.log(payload); @@ -391,10 +421,10 @@ function main() { data: payload, dataType: "json", success: function (response) { - vfNorm=response.vfNorm; vf=response.vfBuffer; - plotVfNorm('vf-norm-preview', tf.tensor(vfNorm).arraySync()); - console.log(response.vfNorm); + umPerPx = xmax / width; + plotVfNorm('vf-norm-preview', tf.tensor(response.vfBuffer).sum(2).arraySync(), generateScalebar(width / 10, width / 3, umPerPx)); + document.getElementById('vf-norm-preview').on('plotly_relayout', updateVfNormScalebar); }, error: function (err) { console.log(err); @@ -403,33 +433,39 @@ function main() { }; - function runCelltypeAssignments() { - [signatureMatrix, clusterLabels] = createSignatureMatrix(signatureContent, genes); - celltypeMap = assignCelltypes(tf.tensor(vf), tf.tensor(vfNorm), signatureMatrix, threshold); - assignCelltypesviaPOST(vf, vfNorm, signatureMatrix, threshold) - plotCelltypeMap('celltypes-preview', celltypeMap.arraySync(), clusterLabels, getClusterLabel); + function runCelltypeAssignments() { + $('#errSign').remove(); + $('#errVF').remove(); + if (!signatureMatrix) { + printErr('#celltypes-preview', 'errSign', 'Please load a signature matrix first.'); + } + else if (!vf) { + printErr('#celltypes-preview', 'errVF', 'Please run a KDE first.'); + } + else { + runCelltypeAssignmentsviaPOST() + } }; - - function assignCelltypesviaPOST(vf, vfNorm, signatureMatrix, threshold){ - // $(document).on({ - // ajaxStart: function(){ - // $("#vf-norm-load").show(); - // $("#vf-norm-preview").hide(); - // }, - // ajaxStop: function(){ - // $("#vf-norm-load").hide(); - // $("#vf-norm-preview").show(); - // } - // }); + function runCelltypeAssignmentsviaPOST() { + $(document).on({ + ajaxStart: function(){ + //console.log(arrSigMat); + $("celltypes-load").show(); + $("celltypes-preview").hide(); + }, + ajaxStop: function(){ + $("celltypes-load").hide(); + $("celltypes-preview").show(); + } + }); + payload = JSON.stringify({ - paramVF: vf, - paramVFNorm: vfNorm, - paramZSigMat: signatureMatrix, + paramvf: vf, + paramSigMat: signatureMatrix.arraySync(), paramthreshold: threshold, }); - //console.log(payload); - + $.ajax({ type: "POST", url: "/assignCelltypes", @@ -437,57 +473,89 @@ function main() { data: payload, dataType: "json", success: function (response) { - celltypeMap=tf.tensor(response.vfNorm); - //plotCelltypeMap('celltypes-preview', celltypeMap.arraySync(), clusterLabels, getClusterLabel); - console.log(response.celltypeMap); + celltypeMap = tf.tensor(response.celltypeMap) + plotCelltypeMap('celltypes-preview', celltypeMap.arraySync(), clusterLabels, getClusterLabel, layout = generateScalebar(width / 10, width / 3, umPerPx)); + umPerPx = xmax / width; + document.getElementById('celltypes-preview').on('plotly_relayout', updateCtMapScalebar); }, error: function (err) { console.log(err); } }); - } + + }; + + function updateVfShape() { + //togglePreviewGenerator(); + $('#errMemory').remove(); height = parseInt(document.getElementById('vf-width').value); width = Math.ceil(height * edgeRatio); - document.getElementById("vf-size-information").innerHTML = - "total size: (" + width + "," + height + "," + genes.length + "); " + Math.ceil(width * height * 32 * genes.length / 1024 ** 2) + " gB" - updateParameterVf(); + setVfSizeIndicator(width, height, genes); + if (document.getElementById('preview-generator').style.display == 'block') { + //updateParameterVf(); + updateParameterRectangle(pointerCoordinates, parameterWidth * xmax / width); + } }; function updateSigma() { + //togglePreviewGenerator(); sigma = parseFloat(document.getElementById('KDE-bandwidth').value); - updateParameterVf(); + console.log(document.getElementById('preview-generator').style.display); + + // if (document.getElementById('preview-generator').style.display == 'block') { + // updateParameterVf(); + // } }; function updateThreshold() { + //togglePreviewGenerator(); threshold = parseFloat(document.getElementById('threshold').value); - updateParameterCelltypes(); + // if (document.getElementById('preview-generator').style.display == 'block') { + // updateParameterCelltypes(); + // } }; function toggleParameterGenerator() { - var previewGenerator = document.getElementById('preview-generator'); + // var previewLoader = document.getElementById('preview-loader'); + // previewLoader.style.display = "block"; + // var previewGenerator = document.getElementById('preview-generator'); + // previewGenerator.style.display = "none"; + // //previewLoader.style.display = 'block'; + // setTimeout(function () { + var previewGenerator = document.getElementById('preview-generator'); + if (previewGenerator.style.display == "none") { + displayParameterGenerator(); + createParameterCoodinatesPlot(); + } else if (document.getElementById("bar-parameters").innerHTML.includes("Refresh")) { + displayParameterGenerator(); + createParameterCoodinatesPlot(); + } else { + hideParameterGenerator(); + } + // var previewLoader = document.getElementById('preview-loader'); + // previewLoader.style.display = "none"; + // var previewGenerator = document.getElementById('preview-generator'); + // previewGenerator.style.display = "block"; + // }, 0); - if (previewGenerator.style.display === "none") { - displayParameterGenerator(); - createParameterCoodinatesPlot(); - } else { - hideParameterGenerator(); - } }; - - function updateParameterCelltypes() { parameterCelltypeMap = assignCelltypes(vfParameter, vfNormParameter, signatureMatrix, threshold); - plotCelltypeMap('parameter-celltypes', parameterCelltypeMap.arraySync(), clusterLabels); + labelsShort = clusterLabels.map(function (e) { + return e.substring(0, 5) + '.'; + }); + console.log(labelsShort); + plotCelltypeMap('parameter-celltypes', parameterCelltypeMap.arraySync(), labelsShort); } function updateParameterVf() { [vfParameter, vfNormParameter] = runKDE(parameterX, parameterY, parameterZ, genes, parameterWidth * xmax / width * 2, parameterWidth * ymax / height * 2, - sigma, parameterWidth * 2, parameterWidth * 2); + sigma / xmax * height, parameterWidth * 2, parameterWidth * 2); plotVfNorm('parameter-vf', vfNormParameter.arraySync()); updateParameterCelltypes(); }; @@ -528,6 +596,7 @@ function main() { color: 'rgba(255, 255, 255, 1)' }, },], + 'showlegend': false, } @@ -537,32 +606,49 @@ function main() { document.getElementById('parameter-coordinates') .on('plotly_click', updateRectangle); updateParameterVf(); - } + }; function updatePointerCoordinates(eventData) { + togglePreviewGenerator() pointerCoordinates = [eventData.xvals[0], eventData.yvals[0]]; }; function updateRectangle(eventData) { + togglePreviewGenerator(); updateParameterRectangle(pointerCoordinates, parameterWidth * xmax / width); parameterWindow = [Math.ceil(pointerCoordinates[1] / xmax * width), Math.ceil(pointerCoordinates[0] / xmax * width)]; updateParameterCoordinates(); - updateParameterVf(); - console.log(pointerCoordinates); + //updateParameterVf(); + // console.log(pointerCoordinates); }; + function togglePreviewGenerator() { + // console.log('Hellow') + refreshParameterGenerator() + } function initiateButtons() { - document.getElementById('btn-signatures-hidden') .addEventListener('change', importSignatures); document.getElementById('btn-coordinates-hidden') .addEventListener('change', importCoordinates); + + document.getElementById('coordinates-dragzone') + .addEventListener("dragover", allowDrop); + document.getElementById('coordinates-dragzone') + .addEventListener("drop", dropCoords); + document.getElementById('signatures-dragzone') + .addEventListener("dragover", allowDrop); + document.getElementById('signatures-dragzone') + .addEventListener("drop", dropSignatures); + document.getElementById('btn-KDE') .addEventListener('click', runFullKDE); document.getElementById('btn-types') .addEventListener('click', runCelltypeAssignments); + document.getElementById('btn-reload') + .addEventListener('click', reloadPage); document.getElementById('btn-parameters') .addEventListener('click', toggleParameterGenerator); @@ -572,13 +658,31 @@ function main() { .addEventListener('change', updateSigma); document.getElementById('threshold') .addEventListener('change', updateThreshold); + // .addEventListener("change", togglePreviewGenerator); + document.getElementById('exampleScale') + .addEventListener("change", updateScale); document.getElementById('button-tutorial') .addEventListener('click', runTutorial); - document.getElementById('btn-reload') - .addEventListener('click', reloadPage); + document.getElementById('vf-width') + .addEventListener("keypress", togglePreviewGenerator); + document.getElementById('KDE-bandwidth') + .addEventListener("keypress", togglePreviewGenerator); + document.getElementById('threshold') + .addEventListener("keypress", togglePreviewGenerator); + + // Reset values @ page reload + document.getElementById('vf-width').value = 500; + document.getElementById('KDE-bandwidth').value = 1; + document.getElementById('threshold').value = 2; }; + function initiateDataToggle() { + $('[data-toggle="KDE-bandwidth"]').tooltip(); + $('[data-toggle="vf-width"]').tooltip(); + $('[data-toggle="threshold"]').tooltip(); + }; + initiateDataToggle(); initiateButtons(); } diff --git a/app/static/js/model.js b/app/static/js/model.js index eae9faf..8393b3c 100644 --- a/app/static/js/model.js +++ b/app/static/js/model.js @@ -1,9 +1,8 @@ -function runKDE(X, Y, Zgenes, genes, xmax, ymax, sigma, height, width, nStds=3) { +function runKDE(X, Y, Zgenes, genes, xmax, ymax, sigma, height, width, nStds=2) { + console.log([height, width, genes.length]); var vfBuffer = tf.buffer([height, width, genes.length]); - var test=tf.buffer([3,3,3]) - console.log(test); - console.log(vfBuffer); + var x = 0; var y = 0; var z = 0; @@ -11,6 +10,8 @@ function runKDE(X, Y, Zgenes, genes, xmax, ymax, sigma, height, width, nStds=3) var counter = 0; var n_steps = Math.ceil(sigma*nStds); + console.log(sigma,n_steps); + var normalization = 1/(1*Math.PI*sigma**2)**0.5 for (var i = 0; i < Zgenes.length; i++) { @@ -39,16 +40,21 @@ function runKDE(X, Y, Zgenes, genes, xmax, ymax, sigma, height, width, nStds=3) } - else { - console.log(Zgenes[i], i, z); - } + // else { + // // console.log(Zgenes[i], i, z); + // } } - vf = vfBuffer.toTensor(); + try{ + vf = vfBuffer.toTensor(); + } + catch{ + throw ("Exceeded"); + } + vfNorm = vf.sum(2); - console.log(vfNorm); return [vf, vfNorm]; @@ -61,6 +67,7 @@ function assignCelltypes(vf, vfNorm, signatureMatrix, threshold) { var inter; for (var i = 0; i < vf.shape[0]; i++) { + inter = vf.gather(i); inter = inter.add(1).log(); inter = inter.transpose().div(inter.sum(1)).transpose(); diff --git a/app/static/js/tutorial_generator.js b/app/static/js/tutorial_generator.js index 2b69ae2..0222595 100644 --- a/app/static/js/tutorial_generator.js +++ b/app/static/js/tutorial_generator.js @@ -6,10 +6,10 @@ function runTutorial() { var tutorialWindowNames = ['data', 'data-coords', 'data-disp', 'data-sigs', 'parameters', 'parameters-coordinates', 'parameters-sigma', - 'analysis-KDE', 'analysis-assignments'] + 'analysis-KDE', 'analysis-assignments', 'download'] document.getElementById('tutorial-modal').style.display = 'block'; - + document.getElementById('button-tutorial').removeEventListener('click', runTutorial);; $('.btn-next').click(function () { event.preventDefault(); var sectionTo = $(this).attr('href'); @@ -26,6 +26,7 @@ function runTutorial() { event.preventDefault(); console.log($(this)); (this).parentElement.parentElement.style.display = 'none'; + document.getElementById('button-tutorial').addEventListener('click', runTutorial); tutorialMode = false; }); diff --git a/app/static/js/view.js b/app/static/js/view.js index 96a3902..17d4c71 100644 --- a/app/static/js/view.js +++ b/app/static/js/view.js @@ -20,8 +20,10 @@ async function plotCoordinates(div, X, Y, ZGenes, layoutCoordinates = {}) { paper_bgcolor: 'rgba(0,0,0,0.4)', plot_bgcolor: 'rgba(0,0,0,1)', 'title': 'mRNA map', - 'showlegend': true, - 'margin': { 'l': 10, 'r': 0, 't': 0, 'b': 15 }, + 'margin': { 'l': 20, 'r': 0, 't': 0, 'b': 15 }, + font: { color: '#dddddd' }, + xaxis: { title: 'μm' }, + yaxis: { scaleanchor: "x", }, }, ...layoutCoordinates }; @@ -45,7 +47,9 @@ async function plotCoordinates(div, X, Y, ZGenes, layoutCoordinates = {}) { }); } - Plotly.plot(div, data, layoutCoordinates); + Plotly.newPlot(div, data, layoutCoordinates); + document.getElementById('divScale').style.display = 'block'; + }; async function plotSignatures(div, genes, clusterLabels, signatureMatrix) { @@ -79,9 +83,99 @@ async function plotSignatures(div, genes, clusterLabels, signatureMatrix) { } - Plotly.newPlot(div, data, layout_signatures, { responsive: true }); + Plotly.react(div, data, layout_signatures, { responsive: true }); }; +function generateScalebar(start = 30, end = 120, umPerPx = 1) { + + linestyle = { + color: 'rgba(255, 255, 255, 1)', + width: 2 + }; + + + var length = (end - start) * umPerPx; + var decimals = Math.ceil(Math.log10(length)) - 1; + var inter = length / (Math.pow(10, decimals)); + + var lengthRound = Math.ceil(inter) * Math.pow(10, decimals); + + console.log(length, decimals, inter, lengthRound); + + end = start + lengthRound / umPerPx + + text = lengthRound + " μm"; + + layout = { + // text + annotations: [{ + showarrow: false, + text: '' + text + '', + align: "center", + yref: 'paper', + x: (start + end) / 2, + xanchor: "center", + y: 0.05, + yanchor: "bottom", + font: { + size: 13, + } + },], + shapes: [ + //Surrounding box Rectangle + { + type: 'rect', + yref: 'paper', + x0: start - 10, + y0: 0.012, + x1: end + 10, + y1: 0.06, + fillcolor: 'rgba(0,0,0,0.6)', + line: { + color: 'rgba(255, 255, 255, 1)', + width: 1.2 + }, + }, + //horizontal line + { + type: 'line', + yref: 'paper', + x0: start, + y0: 0.03, + x1: end, + y1: 0.03, + // fillcolor: 'rgba(255,255,255,1)', + line: linestyle, + }, + { + //caps + type: 'line', + yref: 'paper', + x0: start, + y0: 0.02, + x1: start, + y1: 0.04, + // fillcolor: 'rgba(0,0,0,0.6)', + line: linestyle + }, + { + type: 'line', + yref: 'paper', + x0: end, + y0: 0.02, + x1: end, + y1: 0.04, + fillcolor: 'rgba(0,0,0,0.6)', + line: linestyle + }, + + ] + + } + + return layout; +} + function plotVfNorm(div, vfNorm, layout = {}) { var data = [ @@ -90,21 +184,31 @@ function plotVfNorm(div, vfNorm, layout = {}) { type: 'heatmapgl', colorscale: 'Viridis', 'showgrid': false, - } + }, + ]; - var layoutVfNorm = { // all "layout" attributes: #layout - paper_bgcolor: 'rgba(0,0,0,0.7)', - plot_bgcolor: 'rgba(0,0,0,0)', - showlegend: false, - showscale: false, - 'font': { - color: 'white', + + var layoutVfNorm = { + ...{ // all "layout" attributes: #layout + paper_bgcolor: 'rgba(0,0,0,0.7)', + plot_bgcolor: 'rgba(0,0,0,0)', + showlegend: false, + showscale: true, + 'font': { + color: 'white', + }, + 'title': 'generated vector field norm:', + + 'xaxis': { title: 'px' }, + 'yaxis': { scaleanchor: "x", title: 'px' }, + + 'showlegend': false, + }, - 'title': 'generated vector field norm:', - // 'margin': { 'l': 10, 'r': 0, 't': 0, 'b': 15 }, - }; + ...layout + } - Plotly.plot(div, data, layoutVfNorm); + Plotly.newPlot(div, data, layoutVfNorm, { editable: true }); }; @@ -140,21 +244,37 @@ function createColorMap(nColors) { return [colorMap, tickvals] }; -function plotCelltypeMap(div, celltypeMap, clusterLabels, getClusterLabel = null) { +function checkForExistingPlot(div) { + return (document.getElementById(div).getElementsByClassName('plot-container').length > 0); +} + +function printErr(div, id, msg) { + err = document.createElement('div', { role: "alert" }) + err.id = id; + err.className = "alert alert-warning"; + err.innerHTML = (msg); + $(div).append(err) +} + +function plotCelltypeMap(div, celltypeMap, clusterLabels, getClusterLabel = null, layout = {}) { [colorMap, tickVals] = createColorMap(clusterLabels.length); var tickText = ['ECM'].concat(clusterLabels) var layout = { - paper_bgcolor: 'rgba(0,0,0,0.7)', - plot_bgcolor: 'rgba(0,0,0,0)', - showlegend: false, - showscale: false, - 'font': { - color: 'white', - }, - 'title': 'generated vector field norm:' + ...{ + paper_bgcolor: 'rgba(0,0,0,0.7)', + plot_bgcolor: 'rgba(0,0,0,0)', + showlegend: false, + showscale: false, + 'font': { + color: 'white', + }, + 'title': 'generated tissue map:', + 'xaxis': {}, + 'yaxis': { scaleanchor: "x", }, + }, ...layout } var data = [ @@ -173,9 +293,25 @@ function plotCelltypeMap(div, celltypeMap, clusterLabels, getClusterLabel = null ticktext: tickText, } - } + }, + // { + + // x: [0, 1, 2], + // y: [3, 3, 3], + // mode: 'lines+text', + // name: 'Lines and Text', + // text: ['Text G', 'Text H', 'Text I'], + // textposition: 'bottom', + // type: 'scatter' + // } ]; - Plotly.plot(div, data, layout, { responsive: true }); + // console.log(document.getElementById(div),checkForExistingPlot(div)); + // if (!checkForExistingPlot(div)) { + Plotly.react(div, data, layout, { responsive: true }); + // }else{ + // console.log('updating...'); + // Plotly.update(div, data, layout, { responsive: true }); + // } if (getClusterLabel != null) { var hoverInfo = document.getElementById('hoverinfo'); @@ -214,9 +350,9 @@ function plotCelltypeMap(div, celltypeMap, clusterLabels, getClusterLabel = null }; function displayParameterGenerator() { - var previewGenerator = document.getElementById('preview-generator'); + var buttonParameters = document.getElementById('bar-parameters'); - +var previewGenerator = document.getElementById('preview-generator'); previewGenerator.style.display = "block"; buttonParameters.innerHTML = 'Close preview generator'; } @@ -229,6 +365,18 @@ function hideParameterGenerator() { buttonParameters.innerHTML = 'Use preview generator for parameter search'; } +function refreshParameterGenerator() { + var previewGenerator = document.getElementById('preview-generator'); + if (previewGenerator.style.display == "block"){ + var buttonParameters = document.getElementById('bar-parameters'); + previewGenerator.style.display = "block"; + buttonParameters.innerHTML = 'Refresh preview generator'; + } + +} + + + function updateParameterRectangle(clickCoords, rectWidth) { // update_layout_parameters(rect_center = clickCoords); @@ -250,69 +398,16 @@ function updateParameterRectangle(clickCoords, rectWidth) { Plotly.relayout('parameter-coordinates', update) }; +function setVfSizeIndicator(width, height, genes) { + var sizeIndicator; - - - - -function updateTable(rows, allTissues, allSpecies) { - const table = document.getElementById('marker-body'); - table.innerHTML=''; - - - - var pos_y = 0; - - for (var r = 0; r < rows.length; r++) { - - if ((rows[r]).visible) { - row = table.insertRow(-1); - row.id = (rows[r]).label - // [cellType,tissueType,species] = (rows[r]).tissue.split('|'); - - cellType = (rows[r]).celltype; - tissueType = (rows[r]).tissue; - species = (rows[r]).species; - // console.log(rows[r]); - if (r == 0) { - var topTissue = tissueType; - var topSpecies = species; - } - - tickCell = row.insertCell(-1); - var x = document.createElement("INPUT"); - x.setAttribute("type", "checkbox"); - x.checked = true; - tickCell.appendChild(x); - - cellTypeCell = row.insertCell(-1); - - // checked = (tissueType==topTissue && species==topSpecies )? 'checked' : ' '; - cellTypeCell.innerHTML += (cellType); - cellTypeCell.classList.add('tissue-cell'); - - var tissueCell = row.insertCell(-1); - // checked = (tissueType==topTissue)? 'checked' : ' '; - tissueCell.innerHTML += (tissueType); - tissueCell.classList.add('tissue-cell'); - - speciesCell = row.insertCell(-1); - // checked = (species==topSpecies )? 'checked' : ' '; - speciesCell.innerHTML += (species); - speciesCell.classList.add('tissue-cell'); - - var expressions = ((rows[r]).expressions); - - for (var e = 0; e < expressions.length; e++) { - cell = row.insertCell(e + 4); - exp = expressions[e]; - cell.style.backgroundColor = 'rgba(' + [40, 255, 50, exp].join(',') + ')'; - } - pos_y++; - } + if (width == 0 || height == 0 || genes.length == 0) { + size = ' - '; + } else { + size = (width * height * genes.length * 32 / 2 ** 30).toFixed(1) + " gB"; } - - -} + document.getElementById("vf-size-information").innerHTML = + "total size: (" + width + "," + height + "," + genes.length + "); " + size; +}; \ No newline at end of file diff --git a/app/templates/indexMain.html b/app/templates/indexMain.html index 30c82f1..9998ad6 100644 --- a/app/templates/indexMain.html +++ b/app/templates/indexMain.html @@ -40,7 +40,7 @@

SSAM.net