phylojs
Version:
A simple typescript library for phylogenetic trees
108 lines (107 loc) • 3.72 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.readTreesFromNexus = exports.readNexus = exports.getTranslateFromNexus = void 0;
const error_1 = require("../../utils/error");
const newick_1 = require("./newick");
// Function to extract the translate mapping
function getTranslateFromNexus(nexus) {
const tmap = {};
const lines = nexus.split('\n');
let fullLine = '';
for (let i = 1; i < lines.length; i++) {
fullLine += lines[i].trim();
// Remove comments
fullLine = fullLine.replace(/\[[^&][^\]]*\]/g, '').trim();
// Parse translate line
if (fullLine.toLowerCase().startsWith('translate')) {
const tStringArray = fullLine.slice(9, fullLine.length - 1).split(',');
for (let j = 0; j < tStringArray.length; j++) {
const tvec = tStringArray[j].trim().split(/\s+/);
const tkey = tvec[0];
let tval = tvec.slice(1).join(' ');
tval = tval.replace(/^"(.*)"$/, '$1').replace(/^'(.*)'$/, '$1');
tmap[tkey] = tval;
}
break;
}
}
return tmap;
}
exports.getTranslateFromNexus = getTranslateFromNexus;
function translateTree(tree, tmap) {
// traverse nodes
tree.root.applyPreOrder(node => {
// If node's identifier exists in tmap, replace it
if (node.label && tmap[node.label]) {
node.label = tmap[node.label];
}
});
return tree;
}
function _getTreesFromNexus(nexus, singleTree) {
const trees = [];
const lines = nexus.split('\n');
let inTrees = false;
let fullLine = '';
const tmap = getTranslateFromNexus(nexus); // Retrieve the translation mapping
for (let i = 1; i < lines.length; i++) {
fullLine += lines[i].trim();
if (fullLine[fullLine.length - 1] !== ';')
continue;
// Remove comments
fullLine = fullLine.replace(/\[[^&][^\]]*\]/g, '').trim();
if (!inTrees) {
if (fullLine.toLowerCase() === 'begin trees;')
inTrees = true;
fullLine = '';
continue;
}
if (fullLine.toLowerCase() === 'end;')
break;
// Parse tree line
const matches = /tree (\w|\.)+ *(\[&[^\]]*] *)* *= *(\[&[^\]]*] *)* */.exec(fullLine.toLowerCase());
if (matches !== null) {
const eqIdx = matches[0].length;
try {
let tree = (0, newick_1.readNewick)(fullLine.slice(eqIdx));
tree = translateTree(tree, tmap); // Use the translation map here
trees.push(tree);
if (singleTree)
return trees; // If only one tree is requested, return immediately
}
catch (e) {
if (e instanceof error_1.SkipTreeException) {
console.log('Skipping Nexus tree: ' + e.message);
}
else {
throw e;
}
}
}
fullLine = '';
}
return trees;
}
/**
* Reads a tree from nexus format. Currently does not support custom annotation parsing.
* @param {string} nexus
* @returns {Tree}
*/
function readNexus(nexus) {
const trees = _getTreesFromNexus(nexus, true);
if (trees.length === 0) {
throw new Error('No trees found in Nexus.');
}
return trees[0];
}
exports.readNexus = readNexus;
/**
* Reads trees from from nexus format.
* @param {string} nexus
* @returns {Tree[]}
*/
function readTreesFromNexus(nexus) {
const trees = _getTreesFromNexus(nexus, false);
return trees;
}
exports.readTreesFromNexus = readTreesFromNexus;