UNPKG

phylojs

Version:

A simple typescript library for phylogenetic trees

94 lines (93 loc) 3.58 kB
import { Tree, Node } from '../../'; import { SkipTreeException } from '../../utils/error'; const neXMLNS = 'http://www.nexml.org/2009'; function parse(nexml) { const parser = new DOMParser(); const doc = parser.parseFromString(nexml, 'application/xml'); return doc.getElementsByTagName('tree'); } /** * Reads NeXML string and returns tree. * @param {string} nexml * @returns {Tree} */ export function readNeXML(nexml) { const treeElements = parse(nexml); if (treeElements.length === 0) { throw new Error('No tree element found in NeXML.'); } const treeElement = treeElements[0]; const neXML = new NeXML(treeElement); if (!neXML.root) { throw new Error('Failed to parse the NeXML data correctly.'); } return new Tree(neXML.root); } /** * Reads NeXML strings and returns tree array. * @param {string} nexml * @returns {Tree[]} */ export function readTreesFromNeXML(nexml) { const trees = []; const treeElements = parse(nexml); for (let i = 0; i < treeElements.length; i++) { const treeElement = treeElements[i]; const neXML = new NeXML(treeElement); if (!neXML.root) { console.log('Skipping NeXML tree: Unrooted tree.'); continue; } trees.push(new Tree(neXML.root)); } return trees; } export class NeXML { constructor(treeElement) { this.thisNodeID = 0; this.processTree(treeElement); } processTree(treeElement) { const nodeElements = treeElement.getElementsByTagNameNS(neXMLNS, 'node'); const edgeElements = treeElement.getElementsByTagNameNS(neXMLNS, 'edge'); let metaElements; let metaEl; const nodesByID = {}; for (let nidx = 0; nidx < nodeElements.length; nidx++) { const nodeEl = nodeElements[nidx]; const node = new Node(this.thisNodeID++); node.label = nodeEl.getAttribute('label') || undefined; nodesByID[nodeEl.getAttribute('id') || ''] = node; if (nodeEl.getAttribute('root') === 'true') this.root = node; metaElements = nodeEl.getElementsByTagNameNS(neXMLNS, 'meta'); for (let midx = 0; midx < metaElements.length; midx++) { metaEl = metaElements[midx]; if (metaEl.hasAttribute('property') && metaEl.hasAttribute('content')) { node.annotation[metaEl.getAttribute('property') || ''] = metaEl.getAttribute('content'); } } } if (!this.root) { throw new SkipTreeException('Unrooted tree.'); } for (let eidx = 0; eidx < edgeElements.length; eidx++) { const edgeEl = edgeElements[eidx]; const parent = nodesByID[edgeEl.getAttribute('source') || '']; const child = nodesByID[edgeEl.getAttribute('target') || '']; parent.addChild(child); if (edgeEl.hasAttribute('length')) { child.branchLength = parseFloat(edgeEl.getAttribute('length') || '0'); } metaElements = edgeEl.getElementsByTagNameNS(neXMLNS, 'meta'); for (let midx = 0; midx < metaElements.length; midx++) { metaEl = metaElements[midx]; if (metaEl.hasAttribute('property') && metaEl.hasAttribute('content')) { child.annotation[metaEl.getAttribute('property') || ''] = metaEl.getAttribute('content'); } } } } }