phylojs
Version:
A simple typescript library for phylogenetic trees
102 lines (101 loc) • 3.43 kB
JavaScript
import { SkipTreeException } from '../../utils/error';
import { readNewick } from './newick';
// Function to extract the translate mapping
export 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;
}
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 = 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 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}
*/
export function readNexus(nexus) {
const trees = _getTreesFromNexus(nexus, true);
if (trees.length === 0) {
throw new Error('No trees found in Nexus.');
}
return trees[0];
}
/**
* Reads trees from from nexus format.
* @param {string} nexus
* @returns {Tree[]}
*/
export function readTreesFromNexus(nexus) {
const trees = _getTreesFromNexus(nexus, false);
return trees;
}