UNPKG

prot-annot

Version:

A package to generate SVG images of protein annotation.

299 lines (298 loc) 11.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var d3_1 = require("d3"); var kDefaults = { common: { height: 30, scale: 0.5, }, pfam: { fill: 'white', fontFamily: 'Verdana', fontSize: 15, height: 25, lengthToPixelFactor: 10, namePadding: 10, opacity: 0.9, overlap: { tolerance: 10, }, partialPixel: 3, stroke: 'black', strokeWidth: 2, }, sequence: { color: 'gray', height: 5, opacity: 1, }, tm: { color: 'rgba(0,126,204,0.7)', height: 30, opacity: 0.7, }, }; var ProtAnnot = /** @class */ (function () { function ProtAnnot(aseqInfo, config) { if (config === void 0) { config = {}; } this.supportedFeatures = ['pfam31', 'tmhmm2']; this.aseqInfo = aseqInfo; this.config = config === {} ? { pfam31: true, tmhmm2: true } : config; this.svg = ''; this.scale = kDefaults.common.scale; } /** * Insert SVG onto HTMLElement * * @param {HTMLElement} htmlElement * @param {number} [scale=kDefaults.common.scale] * @memberof ProtAnnot */ ProtAnnot.prototype.draw = function (htmlElement, scale) { if (scale === void 0) { scale = kDefaults.common.scale; } this.scale = scale; this.svg = d3_1.select(htmlElement).append('svg'); if (this.aseqInfo.length) { this.svg.attr('height', kDefaults.common.height).attr('width', this.aseqInfo.length * this.scale); this.drawSequence() .drawTm() .drawDomain(); } }; /* /** * Return a string with SVG code for the figure * * @param {number} [scale=kDefaults.common.scale] * @returns * @memberof ProtAnnot public writeSvg(scale: number = kDefaults.common.scale) { const { window } = new JSDOM(`<!DOCTYPE html><body><div id="prot-annot"/></body>`); const protAnnotDiv = window.document.getElementById('prot-annot'); if (protAnnotDiv) { this.draw(protAnnotDiv); return protAnnotDiv.innerHTML; } throw new Error('Something did not work.'); } */ ProtAnnot.prototype.drawSequence = function () { if (this.aseqInfo.length) { this.svg .append('g') .attr('class', 'prot-annot-seq') .append('rect') .attr('id', function (d, i) { return "prot-annot-seq-" + i; }) .attr('x', 1) .attr('y', kDefaults.common.height / 2 - kDefaults.sequence.height / 2) .attr('width', this.aseqInfo.length * this.scale) .attr('height', kDefaults.sequence.height) .attr('fill', kDefaults.sequence.color) .attr('opacity', kDefaults.sequence.opacity); } return this; }; ProtAnnot.prototype.drawTm = function () { var _this = this; if (this.aseqInfo.tmhmm2) { if (this.aseqInfo.tmhmm2.tms) { this.svg .append('g') .attr('class', 'prot-annot-tm') .selectAll('rect') .data(this.aseqInfo.tmhmm2.tms) .enter() .append('rect') .attr('id', function (d, i) { return "prot-annot-tm-" + i; }) .attr('x', function (d) { return d[0] * _this.scale; }) .attr('y', kDefaults.common.height / 2 - kDefaults.tm.height / 2) .attr('width', function (d) { return (d[1] - d[0]) * _this.scale; }) .attr('height', kDefaults.tm.height) .attr('fill', kDefaults.tm.color) .attr('opacity', kDefaults.tm.opacity); } } return this; }; ProtAnnot.prototype.drawDomain = function () { if (this.aseqInfo.pfam31.length) { this.drawDomainBody(); } return this; }; ProtAnnot.prototype.drawDomainBody = function () { var _this = this; var self = this; var processedPfam = this.removeOverlapps(); var domain = this.svg .append('g') .attr('class', 'prot-annot-pfam') .selectAll('g') .data(processedPfam && processedPfam.length > 0 ? processedPfam : []) .enter() .append('g') .attr('id', function (d, i) { return "prot-annot-pfam-domain-" + i; }); domain .append('rect') .attr('id', function (d, i) { return "prot-annot-pfam-body-" + i; }) .attr('class', "prot-annot-pfam-body") .attr('x', function (d) { return d.ali_from * _this.scale; }) .attr('y', kDefaults.common.height / 2 - kDefaults.pfam.height / 2) .attr('width', function (d) { return (d.ali_to - d.ali_from) * _this.scale; }) .attr('height', kDefaults.pfam.height) .attr('fill', kDefaults.pfam.fill) .attr('opacity', kDefaults.pfam.opacity) .attr('stroke', kDefaults.pfam.stroke) .attr('title', function (d) { return d.name; }) .attr('stroke-width', 0); domain .append('path') .attr('d', function (d) { var domainBorderResult = self.domainBorder(d); var leftAndRightPartialPixels = domainBorderResult[1]; return domainBorderResult[0]; }) .attr('stroke', kDefaults.pfam.stroke) .attr('stroke-width', kDefaults.pfam.strokeWidth) .attr('fill', 'none') .attr('stroke-linecap', 'round') .attr('title', function (d) { return d.name; }) .attr('id', function (d, i) { return "prot-annot-pfam-border-" + i; }) .attr('id', "prot-annot-pfam-borders"); // private nameDomain(domain: any, kDefaults.common.height / 2, featureScale, getUniqueFeatureName, leftAndRightPartialPixels) { domain .append('text') .attr('x', function (d) { return ((d.ali_from + d.ali_to) / 2) * _this.scale; }) .attr('y', kDefaults.common.height / 2) .attr('text-anchor', 'middle') .attr('alignment-baseline', 'central') .attr('textLength', function (d) { var scaledDomainLength = (d.ali_to - d.ali_from) * _this.scale; return (Math.min(d.name.length * kDefaults.pfam.lengthToPixelFactor, scaledDomainLength) - kDefaults.pfam.namePadding); }) .attr('font-family', kDefaults.pfam.fontFamily) .attr('font-size', kDefaults.pfam.fontSize) .attr('lengthAdjust', 'spacingAndGlyphs') .text(function (d) { return d.name; }) .attr('title', function (d) { return d.name; }) .attr('class', 'prot-annot-pfam-names') .attr('id', function (d, i) { return "prot-annot-pfam-name-" + i; }); return this; }; ProtAnnot.prototype.domainBorder = function (pfam) { var partialPixelLeft = pfam.hmm_cov[0] === '[' ? 0 : kDefaults.pfam.partialPixel * this.scale; var partialPixelRight = pfam.hmm_cov[1] === ']' ? 0 : kDefaults.pfam.partialPixel * this.scale; var kQuarterDomainHeight = kDefaults.pfam.height / 4; var aliFrom = pfam.ali_from * this.scale; var aliTo = pfam.ali_to * this.scale; var y0 = kDefaults.common.height / 2 - kDefaults.pfam.height / 2; var y1 = kDefaults.common.height / 2 + kDefaults.pfam.height / 2; var pathList = [ { x: aliFrom, y: y0 }, { x: aliTo, y: y0 }, { x: aliTo - partialPixelRight, y: y0 + kQuarterDomainHeight }, { x: aliTo, y: y0 + kQuarterDomainHeight * 2 }, { x: aliTo - partialPixelRight, y: y0 + kQuarterDomainHeight * 3 }, { x: aliTo, y: y0 + kDefaults.pfam.height }, { x: aliFrom, y: y1 }, { x: aliFrom + partialPixelLeft, y: y1 - kQuarterDomainHeight }, { x: aliFrom, y: y1 - kQuarterDomainHeight * 2 }, { x: aliFrom + partialPixelLeft, y: y1 - kQuarterDomainHeight * 3 }, { x: aliFrom, y: y1 - kDefaults.pfam.height }, ]; var path = ' M ' + pathList[0].x + ' ' + pathList[0].y + ' L ' + pathList[1].x + ' ' + pathList[1].y + ' L ' + pathList[2].x + ' ' + pathList[2].y + ' L ' + pathList[3].x + ' ' + pathList[3].y + ' L ' + pathList[4].x + ' ' + pathList[4].y + ' L ' + pathList[5].x + ' ' + pathList[5].y + ' L ' + pathList[6].x + ' ' + pathList[6].y + ' L ' + pathList[7].x + ' ' + pathList[7].y + ' L ' + pathList[8].x + ' ' + pathList[8].y + ' L ' + pathList[9].x + ' ' + pathList[9].y + ' L ' + pathList[10].x + ' ' + pathList[10].y + ' Z'; return [path, [partialPixelRight, partialPixelLeft]]; }; ProtAnnot.prototype.removeOverlapps = function () { var _this = this; var pfam31Sorted = Array.from(this.aseqInfo.pfam31).sort(function (p1, p2) { return p1.ali_from - p2.ali_from; }); // overlap threshold is 10 aa var pfam31Final = new Set(); var pfam1 = pfam31Sorted[0]; var significantPfam = pfam1; var overlapLength = 0; var lastAdded = pfam1; pfam31Final.add(pfam1); pfam31Sorted.slice(1).forEach(function (pfam2) { if (pfam1.ali_to > pfam2.ali_from) { overlapLength = pfam1.ali_to - pfam2.ali_from; if (overlapLength > kDefaults.pfam.overlap.tolerance) { significantPfam = _this.compareEvalues(pfam1, pfam2); // if the previously added is pfam1 and it's less significant than pfam 2 // then remove this previously added if (lastAdded === pfam1 && lastAdded !== significantPfam) { pfam31Final.delete(lastAdded); } pfam31Final.add(significantPfam); lastAdded = significantPfam; } else { pfam31Final.add(pfam2); lastAdded = pfam2; significantPfam = pfam2; } pfam1 = significantPfam; } else { pfam31Final.add(pfam2); lastAdded = pfam2; significantPfam = pfam2; pfam1 = significantPfam; } }); return Array.from(pfam31Final); }; ProtAnnot.prototype.compareEvalues = function (pfam1, pfam2) { var eval1 = pfam1.i_evalue; var eval2 = pfam2.i_evalue; if (eval1 !== eval2) { return eval1 > eval2 ? pfam2 : pfam1; } return pfam1.ali_to - pfam1.ali_from >= pfam2.ali_to - pfam2.ali_from ? pfam1 : pfam2; }; return ProtAnnot; }()); exports.ProtAnnot = ProtAnnot; //# sourceMappingURL=ProtAnnot.js.map