UNPKG

cyto-chromosome-vis

Version:

Interactive visual representation/ web visualization tool for chromosome ideograms

396 lines (313 loc) 11.5 kB
(function(cyto_chr, d3) { 'use strict' cyto_chr.margin = { top: 38, left: 14, right: 5 }; var CHR1_BP_END = 248956422; var CHR1_BP_MID = 121700000; var Chromosome = function() { this._segment = '1'; this._selectionMode = 'single'; this._domTarget = d3.select(document.documentElement); this._resolution = "550"; this._width = null; this._height = 17; this._useRelative = true; this._showAxis = false; this.dispatch = d3.dispatch('bandclick', 'selectorchange', 'selectorend', 'selectordelete'); this.rendered = false; this.selectors = []; this.model = []; this.maxBasePair = 0; this.xscale = d3.scale.linear(); }; Chromosome.prototype.getMaxBasepair = function() { return this.maxBasePair; }; Chromosome.prototype.segment = function (a) { if (typeof a === 'number') a = a.toString(); return cyto_chr.InitGetterSetter.call(this, '_segment', a); }; Chromosome.prototype.selectionMode = function (a) { return cyto_chr.InitGetterSetter.call(this, '_selectionMode', a); }; Chromosome.prototype.target = function (a) { if(typeof a === 'string') a = d3.select(a); if(a.empty()) { throw "Error: Invalid dom target"; } return cyto_chr.InitGetterSetter.call(this, '_domTarget', a); }; Chromosome.prototype.resolution = function (a) { if (typeof a === 'number') a = a.toString(); if (a === "400" || a === "550" || a ==="850" || a === "1200" || a === undefined) { return cyto_chr.InitGetterSetter.call(this, '_resolution', a); } else { throw "Error: Invalid resolution. Please enter 400, 550, 850, or 1200 only."; } }; Chromosome.prototype.width = function (a) { return cyto_chr.InitGetterSetter.call(this, '_width', a); }; Chromosome.prototype.height = function (a) { return cyto_chr.InitGetterSetter.call(this, '_height', a); }; Chromosome.prototype.useRelative = function (a) { return cyto_chr.InitGetterSetter.call(this, '_useRelative', a); }; Chromosome.prototype.showAxis = function (a) { return cyto_chr.InitGetterSetter.call(this, '_showAxis', a); }; Chromosome.prototype.on = function(e, listener) { if (!this.dispatch.hasOwnProperty(e)) throw "Error: No event for " + e; this.dispatch.on(e, listener); }; Chromosome.prototype.config = function(type, arg) { return this[type](arg); }; Chromosome.prototype.renderAxis = function () { var bpAxis = d3.svg.axis() .scale(this.xscale) .tickFormat(d3.format('s')) .orient("bottom"); if (this._useRelative && (this._segment === "Y" || this._segment === "22" || this._segment === "21" || this._segment === "20" || this._segment === "19")) { bpAxis.ticks(6); } var axisg = this.svgTarget.append('g') .classed('bp-axis', true) .attr('transform', 'translate('+ cyto_chr.margin.left + ',' + (this._height + cyto_chr.margin.top + 6) + ")"); axisg.call(bpAxis); axisg.selectAll('text') .style('font', '10px sans-serif'); axisg.selectAll('path, line') .style({ "fill": "none", "stroke": "#666666", "shape-rendering": "crispEdges" }); }; Chromosome.prototype.remove = function() { this.svgTarget.remove(); }; Chromosome.prototype.moveSelectorTo = function(start, stop) { if(arguments.length !== 2) { throw "Error moveSelectorTo: Invalid number of arguments. Both start and stop coordinates are required"; } if (this.selectors.length === 0) { this.newSelector(0, "10000000"); } else { this.selectors[0].move(start, stop); } }; Chromosome.prototype.newSelector = function(bp_start, bp_stop) { var self = this; function selectorRemoveCB(sel) { self.dispatch.selectordelete(sel); var index = self.selectors.indexOf(sel); self.selectors.splice(index, 1); } var ve = cyto_chr.selector(selectorRemoveCB) .x(cyto_chr.margin.left) .y(cyto_chr.margin.top - (this._height / 4)) .height(this._height + (this._height / 2)) .xscale(this.xscale) .extent([bp_start, bp_stop]) .target(this.svgTarget) .render(); ve.dispatch.on('change', function(d) { self.dispatch.selectorchange(d); }); ve.dispatch.on('changeend', function(d) { self.dispatch.selectorend(d); }); this.selectors.push(ve); }; Chromosome.prototype.getSelections = function() { var ret = []; for(var i = 0; i < this.selectors.length; i++) { var sel = this.selectors[i].extent(); ret.push({ start: sel[0], stop: sel[1] }) } return ret; }; Chromosome.prototype.getSelectedBands = function(sensitivity) { var results = []; if (this.selectors.length > 0) { var se = this.selectors[0].extent(); var selStart = +se[0]; var selStop = +se[1]; if (typeof sensitivity !== 'undefined') { selStart -= sensitivity; selStop += sensitivity; } results = this.model.slice().filter(function(e) { var bStart = +e.bp_start; var bStop = +e.bp_stop; if ((selStart >= bStart && selStart < bStop) || (selStop > bStart && selStop <= bStop) || (selStart <= bStart && selStop >= bStop)) { return true; } else { return false; } }); } return results; }; Chromosome.prototype.getSVGTarget = function() { return this.svgTarget; }; Chromosome.prototype.render = function (zoomRange) { var self = this; if(self.rendered) { self.selectors = []; self.remove(); } if(self._width === null) { var parentWidth = d3.select(self._domTarget[0][0].parentNode).node().getBoundingClientRect().width; self.width(parentWidth) } cyto_chr.modelLoader.load(this._segment, this._resolution, function(data) { self.model = data; self.maxBasePair = d3.max(data, function(d) { return +d.bp_stop; }); self.segMid = 0; for(var j =0; j < data.length;j++) { if(data[j].stain ==="acen") { self.segMid = data[j].bp_stop; break; } } var rangeTo = self._useRelative ? (self.maxBasePair / CHR1_BP_END) * self._width : self._width; rangeTo -= (cyto_chr.margin.left + cyto_chr.margin.right); if (zoomRange) { self.xscale.domain([zoomRange[0], zoomRange[1]]); } else { self.xscale.domain([1, self.maxBasePair]); } self.xscale.range([0, rangeTo]); var h = self._height + 62; var w = self._width; self.svgTarget = self._domTarget .style('height', h + 'px') .style('width', (w+5) + 'px') .append('svg') .attr('width', w) .attr('height', h); var bands = self.svgTarget.selectAll('g') .data(data).enter(); self.svgTarget.append('text') .text(self._segment) .attr('x', 5) .attr('y', cyto_chr.margin.top + (self._height/ 2) + 2) .attr('text-anchor','middle') .style('font', '10px sans-serif'); function bpCoord(bp) { var xshift = 0; if(self.alignCentromere && self._segment !== "1") { xshift = self.xscale(CHR1_BP_MID) - self.xscale(self.segMid); } return self.xscale(bp) + cyto_chr.margin.left + xshift; } bands.append('g') .each(function(d, i) { var elem = d3.select(this); function applyBorder() { this .attr('stroke', '#000000') .attr('stroke-width', 0.2); } function drawRoundedRect(d, r, tl, tr, bl, br) { return this.append('path') .attr("d", cyto_chr.roundedRect(bpCoord(d.bp_start), cyto_chr.margin.top, bpCoord(d.bp_stop) - bpCoord(d.bp_start), self._height, r, tl, tr, bl, br)) .style('fill', cyto_chr.getStainColour(d.stain, d.density)); } var labelSkipFactor = self._resolution === '1200' ? 8 : 2; if (self._useRelative && self._resolution == "1200" && self._segment == 'Y') { labelSkipFactor = 12; } if(i % labelSkipFactor === 0) { var bmid = (bpCoord(d.bp_stop) + bpCoord(d.bp_start)) / 2; elem.append('line') .attr('x1', bmid) .attr('y1', cyto_chr.margin.top) .attr('x2', bmid) .attr('y2', cyto_chr.margin.top - 4) .style('stroke', 'grey') .style('stroke-width',1); elem.append('text') .attr('transform', 'translate(' + bmid + ',' + (cyto_chr.margin.top - 6) + ')rotate(-50)') .style('font', '10px sans-serif') .text(d.arm + d.band); } var rect; var w = bpCoord(d.bp_stop) - bpCoord(d.bp_start); var acenThreshold = (self._resolution == "1200") ? 7 : 6; if (i === 0 && w > 10) { rect = drawRoundedRect.call(elem, d, 4, true, false, true, false); applyBorder.call(rect); } else if (d.stain === "acen" && (w > acenThreshold)) { if (d.arm === "p") { rect = drawRoundedRect.call(elem, d, 5, false, true, false, true); } else if(d.arm === "q") { rect = drawRoundedRect.call(elem, d, 5, true, false, true, false); } } else if (i === data.length - 1) { rect = drawRoundedRect.call(elem, d, 5, false, true, false, true); applyBorder.call(rect); } else { var ys = d.stain === "stalk" ? cyto_chr.margin.top + (self._height / 4) : cyto_chr.margin.top; var hs = d.stain === "stalk" ? self._height / 2 : self._height; rect = elem.append('rect') .attr('x', bpCoord(d.bp_start)) .attr('y', ys) .attr('height', hs) .attr('width', self.xscale(d.bp_stop) - self.xscale(d.bp_start)) .style('fill', cyto_chr.getStainColour(d.stain, d.density)); applyBorder.call(rect); } rect.append('title') .text(d.arm + d.band) rect.on('mouseover', function(d) { var e = d3.select(this) .style('opacity', "0.5") .style('cursor', 'pointer'); if (d.stain === "gneg") { e.style('fill', cyto_chr.getStainColour("gpos", "25")); } }); rect.on('mouseout', function(d) { var e = d3.select(this) .style('opacity', "1") .style('cursor', 'default'); if (d.stain === "gneg") { e.style('fill', cyto_chr.getStainColour("gneg")); } }); rect.on('click', function(d) { if (self.selectors.length === 0 || (self._selectionMode === 'multi' && d3.event.altKey)) { self.newSelector(d.bp_start, d.bp_stop); } if (self._selectionMode === 'single' && self.selectors.length > 0) { self.moveSelectorTo(d.bp_start, d.bp_stop); } self.dispatch.bandclick(d); }); }); if (self._showAxis) { self.renderAxis(); } self.rendered = true; }); return self; }; cyto_chr.chromosome = function() { return new Chromosome(); };1 })(cyto_chr || {}, d3);