phylojs
Version:
A simple typescript library for phylogenetic trees
116 lines (115 loc) • 4.36 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.PhyloXML = exports.readTreesFromPhyloXML = exports.readPhyloXML = void 0;
const __1 = require("../../");
const error_1 = require("../../utils/error");
function parse(phyloxml) {
const parser = new DOMParser();
const doc = parser.parseFromString(phyloxml, 'application/xml');
return doc.getElementsByTagName('phylogeny');
}
/**
* Read a tree from phyloXML format. If a list of trees is
* @param {string} phyloxml
* @returns {Tree}
*/
function readPhyloXML(phyloxml) {
const phylogenyElements = parse(phyloxml);
if (phylogenyElements.length === 0) {
throw new Error('No phylogeny element found in phyloXML.');
}
const phylogenyElement = phylogenyElements[0];
const phyloXML = new PhyloXML(phylogenyElement);
if (!phyloXML.root) {
throw new Error('Failed to parse the phyloXML data correctly.');
}
return new __1.Tree(phyloXML.root);
}
exports.readPhyloXML = readPhyloXML;
/**
* Reads multiple trees from phyloXML format.
* @param {string} phyloxml
* @returns {Tree[]}
*/
function readTreesFromPhyloXML(phyloxml) {
const trees = [];
const phylogenyElements = parse(phyloxml);
for (let i = 0; i < phylogenyElements.length; i++) {
const phylogenyElement = phylogenyElements[i];
try {
const phyloXML = new PhyloXML(phylogenyElement);
trees.push(new __1.Tree(phyloXML.root));
}
catch (SkipTreeException) {
console.log(`Skipping phyloXML tree ${i}: Unrooted tree.`);
}
}
return trees;
}
exports.readTreesFromPhyloXML = readTreesFromPhyloXML;
// TreeFromPhyloXML constructor
class PhyloXML {
constructor(phylogenyElement) {
this.thisNodeID = 0;
this.root = this.walkDom(undefined, phylogenyElement);
// Zero root edge length means undefined
if (this.root.branchLength === 0.0)
this.root.branchLength = undefined;
}
annotateNode(node, prefix, elements) {
for (let j = 0; j < elements.length; j++) {
const tname = elements[j].tagName;
const tval = elements[j].textContent;
node.annotation[prefix + '_' + tname] = tval;
}
}
walkDom(parent, cladeElement) {
const node = new __1.Node(this.thisNodeID++);
if (parent !== undefined)
parent.addChild(node);
for (let i = 0; i < cladeElement.children.length; i++) {
const childEl = cladeElement.children[i];
const tagName = childEl.tagName;
switch (tagName) {
case 'clade':
this.walkDom(node, childEl);
break;
case 'name':
if (childEl.textContent) {
node.label = childEl.textContent;
}
break;
case 'taxonomy':
this.annotateNode(node, 'taxonomy', childEl.children);
break;
case 'sequence':
this.annotateNode(node, 'sequence', childEl.children);
break;
case 'confidence':
node.annotation[`confidence_${childEl.getAttribute('type') || ''}`] =
childEl.textContent;
break;
case 'branch_length':
node.branchLength = Number(childEl.textContent);
break;
case 'property': {
const refAttribute = childEl.getAttribute('ref');
if (refAttribute) {
// this checks both for null and empty string
node.annotation[refAttribute] = childEl.textContent;
}
break;
}
default:
break;
}
}
const rootedAttribute = cladeElement.getAttribute('rooted');
if (rootedAttribute && rootedAttribute.toLowerCase() === 'false')
throw new error_1.SkipTreeException('Unrooted tree.');
if (cladeElement.hasAttribute('branch_length'))
node.branchLength = Number(cladeElement.getAttribute('branch_length'));
return node;
}
}
exports.PhyloXML = PhyloXML;