Last active
December 9, 2017 21:27
-
-
Save tgotwig/2ea023d4296af7772e2797c90bb50012 to your computer and use it in GitHub Desktop.
Syntenyplot (+tooltip)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<svg width="960" height="500" | |
></svg> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script src="https://unpkg.com/tippy.js@2.0.3/dist/tippy.all.min.js"></script> | |
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet"> | |
<script> | |
;(function (global, d3) { | |
const OPACITY_LOW = 0.4 | |
const OPACITY_HIGH = 1 | |
const STROKE_COLORS = d3.scaleOrdinal(['#2196f3', '#ff9800']) // material colors by material.io | |
const INBOX_PADDING = 8 | |
let STROKE_WIDTH = 3 | |
/** | |
* Creates an interactive dotplot from json like this: | |
* { contig: <string>, rStart: <NUM>, rEnd: <NUM>, cStart: <NUM>, cEnd: <NUM>, strand: <string: + or -> } | |
* and renders it into selector. | |
* | |
* @param {string} data The data: [{"1.1": "1.2", "2.1": 2.2, ...}]. | |
* @param {string} SELECTOR The seletor. | |
* @param {string} REF_LAB The name of x-axis-label. | |
* @param {string} QRY_LAB The name of y-axis-label. | |
*/ | |
let drawDotPlot = function (data, REF_LAB, QRY_LAB) { | |
const svg = d3.select('svg') | |
const margin = {top: 20, right: 20, bottom: 30, left: 40} | |
const WIDTH = +svg.attr('width') - margin.left - margin.right | |
const HEIGHT = +svg.attr('height') - margin.top - margin.bottom | |
const xScale = d3.scaleLinear().range([120, WIDTH]) | |
const xAxis = d3.axisBottom(xScale) | |
const yScale = d3.scaleLinear().range([HEIGHT, 0]) | |
const yAxis = d3.axisLeft(yScale) | |
svg | |
.attr('class', 'dotPlot') | |
.attr('width', WIDTH + margin.left + margin.right) | |
.attr('height', HEIGHT + margin.top + margin.bottom) | |
.append('g') | |
.attr('transform', `translate(${margin.left}, ${margin.top})`) | |
const xMin = d3.min(data, d => d.rStart) | |
const xMax = d3.max(data, d => d.rEnd) | |
const xPadding = (xMax - xMin) / WIDTH * (STROKE_WIDTH + INBOX_PADDING) | |
let yMin = -1 | |
let yMax = 0 | |
Array.from(data, (e) => { | |
if (yMin === -1) { | |
yMin = e.cStart | |
yMax = e.cStart | |
} | |
yMax += e.cEnd - e.cStart | |
}) | |
const yPadding = (yMax - yMin) / HEIGHT * (STROKE_WIDTH + INBOX_PADDING) | |
// x-axis | |
xScale.domain([xMin - xPadding, xMax + xPadding]) | |
svg.append('g') | |
.attr('id', 'xxx') | |
.attr('transform', 'translate(0,' + HEIGHT + ')') | |
.call(xAxis) | |
// x-label | |
svg.append('text') | |
.attr('transform', 'translate(' + (WIDTH / 2) + ' ,' + (HEIGHT + margin.top + 16) + ')') // from 10 to 16!!! | |
.style('text-anchor', 'middle') | |
.text(`${REF_LAB}`) | |
// x-axis-2 | |
svg.append('line') | |
.attr('x1', 0) | |
.attr('y1', 0.5) | |
.attr('x2', WIDTH) | |
.attr('y2', 0.5) | |
.style('stroke', 'black') | |
// y-axis | |
yScale.domain([yMin - yPadding, yMax + yPadding]) | |
svg.append('g') | |
.attr('transform', 'translate(120)') | |
.call(yAxis) | |
// y-label | |
svg.append('text') | |
.attr('transform', 'translate(45, 280) rotate(-90)') | |
.style('text-ancho', 'middle') | |
.text(`${QRY_LAB}`) | |
// y-axis-2 | |
svg.append('line') | |
.attr('x1', WIDTH + 0.5) | |
.attr('y1', 0) | |
.attr('x2', WIDTH + 0.5) | |
.attr('y2', HEIGHT) | |
.style('stroke', 'black') | |
// memory for main-strokes | |
let memory = -1 | |
const reset = function () { memory = -1 } | |
// main-strokes | |
svg.selectAll('.line') | |
.data(data) | |
.enter() | |
.append('line') | |
.attr('class', 'line') | |
.attr('x1', d => { | |
return xScale(d.rStart) + STROKE_WIDTH / 2 | |
}) | |
.attr('y1', d => { | |
if (memory === -1) { | |
memory = d.cEnd | |
return yScale(d.strand === '+' ? d.cStart : d.cEnd) | |
} | |
let dif = d.cEnd - d.cStart | |
let out = d.strand === '+' ? memory : memory + dif | |
memory += dif | |
return yScale(out) | |
}) | |
.call(reset) | |
.attr('x2', d => { | |
return xScale(d.rEnd) - STROKE_WIDTH / 2 | |
}) | |
.attr('y2', d => { | |
if (memory === -1) { | |
memory = d.cEnd | |
return yScale(d.strand === '+' ? d.cEnd : d.cStart) | |
} | |
let dif = d.cEnd - d.cStart | |
let out = d.strand === '+' ? memory + dif : memory | |
memory += dif | |
return yScale(out) | |
}) | |
.attr('title', d => { | |
const tipText = `<div style="text-align: left; font-family: 'Roboto Mono', monospace;"> | |
<b>Contig</b> ${' '.repeat(3)}${d.contig}<br> | |
<b>Length</b> ${' '.repeat(2)} | |
${d.cEnd > d.cStart ? (d.cEnd - d.cStart).toLocaleString() : (d.cStart - d.cEnd).toLocaleString()}<br> | |
<b>Strand</b> ${' '.repeat(3)}${d.strand}<br> | |
<b>Position</b> ${' '.repeat(1)}${d.rStart.toLocaleString()} - ${d.rEnd.toLocaleString()}<br> | |
</div>` | |
return tipText | |
}) | |
.style('stroke', d => STROKE_COLORS(d.strand)) | |
.style('stroke-width', STROKE_WIDTH) | |
.style('stroke-linecap', 'round') | |
.style('opacity', OPACITY_LOW) | |
.on('mouseover', function (element) { | |
this.style.opacity = OPACITY_HIGH | |
}) | |
.on('mouseout', function (element) { | |
this.style.opacity = OPACITY_LOW | |
}) | |
} // drawDotPlot(..) | |
let data = [ | |
{ | |
'contig': 'Ecoli_R37_1', | |
'contigLength': '2266003', | |
'rStart': 1, | |
'rEnd': 15390, | |
'cStart': 1067066, | |
'cEnd': 1082454, | |
'length': 15388, | |
'noNonIdentities': 176, | |
'strand': '+' | |
}, | |
{ | |
'contig': 'Ecoli_R37_1', | |
'contigLength': '2266003', | |
'rStart': 15391, | |
'rEnd': 20390, | |
'cStart': 786678, | |
'cEnd': 787183, | |
'length': 505, | |
'noNonIdentities': 10, | |
'strand': '-' | |
}, | |
{ | |
'contig': 'Ecoli_R37_1', | |
'contigLength': '2266003', | |
'rStart': 20390, | |
'rEnd': 30000, | |
'cStart': 262451, | |
'cEnd': 285872, | |
'length': 23421, | |
'noNonIdentities': 284, | |
'strand': '+' | |
} | |
] | |
drawDotPlot(data, 'X-Axis (reference) [nuc. pos.]', 'Y-Axis (contig)') | |
tippy('[title]', { | |
followCursor: true, | |
performance: true, | |
arrow: true, | |
size: 'large', | |
animation: 'scale', | |
}) | |
}(window, window.d3)) | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment