prot-annot
Version:
A package to generate SVG images of protein annotation.
299 lines (298 loc) • 11.6 kB
JavaScript
;
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