phylotree
Version:
A JavaScript library for developing applications and interactive visualizations involving [phylogenetic trees](https://en.wikipedia.org/wiki/Phylogenetic_tree), written as an extension of the [D3](http://d3js.org) [hierarchy layout](https://github.com/d3/
552 lines (454 loc) • 16 kB
TypeScript
// Type definitions for phylotree
// Project: https://github.com/veg/phylotree.js
import { HierarchyNode, HierarchyLink } from 'd3-hierarchy';
/**
* Node data stored in the phylogenetic tree
*/
export interface PhylotreeNodeData {
name: string;
attribute?: string;
annotation?: string;
[]: any;
}
/**
* A node in the phylogenetic tree (D3 hierarchy node with phylotree data)
*/
export type PhylotreeNode = HierarchyNode<PhylotreeNodeData> & {
selected?: boolean;
tag?: boolean;
collapsed?: boolean;
hidden?: boolean;
notshown?: boolean;
screen_x?: number;
screen_y?: number;
text_angle?: number;
text_align?: string;
angle?: number;
radius?: number;
};
/**
* A link/branch in the phylogenetic tree
*/
export type PhylotreeLink = HierarchyLink<PhylotreeNodeData> & {
source: PhylotreeNode;
target: PhylotreeNode;
};
/**
* Options for rendering a phylogenetic tree
*/
export interface RenderOptions {
/** CSS selector or DOM element for the container */
container?: string | HTMLElement;
/** Width of the SVG in pixels */
width?: number;
/** Height of the SVG in pixels */
height?: number;
/** Enable zoom/pan behavior */
zoom?: boolean;
/** Tree layout: "left-to-right" or "right-to-left" */
layout?: 'left-to-right' | 'right-to-left';
/** Use radial/circular layout */
'is-radial'?: boolean;
/** Use unrooted (equal-angle) layout */
'is-unrooted'?: boolean;
/** Align tip labels */
'align-tips'?: boolean;
/** Show node labels */
'show-labels'?: boolean;
/** Show scale bar */
'show-scale'?: boolean;
/** Draw size bubbles on nodes */
'draw-size-bubbles'?: boolean;
/** Show internal node names */
'internal-names'?: boolean | ((node: PhylotreeNode) => boolean);
/** Enable node selection */
selectable?: boolean;
/** Enable collapsing of clades */
collapsible?: boolean;
/** Enable brushing for selection */
brush?: boolean;
/** Left offset in pixels */
'left-offset'?: number;
/** Spacing mode for top-bottom */
'top-bottom-spacing'?: 'fixed-step' | 'fit';
/** Spacing mode for left-right */
'left-right-spacing'?: 'fixed-step' | 'fit';
/** Font size for labels */
'font-size'?: number;
/** Maximum label width in characters */
'max-label-width'?: number;
/** Bubble styler function */
'bubble-styler'?: (node: PhylotreeNode) => number;
/** Restrict which nodes can be selected */
'restricted-selectable'?: string | ((link: PhylotreeLink) => boolean);
/** Logger instance */
logger?: Console;
}
/**
* Options for parsing tree input
*/
export interface ParseOptions {
/** Enable bootstrap value parsing */
bootstrap_values?: boolean;
/** Format type: "newick", "phyloxml", "nexus" */
type?: string | ((input: string, options: ParseOptions) => any);
/** Logger instance */
logger?: Console;
}
/**
* The TreeRender class handles visualization of the tree
*/
export class TreeRender {
/** The phylotree instance this renderer belongs to */
phylotree: Phylotree;
/** The SVG element */
svg: d3.Selection<SVGSVGElement, unknown, null, undefined>;
/** Rendering options */
options: RenderOptions;
/** Current zoom transform */
currentZoomTransform: d3.ZoomTransform | null;
/** Label width in pixels */
label_width: number;
/** Rightmost leaf x position */
right_most_leaf: number;
/** The container element */
container: string | HTMLElement;
constructor(phylotree: Phylotree, options?: RenderOptions);
/** Get the SVG element */
show(): SVGSVGElement;
/** Update the tree display */
update(): this;
/** Update layout coordinates */
update_layout(tree: PhylotreeNode): this;
/** Get/set radial mode */
radial(value?: boolean): boolean | this;
/** Get/set unrooted (equal-angle) layout mode */
unrooted(value?: boolean): boolean | this;
/** Get/set align tips mode */
alignTips(value?: boolean): boolean | this;
/** Get/set selection label */
selectionLabel(label?: string): string | this;
/** Get/set node label accessor */
nodeLabel(accessor?: (node: PhylotreeNode) => string): ((node: PhylotreeNode) => string) | this;
/** Get/set node styler function */
nodeStyler(styler?: (element: d3.Selection<any, PhylotreeNode, any, any>, node: PhylotreeNode) => void): this;
/** Get/set edge styler function */
edgeStyler(styler?: (element: d3.Selection<any, PhylotreeLink, any, any>, link: PhylotreeLink) => void): this;
/** Collapse a node */
collapse_node(node: PhylotreeNode): this;
/** Modify selection based on a callback */
modifySelection(callback: (link: PhylotreeLink, callback: (selected: boolean) => void) => boolean): this;
/** Get selected nodes */
getSelection(): PhylotreeNode[];
/** Get selected branches */
getSelectedBranches(): PhylotreeLink[];
/** Register event listener */
on(event: string, callback: (...args: any[]) => void): this;
/** Emit an event */
emit(event: string, ...args: any[]): this;
/** Selection callback setter */
selectionCallback(callback: (selection: PhylotreeNode[]) => void): this;
/**
* Get or set spacing in the x-direction (horizontal spacing between nodes)
* @param attr The new spacing value if setting
* @param skip_render Whether to skip re-rendering after setting
* @returns The current spacing_x value if getting, or the TreeRender instance if setting
*/
spacing_x(): number;
spacing_x(attr: number, skip_render?: boolean): this;
/**
* Get or set spacing in the y-direction (vertical spacing between levels)
* @param attr The new spacing value if setting
* @param skip_render Whether to skip re-rendering after setting
* @returns The current spacing_y value if getting, or the TreeRender instance if setting
*/
spacing_y(): number;
spacing_y(attr: number, skip_render?: boolean): this;
/**
* Get or set the font size for labels
* @param attr The new font size if setting
* @returns The current font size if getting, or the TreeRender instance if setting
*/
font_size(): number;
font_size(attr: number): this;
/**
* Get or set the scale bar font size
* @param attr The new font size if setting
* @returns The current scale bar font size if getting, or the TreeRender instance if setting
*/
scale_bar_font_size(): number;
scale_bar_font_size(attr: number): this;
/**
* Get or set the node circle size
* @param attr The new circle size if setting
* @param attr2 Optional second parameter
* @returns The current node circle size if getting, or the TreeRender instance if setting
*/
node_circle_size(): number;
node_circle_size(attr: number, attr2?: number): this;
/**
* Get or set CSS classes
* @param opt CSS class options
* @returns The current CSS classes if getting, or the TreeRender instance if setting
*/
css(): { [key: string]: string };
css(opt: { [key: string]: string }): this;
/**
* Get or set transitions
* @param arg Transition settings
* @returns The current transition settings if getting, or the TreeRender instance if setting
*/
transitions(): boolean | null;
transitions(arg: boolean): this;
/**
* Layout the tree
* @param transitions Whether to use transitions
* @returns The TreeRender instance
*/
layout(transitions?: boolean): this;
/** Refresh the tree display */
refresh(): this;
/** Place nodes in the layout */
placenodes(): this;
/**
* Set the separation function for node spacing
* @param callback Function that returns additional spacing between nodes
* @returns The TreeRender instance
*/
separation(callback: (node: PhylotreeNode, previous: PhylotreeNode) => number): this;
/**
* Set internal node labels based on children
* @param callback Function that determines label based on children
* @param respect_existing Whether to respect existing selections
* @returns The TreeRender instance
*/
internalLabel(callback: (children: PhylotreeNode[]) => boolean, respect_existing?: boolean): this;
/**
* Get or set the count handler callback
* @param callback Function called with selection count
* @returns The current handler if getting, or the TreeRender instance if setting
*/
countHandler(): ((count: number) => void) | null;
countHandler(callback: (count: number) => void): this;
/**
* Update the selection key name
* @param old_key The old key name
* @param new_key The new key name
* @returns The TreeRender instance
*/
updateKeyName(old_key: string, new_key: string | null): this;
/**
* Set the size of the tree display
* @param attr Size as [width, height]
* @returns The current size if getting, or the TreeRender instance if setting
*/
set_size(): [number, number];
set_size(attr: [number, number]): this;
}
/**
* Main Phylotree class for creating and manipulating phylogenetic trees
*/
export class phylotree {
/** Hierarchical tree structure (D3 hierarchy) */
nodes: PhylotreeNode;
/** Links between nodes */
links: PhylotreeLink[];
/** Parsed annotation tags */
parsed_tags: string[];
/** The tree renderer (set after calling render()) */
display: TreeRender | null;
/** Original Newick string */
newick_string: string;
/** Selection attribute name */
selection_attribute_name: string;
/**
* Create a new phylogenetic tree
* @param nwk Newick string, PhyloXML string, or hierarchical JSON
* @param options Parsing options
*/
constructor(nwk: string | object, options?: ParseOptions);
/**
* Render the tree to an SVG
* @param options Rendering options
* @returns TreeRender instance
*/
render(options?: RenderOptions): TreeRender;
/**
* Traverse the tree and compute values at each node
* @param callback Function called on each node
* @param traversal_type "pre-order", "post-order", or "in-order"
* @param root_node Optional starting node
* @param backtrack Optional function to stop traversal
*/
traverse_and_compute(
callback: (node: PhylotreeNode) => void,
traversal_type?: 'pre-order' | 'post-order' | 'in-order',
root_node?: PhylotreeNode,
backtrack?: (node: PhylotreeNode) => boolean
): void;
/** Export tree to JSON string */
json(traversal_type?: string): string;
/** Get parsed annotation tags */
get_parsed_tags(): string[];
/** Update tree from JSON */
update(json: object): this;
/** Check if tree has branch lengths */
hasBranchLengths(): boolean;
/** Get all branch lengths */
getBranchLengths(): number[];
/** Set branch length accessor */
setBranchLength(accessor: (node: PhylotreeNode) => number): this;
/** Normalize branch lengths */
normalizeBranchLengths(attr?: string): this;
/** Scale branch lengths by a factor */
scaleBranchLengths(scale: number): this;
/** Get branch name accessor */
branchName(accessor?: (node: PhylotreeNode) => string): string | this;
/** Get Newick representation of tree */
getNewick(annotator?: (node: PhylotreeNode) => string): string;
/** Get tagged Newick representation */
getTaggedNewick(): string;
/** Find most recent common ancestor of nodes */
mrca(nodes: (PhylotreeNode | string)[]): PhylotreeNode;
/** Check if a node is a leaf */
isLeafNode(node: PhylotreeNode): boolean;
/** Get all tip/leaf nodes */
getTips(): PhylotreeNode[];
/** Get all internal nodes */
getInternals(): PhylotreeNode[];
/** Get root node */
getRootNode(): PhylotreeNode;
/** Get all nodes */
getNodes(): PhylotreeNode[];
/** Get node by name */
getNodeByName(name: string): PhylotreeNode | undefined;
/** Select all descendants of a node */
selectAllDescendants(node: PhylotreeNode, terminal?: boolean, internal?: boolean): PhylotreeNode[];
/** Reroot the tree at a node */
reroot(node: PhylotreeNode, fraction?: number): this;
/** Get path from a node to the root */
pathToRoot(node: PhylotreeNode): PhylotreeNode[];
/** Resort children of nodes */
resortChildren(comparator: (a: PhylotreeNode, b: PhylotreeNode) => number): this;
/** Graft a new node */
graftANode(graftAt: PhylotreeNode, newChild: PhylotreeNode, newParent?: PhylotreeNode, lengths?: { new_child: number; new_parent: number }): void;
/** Add a child node */
addChild(parent: PhylotreeNode, child: PhylotreeNode): void;
/** Delete a node */
deleteANode(index: number): void;
/** Assign attributes to nodes */
assignAttributes(attributes: object): this;
/** Get tip lengths for date extraction */
getTipLengths(): { [name: string]: number };
/** Maximum parsimony ancestral state reconstruction */
maxParsimony(attr: string, states: string[]): this;
/** Clear internal node selections */
clearInternalNodes(respect?: boolean): this;
}
// Utility functions
/**
* Parse a Newick format string
*/
export function newickParser(newick: string, options?: ParseOptions): { json: object; error: string | null };
/**
* Get Newick string from tree
*/
export function getNewick(tree: PhylotreeNode, annotator?: (node: PhylotreeNode) => string): string;
/**
* Compute pairwise distances between nodes
*/
export function pairwise_distances(tree: phylotree): number[][];
/**
* Compute pairwise distances between nodes (camelCase alias)
*/
export function pairwiseDistances(tree: phylotree): number[][];
/**
* Compute Sackin's index (tree balance measure)
*/
export function sackin(tree: phylotree): number;
/**
* Find center of tree
*/
export function centerOfTree(tree: phylotree): PhylotreeNode;
/**
* Compute midpoint for rooting
*/
export function computeMidpoint(tree: phylotree): { node: PhylotreeNode; fraction: number };
/**
* Root to tip regression
*/
export function rootToTip(tree: phylotree, attr?: string): {
slope: number;
intercept: number;
r2: number;
data: Array<{ date: number; distance: number; name: string }>;
};
/**
* Fit root to tip (find optimal root)
*/
export function fitRootToTip(tree: phylotree, attr?: string): {
node: PhylotreeNode;
fraction: number;
r2: number;
};
/**
* Extract dates from tip names
*/
export function extract_dates(tree: phylotree, pattern?: RegExp): { [name: string]: number };
/**
* Extract dates from tip names (camelCase alias)
*/
export function extractDates(tree: phylotree, pattern?: RegExp): { [name: string]: number };
/**
* Cluster Picker algorithm
*/
export function clusterPicker(tree: phylotree, options?: {
bootstrap_threshold?: number;
distance_threshold?: number;
}): PhylotreeNode[][];
/**
* PhyloPart clustering algorithm
*/
export function phylopart(tree: phylotree, options?: {
bootstrap_threshold?: number;
}): PhylotreeNode[][];
/**
* Parse FASTA format
*/
export function parseFasta(fasta: string): Array<{ name: string; seq: string }>;
/**
* Neighbor joining tree construction
*/
export function neighborJoining(matrix: number[][], labels: string[]): object;
/**
* Get distance matrix from sequences
*/
export function getDistanceMatrix(sequences: Array<{ name: string; seq: string }>): {
matrix: number[][];
labels: string[];
};
/**
* Load annotations from NEXUS file
*/
export function loadAnnotations(nexus: string): object;
/**
* Parse annotations from NEXUS
*/
export function parseAnnotations(nexus: string): object;
// Tree traversal functions
/**
* In-order traversal
*/
export function inOrder(node: PhylotreeNode, callback: (node: PhylotreeNode) => void, backtrack?: (node: PhylotreeNode) => boolean): void;
/**
* Pre-order traversal
*/
export function preOrder(node: PhylotreeNode, callback: (node: PhylotreeNode) => void, backtrack?: (node: PhylotreeNode) => boolean): void;
/**
* Post-order traversal
*/
export function postOrder(node: PhylotreeNode, callback: (node: PhylotreeNode) => void, backtrack?: (node: PhylotreeNode) => boolean): void;
/**
* Left-child right-sibling traversal
*/
export function leftChildRightSibling(node: PhylotreeNode, callback: (node: PhylotreeNode) => void): void;