diff --git a/TSUMUGI/app/js/components.js b/TSUMUGI/app/js/components.js new file mode 100644 index 0000000..0074e1f --- /dev/null +++ b/TSUMUGI/app/js/components.js @@ -0,0 +1,16 @@ +export function calculateConnectedComponents(cy) { + const visibleElements = cy.elements(':visible'); + const connectedComponents = visibleElements.components(); + + return connectedComponents.map(component => { + let componentObject = {}; + component.nodes().forEach(node => { + const nodeLabel = node.data('label'); + const nodeAnnotations = Array.isArray(node.data('annotation')) + ? node.data('annotation') + : [node.data('annotation')]; + componentObject[nodeLabel] = nodeAnnotations; + }); + return componentObject; + }); +} diff --git a/TSUMUGI/app/js/exporter.js b/TSUMUGI/app/js/exporter.js index d80729c..baaf4c5 100644 --- a/TSUMUGI/app/js/exporter.js +++ b/TSUMUGI/app/js/exporter.js @@ -1,8 +1,10 @@ +import { calculateConnectedComponents } from './components.js'; + // -------------------------------------------------------- // PNG Exporter // -------------------------------------------------------- -function exportGraphAsPNG() { +export function exportGraphAsPNG(cy, file_name) { const pngContent = cy.png({ scale: 6.25, // Scale to achieve 600 DPI full: true // Set to true to include the entire graph, even the offscreen parts @@ -10,7 +12,7 @@ function exportGraphAsPNG() { const a = document.createElement('a'); a.href = pngContent; - a.download = 'TSUMUGI_XXX_genesymbol.png'; + a.download = `${file_name}.png`; document.body.appendChild(a); a.click(); document.body.removeChild(a); @@ -21,9 +23,9 @@ function exportGraphAsPNG() { // CSV Exporter // -------------------------------------------------------- -function exportGraphAsCSV() { +export function exportGraphAsCSV(cy, file_name) { // calculateConnectedComponentsを利用して連結成分を取得 - const connected_component = calculateConnectedComponents(); + const connected_component = calculateConnectedComponents(cy); // CSVのヘッダー行 let csvContent = "cluster,gene,phenotypes\n"; @@ -45,7 +47,7 @@ function exportGraphAsCSV() { const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; - a.download = 'TSUMUGI_XXX_genesymbol.csv'; + a.download = `${file_name}.csv`; document.body.appendChild(a); a.click(); document.body.removeChild(a); diff --git a/TSUMUGI/app/js/tooltips.js b/TSUMUGI/app/js/tooltips.js new file mode 100644 index 0000000..19b74e4 --- /dev/null +++ b/TSUMUGI/app/js/tooltips.js @@ -0,0 +1,96 @@ +// ############################################################ +// Tooltip Handling Functions +// ############################################################ + +// Function to remove all existing tooltips +export function removeTooltips() { + document.querySelectorAll('.cy-tooltip').forEach(el => el.remove()); +} + +// Function to create tooltip content for nodes and edges +function createTooltip(event, cy, map_symbol_to_id) { + const data = event.target.data(); + let tooltipText = ''; + let pos; + + if (event.target.isNode()) { + const annotations = Array.isArray(data.annotation) + ? data.annotation.map(anno => '・ ' + anno).join('
') + : '・ ' + data.annotation; + + const geneID = map_symbol_to_id[data.label] || "UNKNOWN"; // undefined の場合に備える + const url_impc = `https://www.mousephenotype.org/data/genes/${geneID}`; + tooltipText = `Phenotypes of ${data.label} KO mice
` + annotations; + + pos = event.target.renderedPosition(); + } else if (event.target.isEdge()) { + const sourceNode = cy.getElementById(data.source).data('label'); + const targetNode = cy.getElementById(data.target).data('label'); + const annotations = Array.isArray(data.annotation) + ? data.annotation.map(anno => '・ ' + anno).join('
') + : '・ ' + data.annotation; + + tooltipText = `Shared phenotypes of ${sourceNode} and ${targetNode} KOs
` + annotations; + + const sourcePos = cy.getElementById(data.source).renderedPosition(); + const targetPos = cy.getElementById(data.target).renderedPosition(); + pos = { x: (sourcePos.x + targetPos.x) / 2, y: (sourcePos.y + targetPos.y) / 2 }; + } + + return { tooltipText, pos }; +} + +// Function to show tooltip +export function showTooltip(event, cy, map_symbol_to_id) { + removeTooltips(); // Remove existing tooltips + + const { tooltipText, pos } = createTooltip(event, cy, map_symbol_to_id); + + const tooltip = document.createElement('div'); + tooltip.classList.add('cy-tooltip'); + tooltip.innerHTML = tooltipText; + Object.assign(tooltip.style, { + position: 'absolute', + left: `${pos.x + 10}px`, + top: `${pos.y + 10}px`, + padding: '5px', + background: 'white', + border: '1px solid #ccc', + borderRadius: '5px', + boxShadow: '0 2px 10px rgba(0,0,0,0.2)', + zIndex: '1000', + cursor: 'move', + userSelect: 'text' + }); + + document.querySelector('.cy').appendChild(tooltip); + enableTooltipDrag(tooltip); +} + +// Function to enable dragging for tooltips +function enableTooltipDrag(tooltip) { + let isDragging = false; + let offset = { x: 0, y: 0 }; + + tooltip.addEventListener('mousedown', function (e) { + e.stopPropagation(); + isDragging = true; + const rect = tooltip.getBoundingClientRect(); + offset.x = e.clientX - rect.left; + offset.y = e.clientY - rect.top; + tooltip.style.cursor = 'grabbing'; + }); + + document.addEventListener('mousemove', function (e) { + if (isDragging) { + const containerRect = document.querySelector('.cy').getBoundingClientRect(); + tooltip.style.left = `${e.clientX - offset.x - containerRect.left}px`; + tooltip.style.top = `${e.clientY - offset.y - containerRect.top}px`; + } + }); + + document.addEventListener('mouseup', function () { + isDragging = false; + tooltip.style.cursor = 'move'; + }); +} diff --git a/TSUMUGI/app/js/value_scaler.js b/TSUMUGI/app/js/value_scaler.js new file mode 100644 index 0000000..0a6a28f --- /dev/null +++ b/TSUMUGI/app/js/value_scaler.js @@ -0,0 +1,28 @@ + +export function scaleToOriginalRange(value, minValue, maxValue) { + // Scales a value from the range [1, 10] to a new range [minValue, maxValue]. + return minValue + (value - 1) * (maxValue - minValue) / 9; +} + +export function scaleValue(value, minValue, maxValue, minScale, maxScale) { + // スケールをminScaleとmaxScaleの範囲に変換 + if (minValue == maxValue) { + return (maxScale + minScale) / 2; + } + return minScale + (value - minValue) * (maxScale - minScale) / (maxValue - minValue); +} + +export function getColorForValue(value) { + // value を1-10の範囲から0-1の範囲に変換 + const ratio = (value - 1) / (10 - 1); + + // Light Yellow から Orange へのグラデーション + const r1 = 248, g1 = 229, b1 = 140; // Light Yellow + const r2 = 255, g2 = 140, b2 = 0; // Orange + + const r = Math.round(r1 + (r2 - r1) * ratio); + const g = Math.round(g1 + (g2 - g1) * ratio); + const b = Math.round(b1 + (b2 - b1) * ratio); + + return `rgb(${r}, ${g}, ${b})`; +} diff --git a/notebooks/notebools-web/995_generate_html_and_js.ipynb b/notebooks/notebools-web/995_generate_html_and_js.ipynb index 6ba4245..eb84e18 100644 --- a/notebooks/notebools-web/995_generate_html_and_js.ipynb +++ b/notebooks/notebools-web/995_generate_html_and_js.ipynb @@ -9,7 +9,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -22,15 +22,15 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "/mnt/e/TSUMUGI-dev/notebooks/notebools-web\n", - "/mnt/e/TSUMUGI-dev\n" + "/mnt/e/Research/TSUMUGI\n", + "/mnt/e/Research/TSUMUGI\n" ] } ], @@ -54,7 +54,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -83,7 +83,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -98,7 +98,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -117,7 +117,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -126,7 +126,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -146,7 +146,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -164,7 +164,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -178,7 +178,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -209,7 +209,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -226,7 +226,7 @@ "3761" ] }, - "execution_count": 11, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } @@ -254,7 +254,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ @@ -304,7 +304,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -345,7 +345,7 @@ }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -362,7 +362,7 @@ }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ @@ -381,7 +381,7 @@ }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -458,13 +458,14 @@ }, { "cell_type": "code", - "execution_count": 86, + "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ + "Asxl1\n", "Rab10\n" ] } @@ -477,7 +478,7 @@ "########################################\n", "\n", "cat data/overlap/available_gene_symbols.txt |\n", - " grep Rab10 | # <- ここで興味のあるgene symbolを選択\n", + " grep -e Rab10 -e Asxl1 | # <- ここで興味のあるgene symbolを選択\n", " while read gene_symbol; do\n", " echo $gene_symbol\n", "\n", @@ -505,14 +506,14 @@ }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "2025/03/01 15:45:33\n" + "2025/03/02 05:19:07\n" ] } ], @@ -539,7 +540,7 @@ ], "metadata": { "kernelspec": { - "display_name": "env-tsumugi", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -553,7 +554,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.8" + "version": "3.10.15" } }, "nbformat": 4, diff --git a/test-tsumugi/app/genesymbol/Asxl1.html b/test-tsumugi/app/genesymbol/Asxl1.html new file mode 100644 index 0000000..be4a732 --- /dev/null +++ b/test-tsumugi/app/genesymbol/Asxl1.html @@ -0,0 +1,129 @@ + + + + + + + Asxl1 + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Phenotype-similarity gene network of
Asxl1 +

+
+ +
+
+ +
+ + +
+ + +
+ Phenotypes similarity: + 1 - 10 +
+
+ + +
+ Font size: + + 20 +
+
+ +
+ Edge width: + 5 +
+
+ +
+ Node repulsion (Cose only): + 5 +
+
+ + + + +
+ Genotype specificity: +
+ + + +
+
+ +
+ Sex specificity: +
+ + +
+
+ + + + +
+ + + + + +
+
+ + +
+ + + Few + Many + +

Similarity of accessory phenotypes

+
+ +
+
+ + + + diff --git a/test-tsumugi/app/genesymbol/Asxl1.js b/test-tsumugi/app/genesymbol/Asxl1.js new file mode 100644 index 0000000..8d52b90 --- /dev/null +++ b/test-tsumugi/app/genesymbol/Asxl1.js @@ -0,0 +1,415 @@ +import { exportGraphAsPNG, exportGraphAsCSV } from '../js/exporter.js'; +import { scaleToOriginalRange, scaleValue, getColorForValue } from '../js/value_scaler.js'; +import { removeTooltips, showTooltip } from '../js/tooltips.js'; +import { calculateConnectedComponents } from '../js/components.js'; + +// ############################################################################ +// Input handling +// ############################################################################ + + +const elements = (function () { + const req = new XMLHttpRequest(); + let result = null; + + try { + req.open("GET", "../../data/genesymbol/Asxl1.json.gz", false); + req.overrideMimeType("text/plain; charset=x-user-defined"); // バイナリデータとして扱うための設定 + req.send(null); + if (req.status === 200) { + // gzipデータをUint8Arrayに変換 + const compressedData = new Uint8Array( + req.responseText.split("").map(c => c.charCodeAt(0) & 0xff) + ); + // pakoでデコード + const decompressedData = pako.ungzip(compressedData, { to: "string" }); + result = JSON.parse(decompressedData); + } else { + console.error("HTTP error!! status:", req.status); + } + } catch (error) { + console.error("Failed to load or decode JSON.gz:", error); + } + + return result; +})(); + +const map_symbol_to_id = (function () { + const req = new XMLHttpRequest(); + let result = null; + req.onreadystatechange = function () { + if (req.readyState === 4 && req.status === 200) { + result = JSON.parse(req.responseText); + } + }; + req.open("GET", "../../data/marker_symbol_accession_id.json", false); + + + req.send(null); + return result; +})(); + + +// ############################################################################ +// 遺伝型・正特異的フィルタリング関数 +// ############################################################################ + +// フィルターフォームの取得 +const filterGenotypeForm = document.getElementById('genotype-filter-form'); +const filterSexForm = document.getElementById('sex-filter-form'); + +// フィルタリング関数(遺伝型 + 性別) +function filterElementsByGenotypeAndSex() { + const checkedGenotypes = Array.from(filterGenotypeForm.querySelectorAll('input:checked')).map(input => input.value); + const checkedSexs = Array.from(filterSexForm.querySelectorAll('input:checked')).map(input => input.value); + + // console.log("検索キーワード (Genotype):", checkedGenotypes); + // console.log("検索キーワード (Sex):", checkedSexs); + + let targetElements; + + // もし checkedSexs に Female と Male の両方が含まれていたら、性別のフィルターを無効にし、遺伝型のフィルターのみ適用 + if (checkedSexs.includes("Female") && checkedSexs.includes("Male")) { + // console.log("性別フィルター無効(遺伝型のみ適用)"); + targetElements = elements; + } else { + targetElements = elements.map(item => { + if (item.data.annotation) { + const filteredAnnotations = item.data.annotation.filter(annotation => { + const sexMatch = checkedSexs.some(sex => annotation.includes(`${sex}`)); + return sexMatch; + }); + + return { ...item, data: { ...item.data, annotation: filteredAnnotations } }; + } + return item; + }).filter(item => item.data.annotation && item.data.annotation.length > 0); + } + + // 遺伝型フィルターの適用 + const filteredElements = targetElements.map(item => { + if (item.data.annotation) { + const filteredAnnotations = item.data.annotation.filter(annotation => { + const genotypeMatch = checkedGenotypes.some(genotype => annotation.includes(`${genotype}`)); + return genotypeMatch; + }); + + return { ...item, data: { ...item.data, annotation: filteredAnnotations } }; + } + return item; + }).filter(item => item.data.annotation && item.data.annotation.length > 0); + + // Cytoscape のデータを更新 + cy.elements().remove(); // 既存の要素を削除 + cy.add(filteredElements); // 新しい要素を追加 + filterElements(); // 孤立ノードを削除 +} + +// フォーム変更時にフィルタリング関数を実行 +filterGenotypeForm.addEventListener('change', filterElementsByGenotypeAndSex); +filterSexForm.addEventListener('change', filterElementsByGenotypeAndSex); + + +// ############################################################################ +// Cytoscape handling +// ############################################################################ + +const nodeSizes = elements.filter(ele => ele.data.node_color !== undefined).map(ele => ele.data.node_color); +const edgeSizes = elements.filter(ele => ele.data.edge_size !== undefined).map(ele => ele.data.edge_size); + +const nodeMin = Math.min(...nodeSizes); +const nodeMax = Math.max(...nodeSizes); +const edgeMin = Math.min(...edgeSizes); +const edgeMax = Math.max(...edgeSizes); + +function getLayoutOptions() { + return { + name: currentLayout, + nodeRepulsion: nodeRepulsionValue, + componentSpacing: componentSpacingValue + }; +} + +let currentLayout = 'cose'; + +const nodeRepulsionMin = 1; +const nodeRepulsionMax = 10000; +const componentSpacingMin = 1; +const componentSpacingMax = 200; + +let nodeRepulsionValue = scaleToOriginalRange(parseFloat(document.getElementById('nodeRepulsion-slider').value), nodeRepulsionMin, nodeRepulsionMax); +let componentSpacingValue = scaleToOriginalRange(parseFloat(document.getElementById('nodeRepulsion-slider').value), componentSpacingMin, componentSpacingMax); + +const cy = cytoscape({ + container: document.querySelector('.cy'), + elements: elements, + style: [ + { + selector: 'node', + style: { + 'label': 'data(label)', + 'text-valign': 'center', + 'text-halign': 'center', + 'font-size': '20px', + 'width': 15, + 'height': 15, + 'background-color': function (ele) { + const color_value = scaleValue(ele.data('node_color'), nodeMin, nodeMax, 1, 10); + return getColorForValue(color_value); + } + } + }, + { + selector: 'edge', + style: { + 'curve-style': 'bezier', + 'text-rotation': 'autorotate', + 'width': function (ele) { + return scaleValue(ele.data('edge_size'), edgeMin, edgeMax, 0.5, 2); + } + } + } + ], + layout: getLayoutOptions() +}); + + +// // レイアウトが変更されるか、フィルタリングが実行された際に連結成分を計算する関数 +// function calculateConnectedComponents() { +// // 表示されている要素のみを取得 +// const visibleElements = cy.elements(':visible'); + +// // 可視状態の要素で連結成分を計算 +// const connectedComponents = visibleElements.components(); + +// let connected_component = connectedComponents.map(component => { +// let componentObject = {}; + +// // ノードを処理 +// component.nodes().forEach(node => { +// const nodeLabel = node.data('label'); +// const nodeAnnotations = Array.isArray(node.data('annotation')) +// ? node.data('annotation') +// : [node.data('annotation')]; // annotation が配列でない場合も考慮 + +// // ノード名をキー、アノテーションを値とするオブジェクトを作成 +// componentObject[nodeLabel] = nodeAnnotations; +// }); + +// return componentObject; +// }); + +// // 結果をログに出力(デバッグ用) +// // console.log('Connected Components (Formatted):', connected_component); + +// // 必要に応じて connected_component を他の場所で利用可能にする +// return connected_component; +// } + +// レイアウト変更後にイベントリスナーを設定 +cy.on('layoutstop', function () { + calculateConnectedComponents(cy); +}); + + +// ############################################################################ +// Visualization handling +// ############################################################################ + +// -------------------------------------------------------- +// Network layout dropdown +// -------------------------------------------------------- + +document.getElementById('layout-dropdown').addEventListener('change', function () { + currentLayout = this.value; + cy.layout({ name: currentLayout }).run(); +}); + +// -------------------------------------------------------- +// Initialization of the Slider for Phenotypes similarity +// -------------------------------------------------------- +const edgeSlider = document.getElementById('filter-edge-slider'); +noUiSlider.create(edgeSlider, { + start: [1, 10], + connect: true, + range: { + 'min': 1, + 'max': 10 + }, + step: 1 +}); + +function filterElements() { + const edgeSliderValues = edgeSlider.noUiSlider.get().map(parseFloat); + + const edgeMinValue = scaleToOriginalRange(edgeSliderValues[0], edgeMin, edgeMax); + const edgeMaxValue = scaleToOriginalRange(edgeSliderValues[1], edgeMin, edgeMax); + + cy.nodes().forEach(function (node) { + node.style('display', 'element'); + }); + + // Filter edges based on size + cy.edges().forEach(function (edge) { + const edgeSize = edge.data('edge_size'); + const sourceNode = cy.getElementById(edge.data('source')); + const targetNode = cy.getElementById(edge.data('target')); + + if (sourceNode.style('display') === 'element' && targetNode.style('display') === 'element' && + edgeSize >= edgeMinValue && edgeSize <= edgeMaxValue) { + edge.style('display', 'element'); + } else { + edge.style('display', 'none'); + } + }); + + // calculateConnectedComponentsを利用して連結成分を取得 + const connected_component = calculateConnectedComponents(cy); + + // node_colorが1のノードを含む連結成分のみを選択 + const componentsWithNodeColor1 = connected_component.filter(component => { + return Object.keys(component).some(nodeLabel => { + const node = cy.$(`node[label="${nodeLabel}"]`); + return node.data('node_color') === 1; + }); + }); + + // すべてのノードとエッジを一旦非表示にする + cy.nodes().style('display', 'none'); + cy.edges().style('display', 'none'); + + // node_colorが1のノードを含む連結成分のみ表示 + componentsWithNodeColor1.forEach(component => { + Object.keys(component).forEach(nodeLabel => { + const node = cy.$(`node[label="${nodeLabel}"]`); + node.style('display', 'element'); + node.connectedEdges().style('display', 'element'); + }); + }); + + cy.nodes().forEach(function (node) { + const connectedEdges = node.connectedEdges().filter(edge => edge.style('display') === 'element'); + if (connectedEdges.length === 0) { + node.style('display', 'none'); // Hide node if no connected edges + } + }); + + // Reapply layout after filtering + cy.layout(getLayoutOptions()).run(); +} + +// -------------------------------------------------------- +// Update the slider values when the sliders are moved +// -------------------------------------------------------- + +edgeSlider.noUiSlider.on('update', function (values) { + const intValues = values.map(value => Math.round(value)); + document.getElementById('edge-size-value').textContent = intValues.join(' - '); + filterElements(); +}); + + +// ############################################################################ +// Cytoscape's visualization setting +// ############################################################################ + +// -------------------------------------------------------- +// Slider for Font size +// -------------------------------------------------------- +const fontSizeSlider = document.getElementById('font-size-slider'); +noUiSlider.create(fontSizeSlider, { + start: 20, + connect: [true, false], + range: { + 'min': 1, + 'max': 50 + }, + step: 1 +}); +fontSizeSlider.noUiSlider.on('update', function (value) { + const intValues = Math.round(value); + document.getElementById('font-size-value').textContent = intValues; + cy.style().selector('node').style('font-size', intValues + 'px').update(); +}); + +// -------------------------------------------------------- +// Slider for Edge width +// -------------------------------------------------------- +const edgeWidthSlider = document.getElementById('edge-width-slider'); +noUiSlider.create(edgeWidthSlider, { + start: 5, + connect: [true, false], + range: { + 'min': 1, + 'max': 10 + }, + step: 1 +}); +edgeWidthSlider.noUiSlider.on('update', function (value) { + const intValues = Math.round(value); + document.getElementById('edge-width-value').textContent = intValues; + cy.style().selector('edge').style('width', function (ele) { + return scaleValue(ele.data('edge_size'), edgeMin, edgeMax, 0.5, 2) * intValues; + }).update(); +}); + +// -------------------------------------------------------- +// Slider for Node repulsion +// -------------------------------------------------------- +const nodeRepulsionSlider = document.getElementById('nodeRepulsion-slider'); +noUiSlider.create(nodeRepulsionSlider, { + start: 5, + connect: [true, false], + range: { + 'min': 1, + 'max': 10 + }, + step: 1 +}); +nodeRepulsionSlider.noUiSlider.on('update', function (value) { + const intValues = Math.round(value); + nodeRepulsionValue = scaleToOriginalRange(parseFloat(intValues), nodeRepulsionMin, nodeRepulsionMax); + componentSpacingValue = scaleToOriginalRange(parseFloat(intValues), componentSpacingMin, componentSpacingMax); + document.getElementById('node-repulsion-value').textContent = intValues; + cy.layout(getLayoutOptions()).run(); +}); + + +// ############################################################################ +// Tooltip handling +// ############################################################################ + +// Show tooltip on tap +cy.on('tap', 'node, edge', function (event) { + showTooltip(event, cy, map_symbol_to_id); +}); + +// Hide tooltip when tapping on background +cy.on('tap', function (event) { + if (event.target === cy) { + removeTooltips(); + } +}); + +// ############################################################################ +// Exporter +// ############################################################################ + +const file_name = 'TSUMUGI_Asxl1'; + +// -------------------------------------------------------- +// PNG Exporter +// -------------------------------------------------------- + +document.getElementById('export-png').addEventListener('click', function () { + exportGraphAsPNG(cy, file_name); +}); + + +// -------------------------------------------------------- +// CSV Exporter +// -------------------------------------------------------- + +document.getElementById('export-csv').addEventListener('click', function () { + exportGraphAsCSV(cy, file_name); +}); diff --git a/test-tsumugi/data/genesymbol/Asxl1.json.gz b/test-tsumugi/data/genesymbol/Asxl1.json.gz new file mode 100644 index 0000000..d5c5da1 Binary files /dev/null and b/test-tsumugi/data/genesymbol/Asxl1.json.gz differ diff --git a/test-tsumugi/data/genesymbol/Rab10.json.gz b/test-tsumugi/data/genesymbol/Rab10.json.gz index 57b9455..5d3417b 100644 Binary files a/test-tsumugi/data/genesymbol/Rab10.json.gz and b/test-tsumugi/data/genesymbol/Rab10.json.gz differ diff --git a/test-tsumugi/data/phenotype/increased_fasting_circulating_glucose_level.json.gz b/test-tsumugi/data/phenotype/increased_fasting_circulating_glucose_level.json.gz index 45ca8a8..7e41e2d 100644 Binary files a/test-tsumugi/data/phenotype/increased_fasting_circulating_glucose_level.json.gz and b/test-tsumugi/data/phenotype/increased_fasting_circulating_glucose_level.json.gz differ diff --git a/test-tsumugi/data/phenotype/male_infertility.json.gz b/test-tsumugi/data/phenotype/male_infertility.json.gz index a69fe56..a2db544 100644 Binary files a/test-tsumugi/data/phenotype/male_infertility.json.gz and b/test-tsumugi/data/phenotype/male_infertility.json.gz differ diff --git a/test-tsumugi/data/phenotype/preweaning_lethality,_complete_penetrance.json.gz b/test-tsumugi/data/phenotype/preweaning_lethality,_complete_penetrance.json.gz index 54337df..7c2a8fb 100644 Binary files a/test-tsumugi/data/phenotype/preweaning_lethality,_complete_penetrance.json.gz and b/test-tsumugi/data/phenotype/preweaning_lethality,_complete_penetrance.json.gz differ diff --git a/test-tsumugi/data/phenotype/preweaning_lethality,_incomplete_penetrance.json.gz b/test-tsumugi/data/phenotype/preweaning_lethality,_incomplete_penetrance.json.gz index 3ab348d..a5587bc 100644 Binary files a/test-tsumugi/data/phenotype/preweaning_lethality,_incomplete_penetrance.json.gz and b/test-tsumugi/data/phenotype/preweaning_lethality,_incomplete_penetrance.json.gz differ diff --git a/test-tsumugi/index.html b/test-tsumugi/index.html index 9d62157..c9cc0e5 100644 --- a/test-tsumugi/index.html +++ b/test-tsumugi/index.html @@ -92,10 +92,9 @@