archaeopteryx 2.3.2 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +160 -499
- package/README.md +1109 -98
- package/archaeopteryx.d.ts +141 -0
- package/archaeopteryx.js +6587 -6145
- package/forester.js +2998 -536
- package/package.json +42 -12
package/README.md
CHANGED
|
@@ -2,9 +2,6 @@
|
|
|
2
2
|
Archaeopteryx.js is a software tool for the visualization and analysis of highly annotated phylogenetic trees.
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
### Website
|
|
6
|
-
https://sites.google.com/view/archaeopteryxjs
|
|
7
|
-
|
|
8
5
|
### npm
|
|
9
6
|
https://www.npmjs.com/package/archaeopteryx
|
|
10
7
|
|
|
@@ -12,130 +9,264 @@ https://www.npmjs.com/package/archaeopteryx
|
|
|
12
9
|
https://github.com/cmzmasek/archaeopteryx-js
|
|
13
10
|
|
|
14
11
|
|
|
15
|
-
###
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
* [
|
|
23
|
-
*
|
|
24
|
-
|
|
25
|
-
*
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
*
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
*
|
|
32
|
-
|
|
33
|
-
*
|
|
34
|
-
|
|
35
|
-
*
|
|
36
|
-
|
|
12
|
+
### Live demos
|
|
13
|
+
|
|
14
|
+
Self-contained demos, served from this repository (no external dependencies) —
|
|
15
|
+
they run entirely in your browser:
|
|
16
|
+
|
|
17
|
+
**https://cmzmasek.github.io/archaeopteryx-js/**
|
|
18
|
+
|
|
19
|
+
* [Auspice / Nextstrain JSON](https://cmzmasek.github.io/archaeopteryx-js/demo.html?tree=auspice)
|
|
20
|
+
* [Swine H1 HA1 + alignment (Nexus)](https://cmzmasek.github.io/archaeopteryx-js/demo.html?tree=swh1)
|
|
21
|
+
* [BEAST annotations (Nexus)](https://cmzmasek.github.io/archaeopteryx-js/demo.html?tree=beast)
|
|
22
|
+
* [SARS-CoV-2 time tree (calendar)](https://cmzmasek.github.io/archaeopteryx-js/demo.html?tree=sarscov2)
|
|
23
|
+
* [Herpesviridae DNA polymerase (201 tips)](https://cmzmasek.github.io/archaeopteryx-js/demo.html?tree=herpes_dnapol)
|
|
24
|
+
* [Caliciviridae (186 strains)](https://cmzmasek.github.io/archaeopteryx-js/demo.html?tree=caliciviridae_500)
|
|
25
|
+
* [Adenoviridae (321 strains)](https://cmzmasek.github.io/archaeopteryx-js/demo.html?tree=adenoviridae)
|
|
26
|
+
* [Nucleotide alignment (600 columns)](https://cmzmasek.github.io/archaeopteryx-js/demo.html?tree=alignment_nt)
|
|
27
|
+
* [Sequence alignment](https://cmzmasek.github.io/archaeopteryx-js/demo.html?tree=alignment)
|
|
28
|
+
* [Influenza HA (annotated)](https://cmzmasek.github.io/archaeopteryx-js/demo.html?tree=influenza)
|
|
29
|
+
* [Dinosaur time tree](https://cmzmasek.github.io/archaeopteryx-js/demo.html?tree=dinosaur)
|
|
30
|
+
* [Ammonite time tree (fossil ranges)](https://cmzmasek.github.io/archaeopteryx-js/demo.html?tree=ammonite)
|
|
31
|
+
* [Apaf-1 gene family](https://cmzmasek.github.io/archaeopteryx-js/demo.html?tree=apaf)
|
|
32
|
+
* [Bcl-2 family](https://cmzmasek.github.io/archaeopteryx-js/demo.html?tree=bcl2)
|
|
33
|
+
* [Confidence values](https://cmzmasek.github.io/archaeopteryx-js/demo.html?tree=confidences)
|
|
34
|
+
* [Branch events](https://cmzmasek.github.io/archaeopteryx-js/demo.html?tree=branch_events)
|
|
35
|
+
* [Influenza A H5Nx (354 strains)](https://cmzmasek.github.io/archaeopteryx-js/demo.html?tree=flu_h5)
|
|
36
|
+
* [Start circular](https://cmzmasek.github.io/archaeopteryx-js/demo.html?tree=circular)
|
|
37
|
+
* [Woese tree of life (start unrooted)](https://cmzmasek.github.io/archaeopteryx-js/demo.html?tree=woese)
|
|
38
|
+
* [Start with collapsed controls](https://cmzmasek.github.io/archaeopteryx-js/demo.html?tree=collapsed)
|
|
37
39
|
|
|
38
40
|
|
|
39
41
|
### Detailed developer documentation
|
|
40
42
|
https://docs.google.com/document/d/1COVe0iYbKtcBQxGTP4_zuimpk2FH9iusOVOgd5xCJ3A/edit
|
|
41
43
|
|
|
42
|
-
### Version History
|
|
43
|
-
https://github.com/cmzmasek/archaeopteryx-js/wiki/Archaeopteryx.js-Version-History
|
|
44
|
-
|
|
45
44
|
### Dependencies
|
|
46
45
|
Archaeopteryx.js has the following dependencies:
|
|
47
46
|
* forester.js: https://www.npmjs.com/package/archaeopteryx
|
|
48
47
|
* phyloxml.js: https://www.npmjs.com/package/phyloxml
|
|
49
|
-
* d3.js (version
|
|
50
|
-
*
|
|
51
|
-
* jQuery UI (1.12.1): https://www.npmjs.com/package/jquery-ui/v/1.12.1
|
|
52
|
-
* sax.js (1.2.4): https://www.npmjs.com/package/sax/v/1.2.4
|
|
48
|
+
* d3.js (version 7): https://www.npmjs.com/package/d3
|
|
49
|
+
* sax.js (1.6.1): https://www.npmjs.com/package/sax/v/1.6.1
|
|
53
50
|
|
|
54
|
-
For
|
|
55
|
-
|
|
56
|
-
* canvg: https://www.npmjs.com/package/canvg
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
51
|
+
For **raster PNG export** (optional — the PNG entry appears in the Download
|
|
52
|
+
menu only when `window.Canvg` is present):
|
|
53
|
+
* canvg (4.x): https://www.npmjs.com/package/canvg — publishes ES modules
|
|
54
|
+
only (no classic-script global build), so bridge it yourself:
|
|
55
|
+
```html
|
|
56
|
+
<script type="module">
|
|
57
|
+
import {Canvg} from 'http://path/to/canvg.js'; // a self-contained build; see below
|
|
58
|
+
window.Canvg = Canvg;
|
|
59
|
+
</script>
|
|
60
|
+
```
|
|
61
|
+
A module script is deferred regardless of where it sits in the page, and
|
|
62
|
+
PNG export only runs later from a Download click, so this can go anywhere
|
|
63
|
+
before the click — it does not need to precede archaeopteryx.js's own
|
|
64
|
+
`<script>` tag. `docs/lib/canvg.js` in this repository is one such
|
|
65
|
+
self-contained build (npm's `canvg@4.0.3` bundled into one file, e.g. via
|
|
66
|
+
`esm.sh/canvg@4?bundle`, with its one unnecessary Node-environment shim
|
|
67
|
+
import removed — see the file's own header comment).
|
|
68
|
+
|
|
69
|
+
For **vector PDF export** (optional — the PDF entry appears in the Download
|
|
70
|
+
menu only when both are loaded before archaeopteryx.js):
|
|
71
|
+
* jspdf (4.x): https://www.npmjs.com/package/jspdf
|
|
72
|
+
* svg2pdf.js (2.8.x): https://www.npmjs.com/package/svg2pdf.js
|
|
73
|
+
|
|
74
|
+
File (Newick/New Hampshire, Nexus, phyloXML, FASTA) and SVG download, as well as saving
|
|
75
|
+
the exported PNG, use native browser APIs (`Blob`, `canvas.toBlob()`, and an
|
|
76
|
+
`<a download>` link), so Blob.js, canvas-toBlob.js and FileSaver.js are no longer
|
|
77
|
+
required.
|
|
78
|
+
|
|
79
|
+
The user interface (control panel, sliders, dialogs) is built with native DOM
|
|
80
|
+
elements, so **jQuery and jQuery UI are no longer required** — neither their
|
|
81
|
+
JavaScript nor jQuery UI's CSS.
|
|
64
82
|
|
|
65
83
|
|
|
66
84
|
## Basic Example of HTML for launching Archaeopteryx.js
|
|
67
85
|
|
|
68
86
|
Example of HTML page to launch a basic Archaeopteryx.js instance:
|
|
69
|
-
```
|
|
87
|
+
```html
|
|
70
88
|
<!DOCTYPE html>
|
|
71
|
-
<
|
|
89
|
+
<html>
|
|
72
90
|
<head>
|
|
91
|
+
<meta charset="utf-8">
|
|
73
92
|
<title>Archaeopteryx.js Basic Demo</title>
|
|
74
93
|
|
|
75
|
-
<!--
|
|
76
|
-
<
|
|
77
|
-
|
|
78
|
-
<!--
|
|
79
|
-
<script src="
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
<script src="
|
|
85
|
-
|
|
86
|
-
<!-- Archaeopteryx.js requires forester.js and phyloxml.js:-->
|
|
87
|
-
<script src="http://path/to/phyloxml.js"></script>
|
|
88
|
-
<script src="http://path/to/forester.js"></script>
|
|
89
|
-
<script src="http://path/to/archaeopteryx.js"></script>
|
|
90
|
-
|
|
91
|
-
<!-- CSS for jQuery UI: -->
|
|
92
|
-
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.0/themes/base/jquery-ui.css">
|
|
93
|
-
|
|
94
|
-
<script>
|
|
95
|
-
function load() {
|
|
96
|
-
var options = {};
|
|
97
|
-
options.backgroundColorDefault = '#f0f0f0';
|
|
98
|
-
var settings = {};
|
|
99
|
-
var loc = 'https://raw.githubusercontent.com/cmzmasek/archaeopteryx-js/master/test/data/phyloxml_trees/apaf.xml';
|
|
100
|
-
|
|
101
|
-
jQuery.get(loc,
|
|
102
|
-
function (data) {
|
|
103
|
-
var tree = null;
|
|
104
|
-
try {
|
|
105
|
-
tree = archaeopteryx.parseTree(loc, data, true, false);
|
|
106
|
-
}
|
|
107
|
-
catch (e) {
|
|
108
|
-
alert("error while parsing tree: " + e);
|
|
109
|
-
}
|
|
110
|
-
if (tree) {
|
|
111
|
-
try {
|
|
112
|
-
archaeopteryx.launch('#phylogram1', tree, options, settings);
|
|
113
|
-
}
|
|
114
|
-
catch (e) {
|
|
115
|
-
alert("error while launching archaeopteryx: " + e);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
},
|
|
119
|
-
"text")
|
|
120
|
-
.fail(function () {
|
|
121
|
-
alert("error: failed to read tree(s) from \"" + loc + "\"");
|
|
122
|
-
}
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
</script>
|
|
94
|
+
<!-- D3.js (version 7): -->
|
|
95
|
+
<script src="https://d3js.org/d3.v7.min.js"></script>
|
|
96
|
+
|
|
97
|
+
<!-- SAX XML parser (needed by phyloxml.js): -->
|
|
98
|
+
<script src="https://path/to/sax.js"></script>
|
|
99
|
+
|
|
100
|
+
<!-- Archaeopteryx.js requires forester.js and phyloxml.js: -->
|
|
101
|
+
<script src="https://path/to/phyloxml.js"></script>
|
|
102
|
+
<script src="https://path/to/forester.js"></script>
|
|
103
|
+
<script src="https://path/to/archaeopteryx.js"></script>
|
|
126
104
|
</head>
|
|
127
105
|
|
|
128
|
-
<body
|
|
106
|
+
<body>
|
|
129
107
|
<div>
|
|
130
108
|
<h2>Archaeopteryx.js Basic Demo</h2>
|
|
131
|
-
<div id=
|
|
132
|
-
<div id='controls0' class='ui-widget-content'></div>
|
|
109
|
+
<div id="phylogram1"></div>
|
|
133
110
|
</div>
|
|
111
|
+
|
|
112
|
+
<script>
|
|
113
|
+
window.addEventListener('load', async () => {
|
|
114
|
+
const loc = 'https://path/to/apaf.xml';
|
|
115
|
+
const config = {}; // see "For Developers" below for what can go here
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
const response = await fetch(loc);
|
|
119
|
+
if (!response.ok) {
|
|
120
|
+
throw new Error('HTTP ' + response.status + ' loading ' + loc);
|
|
121
|
+
}
|
|
122
|
+
const data = await response.text();
|
|
123
|
+
|
|
124
|
+
// launchArchaeopteryx returns a viewer handle:
|
|
125
|
+
// viewer.getSelectedNodes() -- the user's node selection
|
|
126
|
+
// viewer.destroy() -- unmount the viewer completely
|
|
127
|
+
// (call it when your view goes away;
|
|
128
|
+
// a later launch works normally)
|
|
129
|
+
const viewer = archaeopteryx.launchArchaeopteryx('#phylogram1', loc, data, config);
|
|
130
|
+
} catch (e) {
|
|
131
|
+
document.getElementById('phylogram1').textContent = 'Error: ' + e.message;
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
</script>
|
|
134
135
|
</body>
|
|
136
|
+
</html>
|
|
135
137
|
```
|
|
136
138
|
|
|
139
|
+
Archaeopteryx.js reports problems by **throwing** — a failed download, an
|
|
140
|
+
unparsable tree, a container that does not resolve, and an unknown or removed
|
|
141
|
+
config key all land in the `catch` above with a message naming the problem.
|
|
142
|
+
See [For Developers](#for-developers) below for what goes into `config`.
|
|
143
|
+
Working copies of every file referenced above are vendored in this
|
|
144
|
+
repository's `docs/lib/` (they are what the [live demos](#live-demos) load),
|
|
145
|
+
and `docs/demo.html` is a real, running version of this page — including
|
|
146
|
+
using `viewer.destroy()` to switch trees in place.
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
|
|
137
150
|
|
|
151
|
+
## Using the visualizations
|
|
138
152
|
|
|
153
|
+
A tree opens **already coloured** by its most informative field — the viewer
|
|
154
|
+
inspects what the tree carries (taxonomy, sequence fields, custom properties)
|
|
155
|
+
and decides by itself what is worth showing. There is nothing to configure.
|
|
156
|
+
|
|
157
|
+
* The **Color** and **Shape** menus (top of the control panel) list every
|
|
158
|
+
field worth visualizing, best first. Colouring paints the node label and
|
|
159
|
+
the node itself; shapes draw a symbol per value. Fields with many values
|
|
160
|
+
(hosts, species) sit at the end of the Color list — they are offered, but
|
|
161
|
+
never chosen for you.
|
|
162
|
+
* Numeric fields with few values are treated as **codes** (H5N1 vs H5N2)
|
|
163
|
+
and get individual colours; with many values they become a **gradient**
|
|
164
|
+
(years). The legend's `[colors]` / `[gradient]` chip switches between the
|
|
165
|
+
two where both make sense.
|
|
166
|
+
* The **legend** is a card you can **drag anywhere**. It shows a colour and
|
|
167
|
+
a **count** per value, `[by count]` / `[A-Z]` toggles the order, a dashed
|
|
168
|
+
**no value** row counts the nodes the field does not cover, and very long
|
|
169
|
+
legends show the top 20 with a `[+N more]` chip. Legends are part of PNG,
|
|
170
|
+
PDF
|
|
171
|
+
and SVG exports (exports always come out light).
|
|
172
|
+
* **Switch into a subtree** (or delete part of the tree) and the menus,
|
|
173
|
+
counts and legends are re-derived for what is on screen — a field with too
|
|
174
|
+
many values on the full tree may become available inside a clade. Colours
|
|
175
|
+
never change when you do this: a value keeps its colour for the whole
|
|
176
|
+
session.
|
|
177
|
+
* The **Visualizations** checkbox hides the chosen colours/shapes; the
|
|
178
|
+
**Visual Styles** checkbox controls colours embedded in the tree file
|
|
179
|
+
itself (and phyloXML branch colours). Search hits and selections always
|
|
180
|
+
outrank visualization colours. **Esc** returns to the opening state.
|
|
181
|
+
|
|
182
|
+
## Layouts
|
|
183
|
+
|
|
184
|
+
Three layouts, switched with the second row of buttons at the top of the
|
|
185
|
+
panel: **rectangular** (root at left), **circular**, and **unrooted** — the
|
|
186
|
+
desktop's equal-angle fan, where each subtree opens a wedge proportional to
|
|
187
|
+
how many tips it holds.
|
|
188
|
+
|
|
189
|
+
In the two radial layouts the zoom row changes meaning, exactly as on the
|
|
190
|
+
desktop: **Y+ / Y− become the plain + / − zoom** (a circle has one diameter;
|
|
191
|
+
the mouse wheel zooms too, and never rotates), **X− / X+ become rotate** (a
|
|
192
|
+
32nd of a turn per press), and the fit-width slot becomes the **node label
|
|
193
|
+
direction** flip — labels riding their spokes or standing upright — while
|
|
194
|
+
vertical expansion greys out. **Fit** centres and scales the fan; **Esc**
|
|
195
|
+
also resets rotation and label direction. Unrooted
|
|
196
|
+
additionally greys out the aligned-phylogram option and Auto-hide Labels
|
|
197
|
+
(there is no common label edge, and no even row spacing to hide against).
|
|
198
|
+
|
|
199
|
+
## Searching
|
|
200
|
+
|
|
201
|
+
Two search boxes (A and B), each with its own **field** menu (built from what
|
|
202
|
+
the tree actually carries — names, taxonomy and sequence fields, every custom
|
|
203
|
+
property, branch lengths, confidences, structural values) and **match mode**
|
|
204
|
+
(contains / starts with / ends with / whole word / regex for text;
|
|
205
|
+
`= != < <= > >= range` for numbers). Inside one box, `,` means OR and `+` means AND (plain-text
|
|
206
|
+
modes only); **Combine A & B** intersects or unites the two. **Match case**
|
|
207
|
+
and **Inverse** apply to both.
|
|
208
|
+
|
|
209
|
+
Hits are hard to miss: their labels take the search colour **in bold**, a
|
|
210
|
+
translucent **pulsing halo** breathes behind each hit, and everything that is
|
|
211
|
+
*not* a hit fades — the desktop's "dim non-matches", engaged only while at
|
|
212
|
+
least one hit is actually visible, so a fruitless search never washes the
|
|
213
|
+
tree out. The **overview** miniature marks every hit as a dot in the same
|
|
214
|
+
colour, and a **◀ k / N ▶** navigator appears under the search boxes: each
|
|
215
|
+
press centres the previous / next hit in the viewport, wrapping around.
|
|
216
|
+
|
|
217
|
+
## Keyboard
|
|
218
|
+
|
|
219
|
+
Deliberately minimal: **Esc** or **Home** resets the view, **O** cycles the
|
|
220
|
+
overview between corners, **PageUp / PageDown** change the font size — and
|
|
221
|
+
the **mouse wheel** zooms (Shift: vertical only; Shift+Alt: horizontal;
|
|
222
|
+
Ctrl+Shift: font size). Everything else is a button; the old Alt+letter
|
|
223
|
+
combos are gone (macOS labels that key Option and types glyphs with it).
|
|
224
|
+
Nothing fires while the cursor is in a text box.
|
|
225
|
+
|
|
226
|
+
## Sequence alignments
|
|
227
|
+
|
|
228
|
+
A tree whose tips carry `<mol_seq is_aligned="true">` shows the alignment as
|
|
229
|
+
a residue track beside the tree (rectangular layout only — an alignment is
|
|
230
|
+
inherently horizontal). Amino acids use a physico-chemical colour scheme,
|
|
231
|
+
nucleotides one colour per base — decided from the residues themselves — with
|
|
232
|
+
gaps drawn as faint dashes that join into lines, so indel blocks read at a
|
|
233
|
+
glance.
|
|
234
|
+
|
|
235
|
+
A long alignment shows a scrollable **window** (at most ~60% of the display):
|
|
236
|
+
drag the slider at the window bottom, or roll the mouse wheel over the track;
|
|
237
|
+
the tree itself never moves. Under the rows sit a **conservation** bar per
|
|
238
|
+
column with the consensus residue beneath it (scored over the tips currently
|
|
239
|
+
displayed), and a 1-based **column ruler**. **Hover any residue** for its
|
|
240
|
+
alignment column, its position within that sequence's own ungapped residues,
|
|
241
|
+
its full name, class, and Kyte-Doolittle hydropathy. The **Alignment**
|
|
242
|
+
checkbox under Display Data toggles the whole track.
|
|
243
|
+
|
|
244
|
+
Alignments arrive with the tree: as phyloXML `<mol_seq is_aligned="true">`
|
|
245
|
+
elements, or in a **Nexus** file whose characters matrix accompanies its tree.
|
|
246
|
+
The **Nexus** entry in the Download menu writes the current tree *and* its
|
|
247
|
+
alignment back into one Nexus file (Taxa, Characters and Trees blocks).
|
|
248
|
+
|
|
249
|
+
## Time trees
|
|
250
|
+
|
|
251
|
+
A tree whose nodes carry phyloXML `<date>` elements is drawn against time.
|
|
252
|
+
Ages (`unit="mya"` and friends, or values that look like ages) get the
|
|
253
|
+
**geologic axis**: two rows of ICS intervals in their official colours —
|
|
254
|
+
Period over Epoch for most trees, coarser pairs for Precambrian-deep ones —
|
|
255
|
+
plus a "Ma before present" ruler. Years (`unit="year"`, or values that look
|
|
256
|
+
like calendar years) get a labelled **calendar axis** instead. The tree's
|
|
257
|
+
layout itself never changes: time is an overlay calibrated by the dates, so
|
|
258
|
+
it also works for a fossil-only tree, where the axis simply stops at the
|
|
259
|
+
youngest tip and labels that age (the ammonite demo ends at the K-Pg, 66).
|
|
260
|
+
|
|
261
|
+
Nodes with a date **range** (`minimum`/`maximum` — minimum is the younger
|
|
262
|
+
bound) draw uncertainty bars: translucent blue **HPD age bars** on internal
|
|
263
|
+
nodes, sepia **fossil-range (FAD/LAD) bars with end caps** on tips. Node
|
|
264
|
+
tooltips show the date. The **Time Axis** checkbox under Display Data toggles
|
|
265
|
+
everything; the axis needs a phylogram (branch lengths carry the time) and
|
|
266
|
+
the rectangular layout. **Time Grid** (off by default, like the desktop's
|
|
267
|
+
"Time axis grid lines") adds faint vertical lines behind the tree at the
|
|
268
|
+
fine geologic-interval boundaries or the calendar year ticks, so a node's
|
|
269
|
+
position can be read against the axis.
|
|
139
270
|
|
|
140
271
|
# forester.js
|
|
141
272
|
forester.js is a general suite for dealing with phylogenetic trees.
|
|
@@ -204,3 +335,883 @@ In New Hampshire format:
|
|
|
204
335
|
|
|
205
336
|
|
|
206
337
|
|
|
338
|
+
|
|
339
|
+
# For Developers
|
|
340
|
+
|
|
341
|
+
This section describes the two configuration objects Archaeopteryx.js accepts.
|
|
342
|
+
It is generated against the current source; if something here disagrees with
|
|
343
|
+
the code, the code is right and this is a bug.
|
|
344
|
+
|
|
345
|
+
## The entry points
|
|
346
|
+
|
|
347
|
+
```js
|
|
348
|
+
// parse and launch in one step (what most callers want); fetch the file
|
|
349
|
+
// content yourself -- the library does no networking
|
|
350
|
+
const viewer = archaeopteryx.launchArchaeopteryx(container, fileName, data, config);
|
|
351
|
+
|
|
352
|
+
// or parse yourself, then launch
|
|
353
|
+
const tree = archaeopteryx.parseTree(fileName, data);
|
|
354
|
+
const viewer = archaeopteryx.launch(container, tree, config);
|
|
355
|
+
|
|
356
|
+
// later, e.g. when an SPA removes the view:
|
|
357
|
+
viewer.destroy();
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Both entry points take **exactly** the arguments shown — a call with the old
|
|
361
|
+
trailing arguments (the separate settings bag, `nodeVisualizations`,
|
|
362
|
+
`nodeLabels`, `specialVisualizations`, or the positional Newick parse
|
|
363
|
+
options) **throws** with a message saying where each one went: everything now
|
|
364
|
+
lives in the **one config object** (`nodeLabels`,
|
|
365
|
+
`nhConfidenceValuesInBrackets`, `nhConfidenceValuesAsInternalNames` are
|
|
366
|
+
config keys). `config` itself is optional — `archaeopteryx.launch('#phylogram1',
|
|
367
|
+
tree)` works.
|
|
368
|
+
|
|
369
|
+
`container` is a **CSS selector or the DOM element itself** (frameworks hand
|
|
370
|
+
you elements). A container that cannot be resolved **throws** — it used to
|
|
371
|
+
render nothing and say nothing. Both entry points return a **viewer handle**:
|
|
372
|
+
|
|
373
|
+
```js
|
|
374
|
+
viewer.getSelectedNodes(); // the node-menu selections (enableManualNodeSelection)
|
|
375
|
+
viewer.destroy(); // unmount COMPLETELY: the container DOM, the node
|
|
376
|
+
// menu / dialogs / alignment scroller, the window
|
|
377
|
+
// resize listener and every page-level key/wheel
|
|
378
|
+
// handler; a later launch() works normally
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
One viewer per page: the library keeps its display state in one place, so a
|
|
382
|
+
second launch — into any container — replaces the first. Launching into the
|
|
383
|
+
same container is the supported way to switch trees (the demo pages do
|
|
384
|
+
exactly that).
|
|
385
|
+
|
|
386
|
+
### Loading the library
|
|
387
|
+
|
|
388
|
+
One file, loadable every way an embedder might want it:
|
|
389
|
+
|
|
390
|
+
* **`<script>` tags** (the classic path): load `d3` (v7), `sax`, `phyloxml`,
|
|
391
|
+
`forester`, then `archaeopteryx`; use `window.archaeopteryx`.
|
|
392
|
+
* **AMD** (Dojo, RequireJS): `require(['archaeopteryx'], ...)` — the module
|
|
393
|
+
reads its dependencies off the page's globals when first required (so load
|
|
394
|
+
the dependency scripts first), and also still sets `window.archaeopteryx`.
|
|
395
|
+
* **CommonJS / bundlers / Node**: `const {archaeopteryx} =
|
|
396
|
+
require('archaeopteryx')` — with **TypeScript definitions** included
|
|
397
|
+
(`archaeopteryx.d.ts` types the whole config object and the handle). In
|
|
398
|
+
plain Node, with no d3 at all, every **parser** works
|
|
399
|
+
(`archaeopteryx.parseNexus(...)` etc.); only `launch()` needs a browser
|
|
400
|
+
and d3, and says so by name. `forester` is importable as the extensionless
|
|
401
|
+
subpath `require('archaeopteryx/forester')` (the package's exports map
|
|
402
|
+
defines exactly that path).
|
|
403
|
+
|
|
404
|
+
Dependencies are checked when used, never at load time, and every failure
|
|
405
|
+
names exactly what is missing (including "the loaded d3 is not usable as d3
|
|
406
|
+
version 7"). The optional export libraries stay page-level globals in every
|
|
407
|
+
loading style: `window.Canvg` (PNG), `window.jspdf` + svg2pdf.js (PDF).
|
|
408
|
+
|
|
409
|
+
The parser is picked from the data and the `location`: content starting with
|
|
410
|
+
`#NEXUS` (or a name ending in `.nex`/`.nexus`) is read as Nexus, JSON content
|
|
411
|
+
(or a name ending in `.json`) as an **Auspice/Nextstrain v2** `dataset.json`,
|
|
412
|
+
a name ending in `xml` as phyloXML, anything else as New Hampshire (Newick).
|
|
413
|
+
A Nexus file shows its **first** tree; a protein/DNA/RNA characters matrix in
|
|
414
|
+
the file (sequential or interleaved) lands on the tips as an aligned
|
|
415
|
+
`mol_seq`, so the alignment track appears just as it does for phyloXML.
|
|
416
|
+
|
|
417
|
+
An Auspice dataset opens on the **time view** (branch lengths from `num_date`
|
|
418
|
+
differences; a divergence-only build falls back to `div` differences): the
|
|
419
|
+
calendar time axis and node-age bars come from `num_date` and its confidence
|
|
420
|
+
interval, and every trait (country, host, clade, ...) becomes a
|
|
421
|
+
`nextstrain:<trait>` node property — so Color-by, search and the node dialog
|
|
422
|
+
pick them up. Both metrics are retained, and
|
|
423
|
+
`forester.applyTimeBranchLengths(phy)` /
|
|
424
|
+
`forester.applyDivergenceBranchLengths(phy)` /
|
|
425
|
+
`forester.hasTimeAndDivergence(phy)` are the plumbing for a future
|
|
426
|
+
time↔divergence display toggle.
|
|
427
|
+
|
|
428
|
+
**BEAST-style and NHX annotations** in Newick/Nexus input are always parsed
|
|
429
|
+
(they used to be discarded): in a `[&key=value,...]` blob — as written by
|
|
430
|
+
BEAST, BEAST 2, TreeAnnotator, FigTree and MrBayes — `posterior`, `prob`
|
|
431
|
+
(+`prob_stddev`) and `bootstrap` become confidences, node `height`
|
|
432
|
+
(median/mean) with its `95%_HPD` (or range) becomes the node date the age
|
|
433
|
+
bars draw, FigTree's `!color` becomes the branch colour, and every other
|
|
434
|
+
field (`rate`, traits, ...) becomes a `beast:<key>` node property for
|
|
435
|
+
Color-by and search. Classic `[&&NHX:...]` tags map to their phyloXML
|
|
436
|
+
equivalents (`S=` taxonomy, `T=` taxonomy id, `B=` support, `D=`
|
|
437
|
+
duplication/speciation event, `GN=`/`AC=` sequence name/accession). Plain
|
|
438
|
+
`[number]` brackets keep their old meaning (confidence values).
|
|
439
|
+
|
|
440
|
+
Both entry points **throw** on bad input — an undefined or empty tree, an
|
|
441
|
+
unparseable file, or a config key that no longer exists. Nothing is reported by
|
|
442
|
+
a popup any more, and nothing fails silently.
|
|
443
|
+
|
|
444
|
+
## Supported file formats
|
|
445
|
+
|
|
446
|
+
| Format | I/O | What Archaeopteryx.js does with it | Ref. |
|
|
447
|
+
|---|---|---|---|
|
|
448
|
+
| **Newick** / New Hampshire (`.nwk`, `.nh`, `.tre`) | in / out | The base tree: topology, names, branch lengths, and bracketed confidence values. | [1] |
|
|
449
|
+
| **NHX** — New Hampshire eXtended | in | `[&&NHX:...]` tags riding on Newick: taxonomy (`S=`, `T=`), sequence (`GN=`, `AC=`), support (`B=`), duplication/speciation events (`D=`). | [2] |
|
|
450
|
+
| **Nexus** (`.nex`, `.nexus`) | in / out | One file for the tree(s) and, in a `CHARACTERS`/`DATA` block, an aligned protein/DNA/RNA matrix (sequential or interleaved) — the tree and its alignment together. | [3] |
|
|
451
|
+
| **phyloXML** (`.xml`) | in / out | The richest native format: taxonomy, sequences and alignments, dates, confidences, branch colours and arbitrary custom properties. | [4] |
|
|
452
|
+
| **Auspice / Nextstrain** `dataset.json` (v2) | in | Phylodynamic builds: sampling dates and their confidence, cumulative divergence, and discrete traits (country, clade, host, ...) with their posterior distributions. | [5] |
|
|
453
|
+
| **BEAST** / BEAST 2 / TreeAnnotator annotations | in (embedded in Newick/Nexus) | `[&posterior=...,height_95%_HPD={lo,hi},rate=...]`-style blobs: posterior clade support, node-age confidence intervals, per-branch rates and other traits. FigTree's `!color` is read the same way. | [6, 7] |
|
|
454
|
+
| **MrBayes** annotations | in (embedded in Newick/Nexus) | `prob=`/`prob.stddev=` blobs: posterior-probability clade support. | [8] |
|
|
455
|
+
| **FASTA** | out | The molecular sequence(s) of the selected tip(s), or every sequence the tree carries. Offered in the Download menu only when the tree actually carries molecular sequences (aligned or not). | [9] |
|
|
456
|
+
| SVG · PNG · vector PDF | out | A snapshot of the drawn tree for publication or further editing — vector (SVG, PDF) or raster (PNG). General-purpose graphics formats, not phylogenetic data, so no literature reference applies. | — |
|
|
457
|
+
|
|
458
|
+
The parser for a given input is auto-detected (see **The entry points**
|
|
459
|
+
above); the Download menu offers whichever output formats the current tree
|
|
460
|
+
can carry.
|
|
461
|
+
|
|
462
|
+
### References
|
|
463
|
+
|
|
464
|
+
1. Felsenstein, J. *PHYLIP (Phylogeny Inference Package)*. Department of
|
|
465
|
+
Genome Sciences, University of Washington, Seattle. The Newick tree
|
|
466
|
+
format itself has no single peer-reviewed citation — it was agreed at a
|
|
467
|
+
1986 meeting of phylogenetics software authors at Newick's Lobster
|
|
468
|
+
House, Dover, New Hampshire, and has since been documented in the
|
|
469
|
+
PHYLIP distribution and in most subsequent tree-software manuals.
|
|
470
|
+
2. Zmasek, C.M., Eddy, S.R. (2001). ATV: display and manipulation of
|
|
471
|
+
annotated phylogenetic trees. *Bioinformatics*, 17(4), 383–384.
|
|
472
|
+
3. Maddison, D.R., Swofford, D.L., Maddison, W.P. (1997). NEXUS: an
|
|
473
|
+
extensible file format for systematic information. *Systematic
|
|
474
|
+
Biology*, 46(4), 590–621.
|
|
475
|
+
4. Han, M.V., Zmasek, C.M. (2009). phyloXML: XML for evolutionary biology
|
|
476
|
+
and comparative genomics. *BMC Bioinformatics*, 10, 356.
|
|
477
|
+
5. Hadfield, J., Megill, C., Bell, S.M., Huddleston, J., Potter, B.,
|
|
478
|
+
Callender, C., Sagulenko, P., Bedford, T., Neher, R.A. (2018).
|
|
479
|
+
Nextstrain: real-time tracking of pathogen evolution. *Bioinformatics*,
|
|
480
|
+
34(23), 4121–4123.
|
|
481
|
+
6. Drummond, A.J., Rambaut, A. (2007). BEAST: Bayesian evolutionary
|
|
482
|
+
analysis by sampling trees. *BMC Evolutionary Biology*, 7, 214.
|
|
483
|
+
7. Bouckaert, R., Vaughan, T.G., Barido-Sottani, J., Duchêne, S., Fourment,
|
|
484
|
+
M., Gavryushkina, A., Heled, J., Jones, G., Kühnert, D., De Maio, N.,
|
|
485
|
+
Matschiner, M., Mendes, F.K., Müller, N.F., Ogilvie, H.A., du Plessis,
|
|
486
|
+
L., Popinga, A., Rambaut, A., Rasmussen, D., Siveroni, I., Suchard,
|
|
487
|
+
M.A., Wu, C.-H., Xie, D., Zhang, C., Stadler, T., Drummond, A.J. (2019).
|
|
488
|
+
BEAST 2.5: an advanced software platform for Bayesian evolutionary
|
|
489
|
+
analysis. *PLoS Computational Biology*, 15(4), e1006650.
|
|
490
|
+
8. Ronquist, F., Teslenko, M., van der Mark, P., Ayres, D.L., Darling, A.,
|
|
491
|
+
Höhna, S., Larget, B., Liu, L., Suchard, M.A., Huelsenbeck, J.P. (2012).
|
|
492
|
+
MrBayes 3.2: efficient Bayesian phylogenetic inference and model choice
|
|
493
|
+
across a large model space. *Systematic Biology*, 61(3), 539–542.
|
|
494
|
+
9. Pearson, W.R., Lipman, D.J. (1988). Improved tools for biological
|
|
495
|
+
sequence comparison. *Proceedings of the National Academy of Sciences
|
|
496
|
+
USA*, 85(8), 2444–2448.
|
|
497
|
+
|
|
498
|
+
## Intelligent pre-sets
|
|
499
|
+
|
|
500
|
+
Almost everything that used to be configured is now read off the tree: a tree
|
|
501
|
+
with taxonomies shows taxonomies and offers a Taxonomy control, a tree without
|
|
502
|
+
them shows neither; a tree whose branches mostly carry lengths is drawn to
|
|
503
|
+
scale, one whose branches mostly do not is drawn as a cladogram.
|
|
504
|
+
|
|
505
|
+
So **the best configuration is usually an empty one**. What is left is the
|
|
506
|
+
handful of things no tree can answer for you: which layout, how the viewer is
|
|
507
|
+
sized, what the surrounding application allows, and what to prefill the search
|
|
508
|
+
boxes with.
|
|
509
|
+
|
|
510
|
+
Everything else is either derived, or a control the user can change once the
|
|
511
|
+
tree is on screen. Those controls still have defaults, and the defaults are
|
|
512
|
+
chosen per tree — they are simply no longer yours to set at launch.
|
|
513
|
+
|
|
514
|
+
That includes **which label checkboxes start checked**: instead of blindly
|
|
515
|
+
showing every field the tree carries, the viewer measures the actual label
|
|
516
|
+
text and unchecks fields that only repeat another field or that would make
|
|
517
|
+
the combined labels uselessly long (see *Initial label fields* in the
|
|
518
|
+
developer spec below). The checkboxes are still there to override it.
|
|
519
|
+
|
|
520
|
+
The same data-driven rule applies to the two big overlays: a tree whose tips
|
|
521
|
+
carry an aligned `mol_seq` opens with its **sequence alignment** showing, and
|
|
522
|
+
a tree with phyloXML `<date>` elements opens with its **time axis** drawn —
|
|
523
|
+
each with a checkbox under Display Data to turn it off.
|
|
524
|
+
|
|
525
|
+
Support and branch-length values draw **2 px smaller than the label font**
|
|
526
|
+
(never below 6 px), as on the desktop, so they annotate without competing.
|
|
527
|
+
And besides the numeric display there are **Support Dots**: a filled dot at
|
|
528
|
+
the midpoint of every branch whose support is at least 95% (`supportDotMinimum`;
|
|
529
|
+
posterior- and bootstrap-scaled trees are told apart automatically). The dot
|
|
530
|
+
is always a fixed amount wider than the branch itself, so it tracks the
|
|
531
|
+
Branch Width slider instead of sitting at one fixed size. A branch drawn
|
|
532
|
+
shorter than the dot itself stays clean.
|
|
533
|
+
|
|
534
|
+
## Configuration
|
|
535
|
+
|
|
536
|
+
One object, passed as the third argument. It is optional, and the best
|
|
537
|
+
configuration is usually an empty one — almost everything that used to be
|
|
538
|
+
configured is now read off the tree (see **Intelligent pre-sets** above). The
|
|
539
|
+
twenty-eight keys below are the ones no tree can answer for you.
|
|
540
|
+
|
|
541
|
+
There used to be two objects, `options` and `settings`, split by whether the
|
|
542
|
+
user could also change the value from the control panel. That was a fact about
|
|
543
|
+
the internals, not something a caller could derive, and getting it wrong was
|
|
544
|
+
silent: the right name in the wrong object did nothing at all. There is now one
|
|
545
|
+
object. A fourth argument is still accepted and merged, so existing call sites
|
|
546
|
+
keep working; it logs a deprecation warning.
|
|
547
|
+
|
|
548
|
+
### Still used
|
|
549
|
+
|
|
550
|
+
| Key | Default | What it does |
|
|
551
|
+
| --- | --- | --- |
|
|
552
|
+
| `collapseControlPanel` | `false` | Open with the control panel collapsed to just its header bar — the same state its own hide/show button toggles. |
|
|
553
|
+
| `enableDynamicSizing` | `true` | Size the tree to its container, and follow window resizes. |
|
|
554
|
+
| `displayWidth` | `800` | Width — only when dynamic sizing is off. |
|
|
555
|
+
| `displayHeight` | `600` | Height — only when dynamic sizing is off. |
|
|
556
|
+
| `zoomToFitUponWindowResize` | `true` | Re-fit the tree after a window resize. |
|
|
557
|
+
| `rootOffset` | `254` | Distance from the left edge to the root. The default clears the control panel: its inset plus its width plus a margin. |
|
|
558
|
+
| `layout` | `'rectangular'` | The starting layout: `'rectangular'`, `'circular'`, or `'unrooted'`. |
|
|
559
|
+
| `ladderizeTree` | `true` | Ladderize the tree on load: at each node, the larger clade first (any number of children, so a polytomy sorts too). |
|
|
560
|
+
| `showMsa` | tree-derived | Open with the alignment track shown. Default: on when the tree carries an aligned `mol_seq`, off otherwise — an explicit `true`/`false` overrides that. |
|
|
561
|
+
| `showTimeAxis` | tree-derived | Open with the time axis shown. Default: on when the tree carries `<date>` elements, off otherwise — an explicit `true`/`false` overrides that. |
|
|
562
|
+
| `timeAxisGrid` | `false` | Open with the Time Grid vertical lines on (only meaningful — and only offered as a checkbox — while the time axis itself is shown). |
|
|
563
|
+
| `showSupportDots` | `false` | Open with the Support Dots marks on (the checkbox appears whenever the tree has confidences). |
|
|
564
|
+
| `supportDotMinimum` | `95` | Support Dots threshold, as a percentage. On a tree whose confidences top out at 1 (posterior probabilities) it is read on the 0–1 scale, so the default means ≥ 0.95 there and ≥ 95 on a bootstrap tree. |
|
|
565
|
+
| `searchAinitialValue` | `null` | Prefill search box A. |
|
|
566
|
+
| `searchBinitialValue` | `null` | Prefill search box B. |
|
|
567
|
+
| `enableVisualizations` | `true` | Offer the Color / Shape visualizations (which fields they cover is decided from the tree). |
|
|
568
|
+
| `initialVisualization` | `null` | The visualization to open with, by its Color-menu name (e.g. `'Host'`; case-insensitive). A name the tree cannot honour logs a console warning and falls back to the automatic choice, so a site-wide value is safe on trees without that field. Default: Archaeopteryx.js picks the most informative field itself. |
|
|
569
|
+
| `visualizationsLegendXpos` | `254` | Legend position, x. |
|
|
570
|
+
| `visualizationsLegendYpos` | `30` | Legend position, y. |
|
|
571
|
+
| `enableDownloads` | `true` | Offer the download buttons. |
|
|
572
|
+
| `pngExportScale` | `4` | PNG export resolution multiplier. |
|
|
573
|
+
| `nhExportWriteConfidences` | `true` | Write confidences into exported Newick. |
|
|
574
|
+
| `nhConfidenceValuesInBrackets` | `true` | Newick parsing: read `[90]`-style bracketed values as confidences. |
|
|
575
|
+
| `nhConfidenceValuesAsInternalNames` | `false` | Newick parsing: read internal node names as confidence values. |
|
|
576
|
+
| `nodeLabels` | `null` | Custom label-field checkboxes: `{key: {label, description, propertyRef, showButton, selected}}` — each adds a panel checkbox labelling nodes with the named property's value. (Was `launch()`'s sixth positional argument.) |
|
|
577
|
+
| `enableSubtreeDeletion` | `true` | Offer node / subtree deletion in the node menu. |
|
|
578
|
+
| `enableAccessToDatabases` | `true` | Offer the “Access DB” link in the node menu. |
|
|
579
|
+
| `enableManualNodeSelection` | `false` | Add the Select/Deselect entries to the node menu. |
|
|
580
|
+
|
|
581
|
+
### Anything else throws
|
|
582
|
+
|
|
583
|
+
An unrecognised key is an error, whether it was removed in this modernization
|
|
584
|
+
or simply mistyped:
|
|
585
|
+
|
|
586
|
+
```
|
|
587
|
+
ArchaeopteryxJS: ERROR: removed config key(s) passed to launch:
|
|
588
|
+
"circular" -- renamed to "layout": use layout: "circular"
|
|
589
|
+
|
|
590
|
+
ArchaeopteryxJS: ERROR: unknown config key(s) passed to launch: "enableDownlods"
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
An ignored key looks like it worked. If you are upgrading, run once and fix
|
|
594
|
+
whatever it names.
|
|
595
|
+
|
|
596
|
+
### What replaced the rest
|
|
597
|
+
|
|
598
|
+
A few of these are worth spelling out, because they are decisions rather than
|
|
599
|
+
constants:
|
|
600
|
+
|
|
601
|
+
* **Phylogram or cladogram** — the tree is drawn to scale when **more than half
|
|
602
|
+
its branches carry a positive length**. A tree where a handful of branches
|
|
603
|
+
have a length and the rest do not is not a phylogram with gaps; it is a
|
|
604
|
+
cladogram, and is now drawn as one.
|
|
605
|
+
* **Branch width** — `2` for a tree of 50 tips or fewer, `1` above that.
|
|
606
|
+
Hairlines suit a crowded tree; on a dozen branches they just look faint.
|
|
607
|
+
* **Labels** — node names, taxonomy, sequences, confidences and events are each
|
|
608
|
+
shown when the tree actually contains them.
|
|
609
|
+
* **Short names** — on from the start when the tree has names longer than 18
|
|
610
|
+
characters, off otherwise. The checkbox is always there either way.
|
|
611
|
+
* **Font** — one size (11) for every label, in whichever sans-serif the
|
|
612
|
+
reader's own system renders best.
|
|
613
|
+
|
|
614
|
+
### Removed — passing these throws
|
|
615
|
+
|
|
616
|
+
All 118 of them, alphabetically:
|
|
617
|
+
|
|
618
|
+
| Key | Why, and what to do instead |
|
|
619
|
+
| --- | --- |
|
|
620
|
+
| `alignPhylogram` | Aligning the tips is a control, not a launch option. |
|
|
621
|
+
| `allowManualNodeSelection` | Renamed to `enableManualNodeSelection`. |
|
|
622
|
+
| `backgroundColorDefault` | The background is fixed. |
|
|
623
|
+
| `backgroundColorForPrintExportDefault` | The export background is fixed. |
|
|
624
|
+
| `border` | Style the tree's svg with CSS instead. |
|
|
625
|
+
| `branchColorDefault` | The default branch colour is fixed. |
|
|
626
|
+
| `branchDataFontSize` | Font size is fixed at launch (derived: 2px smaller than labels, floor 6px) and changed only via the in-panel Font slider — not a launch config key. |
|
|
627
|
+
| `branchWidthDefault` | Branch width follows the size of the tree. |
|
|
628
|
+
| `circular` | Renamed to `layout` (use `layout: 'circular'`). |
|
|
629
|
+
| `circularDisplay` | Replaced by `layout`: `'rectangular'` \| `'circular'` \| `'unrooted'`. |
|
|
630
|
+
| `collapsedLabelLength` | The collapse feature was removed. |
|
|
631
|
+
| `collapseLabelWidth` | The collapse feature was removed. |
|
|
632
|
+
| `controls0` | The control panel is created inside the tree's own container now. |
|
|
633
|
+
| `controls0Left` | The control panel is placed against the tree; drag it to move it. |
|
|
634
|
+
| `controls0Top` | The control panel is placed against the tree; drag it to move it. |
|
|
635
|
+
| `controls1` | The visualization menus moved into the main control panel. |
|
|
636
|
+
| `controls1Left` | The visualization menus moved into the main control panel. |
|
|
637
|
+
| `controls1Top` | The visualization menus moved into the main control panel. |
|
|
638
|
+
| `controls1Width` | The control panel sizes itself. |
|
|
639
|
+
| `controlsBackgroundColor` | The control panel follows the light / dark palette. |
|
|
640
|
+
| `controlsFont` | The legend uses the same sans-serif as the rest of the interface. |
|
|
641
|
+
| `controlsFontColor` | This never had any effect; the legend follows the tree's label colour. |
|
|
642
|
+
| `controlsFontSize` | The legend has one size. |
|
|
643
|
+
| `decimalsForLinearRangeMeanValue` | No longer configurable. |
|
|
644
|
+
| `defaultFont` | Labels use the sans-serif the reader's own system renders best. |
|
|
645
|
+
| `dynahide` | On by default; use the Auto-hide Labels checkbox. |
|
|
646
|
+
| `dynamicallyAddNodeVisualizations` | Visualizations are always derived automatically from the tree now. |
|
|
647
|
+
| `enableBranchVisualizations` | Merged into `enableVisualizations`. |
|
|
648
|
+
| `enableCollapseByBranchLenghts` | The collapse feature was removed. |
|
|
649
|
+
| `enableCollapseByFeature` | The collapse feature was removed. |
|
|
650
|
+
| `enableCollapseByTaxonomyRank` | The collapse feature was removed. |
|
|
651
|
+
| `enableMsaResidueVisualizations` | Colouring by aligned residue was removed. |
|
|
652
|
+
| `enableNodeVisualizations` | Merged into `enableVisualizations`. |
|
|
653
|
+
| `enableSpecialVisualizations2` | The special visualizations were removed. |
|
|
654
|
+
| `enableSpecialVisualizations3` | The special visualizations were removed. |
|
|
655
|
+
| `enableSpecialVisualizations4` | The special visualizations were removed. |
|
|
656
|
+
| `externalNodeFontSize` | Font size is fixed at launch (11px) and changed only via the in-panel Font slider — not a launch config key. |
|
|
657
|
+
| `filterValues` | Reshape the tree's properties yourself before calling launch. |
|
|
658
|
+
| `fontSize` | One default size for every label; the font-size slider changes it. |
|
|
659
|
+
| `found0and1ColorDefault` | The search / selection colours are fixed so they stay distinguishable. |
|
|
660
|
+
| `found0ColorDefault` | The search / selection colours are fixed so they stay distinguishable. |
|
|
661
|
+
| `found1ColorDefault` | The search / selection colours are fixed so they stay distinguishable. |
|
|
662
|
+
| `groupSpecies` | This setting was never read; it did nothing. |
|
|
663
|
+
| `groupYears` | This setting was never read; it did nothing. |
|
|
664
|
+
| `initialCollapseDepth` | The collapse feature was removed. |
|
|
665
|
+
| `initialCollapseFeature` | The collapse feature was removed. |
|
|
666
|
+
| `initialLabelColorVisualization` | Choose the visualization in the Visualizations panel. |
|
|
667
|
+
| `initialNodeFillColorVisualization` | Choose the visualization in the Visualizations panel. |
|
|
668
|
+
| `internalNodeFontSize` | Font size is fixed at launch (11px) and changed only via the in-panel Font slider — not a launch config key. |
|
|
669
|
+
| `labelColorDefault` | The default label colour is fixed. |
|
|
670
|
+
| `minBranchLengthValueToShow` | No longer configurable. |
|
|
671
|
+
| `minConfidenceValueToShow` | No longer configurable. |
|
|
672
|
+
| `nameForFastaDownload` | Download names follow `treeName`. |
|
|
673
|
+
| `nameForNhDownload` | Download names follow `treeName`. |
|
|
674
|
+
| `nameForPhyloXmlDownload` | Download names follow `treeName`. |
|
|
675
|
+
| `nameForPngDownload` | Download names follow `treeName`. |
|
|
676
|
+
| `nameForSvgDownload` | Download names follow `treeName`. |
|
|
677
|
+
| `nhExportReplaceIllegalChars` | Always on; Newick cannot carry those characters. |
|
|
678
|
+
| `nodeLabelGap` | The label gap is fixed. |
|
|
679
|
+
| `nodeSizeDefault` | Node size is fixed; the Node size slider changes it. |
|
|
680
|
+
| `nodeVisualizations` | The visualization-dictionary mechanism (with its per-visualization regex matching) was removed for good; visualizations are derived automatically from the tree itself. Throws as a config key — and any old positional argument after `config` is rejected by count. |
|
|
681
|
+
| `nodeVisualizationsOpacity` | No longer configurable. |
|
|
682
|
+
| `orderTree` | Renamed to `ladderizeTree`, to match the wording used everywhere else. |
|
|
683
|
+
| `phylogram` | The tree is drawn to scale when most of its branches have a length. |
|
|
684
|
+
| `propertiesToIgnoreForNodeVisualization` | Every property the tree carries is offered; choose what to show in the panel. |
|
|
685
|
+
| `searchFieldWidth` | The search boxes size themselves to the control panel. |
|
|
686
|
+
| `searchIsCaseSensitive` | Off by default; use the Match case checkbox. |
|
|
687
|
+
| `searchIsPartial` | Each search box picks its own match mode (contains / starts with / ends with / whole word / regex). |
|
|
688
|
+
| `searchNegateResult` | This is the state of the Inverse checkbox, not an input. |
|
|
689
|
+
| `searchProperties` | Choose the property in the search box's field menu instead. |
|
|
690
|
+
| `searchUsesRegex` | Choose the `regex` match mode in the search box instead. |
|
|
691
|
+
| `selectedColorDefault` | The search / selection colours are fixed so they stay distinguishable. |
|
|
692
|
+
| `shortenNodeNames` | On by default when the tree has long node names; use the Short Names checkbox. |
|
|
693
|
+
| `showBranchColors` | Merged into the Visual Styles checkbox, like the desktop's `Visual Styles/Branch Colors`. |
|
|
694
|
+
| `showBranchColorsButton` | The Visual Styles checkbox appears when the tree has branch colours or style properties. |
|
|
695
|
+
| `showBranchEvents` | Shown when the tree has branch events. |
|
|
696
|
+
| `showBranchLengthValues` | Off by default; use the Branch Length checkbox. |
|
|
697
|
+
| `showBranchVisualizations` | Node and branch visualizations are one switch now; use the Visualizations checkbox. |
|
|
698
|
+
| `showConfidenceValues` | Shown when the tree has confidences. |
|
|
699
|
+
| `showDistributions` | Off by default. |
|
|
700
|
+
| `showDynahideButton` | Shown automatically once the tree has enough tips to need it. |
|
|
701
|
+
| `showExternalLabels` | On by default; use the Ext. Labels checkbox. |
|
|
702
|
+
| `showExternalLabelsButton` | Always shown. |
|
|
703
|
+
| `showExternalNodes` | Node shapes now appear wherever a node visualization applies. |
|
|
704
|
+
| `showExternalNodesButton` | The Ext. Nodes switch no longer exists. |
|
|
705
|
+
| `showInternalLabels` | Off by default; use the Int. Labels checkbox. |
|
|
706
|
+
| `showInternalLabelsButton` | Shown automatically when the tree has internal node data. |
|
|
707
|
+
| `showInternalNodes` | Node shapes now appear wherever a node visualization applies. |
|
|
708
|
+
| `showInternalNodesButton` | The Int. Nodes switch no longer exists. |
|
|
709
|
+
| `showNodeEvents` | Shown when the tree has node events. |
|
|
710
|
+
| `showNodeName` | Shown when the tree has node names. |
|
|
711
|
+
| `showNodeNameButton` | Shown automatically when the tree has node names. |
|
|
712
|
+
| `showNodeVisualizations` | Node and branch visualizations are one switch now; use the Visualizations checkbox. |
|
|
713
|
+
| `showSearchPropertiesButton` | Properties are searched by choosing them in a search box's field menu. |
|
|
714
|
+
| `showSequence` | Shown when the tree has sequences. |
|
|
715
|
+
| `showSequenceAccession` | Sequence labelling follows what the tree contains. |
|
|
716
|
+
| `showSequenceButton` | Shown automatically when the tree has sequences. |
|
|
717
|
+
| `showSequenceGeneSymbol` | Sequence labelling follows what the tree contains. |
|
|
718
|
+
| `showSequenceName` | Sequence labelling follows what the tree contains. |
|
|
719
|
+
| `showSequenceSymbol` | Sequence labelling follows what the tree contains. |
|
|
720
|
+
| `showShortenNodeNamesButton` | Shown automatically when the tree has long node names. |
|
|
721
|
+
| `showTaxonomy` | Shown when the tree has taxonomies. |
|
|
722
|
+
| `showTaxonomyButton` | Shown automatically when the tree has taxonomies. |
|
|
723
|
+
| `showTaxonomyCode` | Taxonomy labelling follows what the tree contains. |
|
|
724
|
+
| `showTaxonomyCommonName` | Taxonomy labelling follows what the tree contains. |
|
|
725
|
+
| `showTaxonomyRank` | Taxonomy labelling follows what the tree contains. |
|
|
726
|
+
| `showTaxonomyScientificName` | Taxonomy labelling follows what the tree contains. |
|
|
727
|
+
| `showTaxonomySynonyms` | Taxonomy labelling follows what the tree contains. |
|
|
728
|
+
| `showVisualizations` | Off by default; use the Visualizations checkbox. |
|
|
729
|
+
| `specialVisualizations` | Removed along with the enableSpecialVisualizations2/3/4 settings. Throws as a config key — and any old positional argument after `config` is rejected by count. |
|
|
730
|
+
| `textFieldHeight` | The text fields size themselves to their content. |
|
|
731
|
+
| `treeName` | The name comes from the tree file. |
|
|
732
|
+
| `unrootedDisplay` | Replaced by `layout`; use `layout: 'unrooted'`. |
|
|
733
|
+
| `useVisualStyles` | On by default; use the Visual Styles checkbox. |
|
|
734
|
+
| `valuesToIgnoreForNodeVisualization` | Every value is shown; choose what to show in the panel. |
|
|
735
|
+
| `visualizationsLegendOrientation` | The legend orientation is fixed; the legend has its own control. |
|
|
736
|
+
| `visualizationsLegendXposOrig` | Internal bookkeeping; set visualizationsLegendXpos. |
|
|
737
|
+
| `visualizationsLegendYposOrig` | Internal bookkeeping; set visualizationsLegendYpos. |
|
|
738
|
+
|
|
739
|
+
## The automatic visualization system (developer spec)
|
|
740
|
+
|
|
741
|
+
This section, together with **Value grouping** below, is written to be
|
|
742
|
+
sufficient for a skilled developer (or another Claude) to rebuild the
|
|
743
|
+
system. The division of labour is strict:
|
|
744
|
+
|
|
745
|
+
* **forester.js decides** — pure, DOM-free, Node-testable functions:
|
|
746
|
+
`visualizationCandidates(tree)` (what to offer and how),
|
|
747
|
+
`visualizationNodeValue(node, candidate)` (a node's folded value),
|
|
748
|
+
`nodeLabelProperty(tree)` (readable tip names),
|
|
749
|
+
`commonNamePrefix(tree, ref)` (shared-prefix for shortening),
|
|
750
|
+
`nodeVisualStyle(node)` (the `style:` namespace), and the `VIS_SYNONYMS`
|
|
751
|
+
dictionary.
|
|
752
|
+
* **archaeopteryx.js renders** — builds d3 scales from the descriptors,
|
|
753
|
+
owns one state object (`_vis`), the menus, the legends, and the
|
|
754
|
+
precedence chains below. It contains no classification logic.
|
|
755
|
+
|
|
756
|
+
The **Color** and **Shape** menus are filled **from the tree alone**. There is
|
|
757
|
+
nothing to configure and nothing to pass in: `launch()`'s old
|
|
758
|
+
`nodeVisualizations` argument and the `dynamicallyAddNodeVisualizations`
|
|
759
|
+
setting are gone, and passing either throws. What is worth offering is
|
|
760
|
+
decided by `forester.visualizationCandidates(tree)` and is under test
|
|
761
|
+
(`test/visualization_test.js`, 45 tests; the ten demo trees under
|
|
762
|
+
`docs/data/` are the fixtures and the executable spec — five of them real
|
|
763
|
+
ViPR / BV-BRC virus trees whose messy annotations the rules were tuned
|
|
764
|
+
against).
|
|
765
|
+
|
|
766
|
+
What gets offered, briefly:
|
|
767
|
+
|
|
768
|
+
* Candidates are taxonomy (code, scientific name, common name), sequence
|
|
769
|
+
(name, symbol, gene name), and node properties. The `style:` namespace is
|
|
770
|
+
never offered — the desktop reserves it for rendering instructions.
|
|
771
|
+
* A field must cover at least **⅔ of the external nodes** and have at least
|
|
772
|
+
2 — and fewer than all — distinct values. Identifier-like fields
|
|
773
|
+
(accessions, genome ids) are refused.
|
|
774
|
+
* Up to **20** distinct values → **Color** (one fixed, colour-vision-aware
|
|
775
|
+
palette at every cardinality). Fields with **21+ values** (hosts, species)
|
|
776
|
+
are still offered — every value coloured, desktop-style, by extending the
|
|
777
|
+
palette with lightened / darkened cycles — provided their values genuinely
|
|
778
|
+
repeat (distinct ≤ 60% of covered nodes; near-unique fields like strains
|
|
779
|
+
stay out). Their legends show the 20 most frequent values with a
|
|
780
|
+
`[+N more]` chip to expand, and they are listed last and never
|
|
781
|
+
auto-applied. Numeric fields come in three bands: up to
|
|
782
|
+
**10** distinct values default to individual colours — numbers that few are
|
|
783
|
+
usually codes, like HA/NA subtypes — **11–20** default to a viridis
|
|
784
|
+
**colour ramp**, and both of those carry a `[colors]` / `[gradient]` switch
|
|
785
|
+
in their legend; above 20 it is a ramp with no switch. Legends list numeric
|
|
786
|
+
values in numeric order by default (words sort by count).
|
|
787
|
+
* Up to **7** distinct values → also **Shape** (the seven distinct d3 symbols).
|
|
788
|
+
* A node without a value keeps the default look, and the legend names the
|
|
789
|
+
field so partial coverage is visible.
|
|
790
|
+
* Property values are **grouped for colouring after normalization** —
|
|
791
|
+
spelling variants, host/country qualifiers, and a common-animal synonym
|
|
792
|
+
dictionary. The exact algorithm is specified under **Value grouping**
|
|
793
|
+
below; raw values remain untouched everywhere else — exports, search,
|
|
794
|
+
and the node-data dialog.
|
|
795
|
+
|
|
796
|
+
The menus are ordered **best first** — categorical fields ahead of numeric
|
|
797
|
+
ramps, then by coverage × balance (the normalized entropy of the value
|
|
798
|
+
distribution), so a field that reads "Nonhuman Mammal" on 92% of its nodes
|
|
799
|
+
sits below one that actually splits the tree. **The best candidate is applied
|
|
800
|
+
automatically on load**: a tree opens coloured by its most informative field
|
|
801
|
+
rather than grey with a menu to discover. Esc resets back to that state.
|
|
802
|
+
|
|
803
|
+
**Local candidacy, stable identity.** Candidates, bands, counts, menus and
|
|
804
|
+
legends always describe the **displayed** tree: switch into a subtree (or
|
|
805
|
+
delete part of the tree) and everything is re-derived, so a field that was
|
|
806
|
+
refused on the full tree — Species at 66 values, say — is offered inside a
|
|
807
|
+
clade where it has six. A value's **colour and shape, by contrast, are
|
|
808
|
+
identities**: assigned once per launch and remembered, so nothing recolours
|
|
809
|
+
when you dive in and out, and deleting a clade never shifts the colours of
|
|
810
|
+
what survives. Numeric ramps are the exception by design — a ramp's colour is
|
|
811
|
+
position in the view's range, so a six-year subtree gets a full-width
|
|
812
|
+
gradient. Your Color/Shape choice survives a view change when its field is
|
|
813
|
+
still a candidate there; otherwise the menu returns to default (and the tree
|
|
814
|
+
falls back to `style:` colours where the file carries them). Auto-apply
|
|
815
|
+
happens only at launch, and view changes never touch any checkbox. Choosing a
|
|
816
|
+
Color also switches the Visualizations checkbox on, since one colour paints
|
|
817
|
+
both the label and the node.
|
|
818
|
+
|
|
819
|
+
### Candidate descriptors
|
|
820
|
+
|
|
821
|
+
`visualizationCandidates` returns an ordered array of plain objects:
|
|
822
|
+
|
|
823
|
+
```js
|
|
824
|
+
{ id: 'prop:vipr:Genus', // 'prop:'+ref | 'tax:code' | 'seq:name' | ...
|
|
825
|
+
kind: 'property', // 'property' | 'taxonomy' | 'sequence'
|
|
826
|
+
ref: 'vipr:Genus', // property ref; null for element slots
|
|
827
|
+
label: 'Genus', // prettified for menus and legend titles
|
|
828
|
+
numeric: false,
|
|
829
|
+
coverage: 307, total: 321, // nodes with a value / external nodes
|
|
830
|
+
values: ['Aviadenovirus', …], // one entry per GROUP (sorted; numeric fields numerically)
|
|
831
|
+
counts: {Aviadenovirus: 12, …},
|
|
832
|
+
canon: {aviadenovirus: 'Aviadenovirus', …}, // group key → display (property fields)
|
|
833
|
+
cut: null, // ';' | ':' | null (host/country qualifier)
|
|
834
|
+
score: 0.704, // coverage/total × normalized entropy
|
|
835
|
+
colorMode: 'category', // 'category' | 'range' (the DEFAULT mode)
|
|
836
|
+
switchable: false, // numeric ≤20 distinct: legend chip may flip the mode
|
|
837
|
+
wide: false, // categorical 21+ distinct (legend caps at 20)
|
|
838
|
+
shape: true } // ≤7 distinct: also offered in the Shape menu
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
Labels are prettified from refs (underscores → spaces, camelCase split,
|
|
842
|
+
lowercase words capitalized, words with capitals kept: `PANGO_Lineage_L0` →
|
|
843
|
+
"PANGO Lineage L0"); a cross-namespace label collision falls back to the
|
|
844
|
+
verbatim refs.
|
|
845
|
+
|
|
846
|
+
### Ranking and auto-apply
|
|
847
|
+
|
|
848
|
+
`score = (coverage / total) × (H / ln distinct)` where
|
|
849
|
+
`H = −Σ p·ln p` over the groups, `p = groupCount / coverage` — coverage
|
|
850
|
+
times the normalized entropy of the value distribution, so a field that
|
|
851
|
+
reads one value on 92% of nodes ranks low even at full coverage. Candidates
|
|
852
|
+
sort by **tier** (clean categorical → range → wide), then score descending,
|
|
853
|
+
ties by label case-insensitively then id. At launch the viewer applies
|
|
854
|
+
`candidates[0]` automatically — unless it is wide — and turns the
|
|
855
|
+
Visualizations checkbox on. Auto-apply happens **only** at launch.
|
|
856
|
+
|
|
857
|
+
### Palettes, scales and shapes
|
|
858
|
+
|
|
859
|
+
* **Categorical**: `VIS_COLOR_PALETTE`, exactly 20 entries — Observable10
|
|
860
|
+
(`#4269d0 #efb118 #ff725c #6cc5b0 #3ca951 #ff8ab7 #a463f2 #97bbf5
|
|
861
|
+
#9c6b4e #9498a0`) followed by each darkened (× 0.7^0.9 per RGB channel).
|
|
862
|
+
Past 20 (wide fields), `extendedPaletteColor(i)` continues it: cycle
|
|
863
|
+
`k = ⌊i/20⌋` re-uses entry `i mod 20` blended `min(0.55, 0.2·k)` toward
|
|
864
|
+
white (odd cycles) or black (even), via d3.interpolateRgb.
|
|
865
|
+
* **Ranges**: 3-stop viridis `#440154 #21908C #FDE725` on a linear scale
|
|
866
|
+
with domain `[min, mean, max]` of the view's **distinct** numeric values
|
|
867
|
+
(mean of distinct values, not of nodes).
|
|
868
|
+
* **Shapes**: `['circle','square','diamond','triangle','cross','star','wye']`
|
|
869
|
+
— the 7 genuinely distinct d3 v7 fill symbols.
|
|
870
|
+
|
|
871
|
+
### Views: local candidacy, stable identity
|
|
872
|
+
|
|
873
|
+
Candidates, bands, counts, menus and legends always describe the
|
|
874
|
+
**displayed** tree. On every view change — switch to subtree, return (whole
|
|
875
|
+
or by one), subtree deletion, Esc — `refreshVisualizations()` re-runs the
|
|
876
|
+
classifier on `displayedRoot()` (`_in_subtree ? _root : _treeData`). A field
|
|
877
|
+
refused on the full tree is offered inside a clade that earns it.
|
|
878
|
+
|
|
879
|
+
Colour and shape are **identities**, held in launch-lifetime memory maps
|
|
880
|
+
(`_vis.colorMemory` / `shapeMemory`, keyed `fieldId → normalizedValue →
|
|
881
|
+
colour`; values lowercased for property fields, verbatim for element slots).
|
|
882
|
+
First assignment wins forever; new values met in smaller views take the next
|
|
883
|
+
free palette slot (`colorNext`/`shapeNext` counters). The launch view
|
|
884
|
+
assigns its sorted domain 0,1,2,…, so first-view behaviour equals a plain
|
|
885
|
+
indexed palette. **Numeric ramps are the deliberate exception**: their
|
|
886
|
+
domain is recomputed per view (a ramp's colour is position in the view's
|
|
887
|
+
range, and a six-year subtree deserves a full-width gradient).
|
|
888
|
+
|
|
889
|
+
A selection survives a view change when its field is still a candidate;
|
|
890
|
+
otherwise the menu returns to `default` and the tree falls back down the
|
|
891
|
+
precedence chain (often to `style:` colours). View changes move no
|
|
892
|
+
checkbox. Esc re-applies the launch auto-choice only if its field still
|
|
893
|
+
exists, and clears the per-legend chip states.
|
|
894
|
+
|
|
895
|
+
### Rendering precedence
|
|
896
|
+
|
|
897
|
+
Highest first, at each paint:
|
|
898
|
+
|
|
899
|
+
* **Label colour**: search/selection highlight → active Color visualization
|
|
900
|
+
→ `style:font_color` → phyloXML branch colour → theme ink.
|
|
901
|
+
* **Node fill**: highlight → duplication/speciation event colour → active
|
|
902
|
+
Color visualization → `style:node_color` (else `style:font_color`) →
|
|
903
|
+
background.
|
|
904
|
+
* **Node outline**: darkened highlight → event colour → visualization fill
|
|
905
|
+
→ style colour → branch colour → branch default.
|
|
906
|
+
* **Node shape path**: suppressed for highlighted/event nodes; chosen Shape
|
|
907
|
+
visualization → `style:node_shape`. A node earns its default dot when a
|
|
908
|
+
Color visualization is active (and no shape was drawn), or when it
|
|
909
|
+
carries `style:node_color` — `font_color` alone paints only the label.
|
|
910
|
+
|
|
911
|
+
### Legend anatomy
|
|
912
|
+
|
|
913
|
+
One draggable SVG card per active visualization (colour, then shape,
|
|
914
|
+
stacked; both move together, positions from
|
|
915
|
+
`visualizationsLegendXpos/Ypos`). Card: background `backgroundColorDefault`
|
|
916
|
+
at 0.92 opacity, border `branchColorDefault` at 0.5, radius 5 — all theme
|
|
917
|
+
colours, so the always-light export rewrite handles them. Title row: the
|
|
918
|
+
candidate's label plus chips laid right-to-left, each shown only when
|
|
919
|
+
meaningful: sort (`[by count]` ⇄ `[A-Z]`/`[by value]`; numeric legends
|
|
920
|
+
default to value order, word legends to count order), mode
|
|
921
|
+
(`[colors]` ⇄ `[gradient]`, switchable numeric fields), expand
|
|
922
|
+
(`[+N more]` ⇄ `[fewer]`, wide fields; the cap keeps the 20 most frequent
|
|
923
|
+
under either sort order). Rows: 9px rounded swatch (or stroked shape
|
|
924
|
+
glyph), value text (ellipsized past 28 chars), count right-aligned at 0.55
|
|
925
|
+
opacity; a dashed **no value** row (total − coverage) pinned last. Gradient
|
|
926
|
+
legends: a 10px bar filled by an SVG linearGradient whose middle stop sits
|
|
927
|
+
at the mean's true position in `[min,max]`, min/max labels beneath, no
|
|
928
|
+
sort/expand chips. Text measured with a canvas 2D context in the legend's
|
|
929
|
+
font: the tree's font size floored at 11. Chip clicks stopPropagation on
|
|
930
|
+
mousedown so they do not start a drag; per-legend chip state lives in
|
|
931
|
+
`_vis.legendSortById / colorModeById / legendExpandedById` (keyed by field
|
|
932
|
+
id, surviving view changes, cleared by Esc).
|
|
933
|
+
|
|
934
|
+
### State and code map
|
|
935
|
+
|
|
936
|
+
All viewer state is one object, `_vis`, reset per launch:
|
|
937
|
+
`candidates`, `byId`, `colorId`, `shapeId` (current choices),
|
|
938
|
+
`autoColorId`, `labelRef`, `labelPrefix`, `hasStyles` (launch-frozen),
|
|
939
|
+
`legendSortById`, `colorModeById`, `legendExpandedById` (per-legend chips),
|
|
940
|
+
`colorMemory`, `colorNext`, `shapeMemory`, `shapeNext` (identity maps).
|
|
941
|
+
Key viewer functions: `initializeVisualizations` (launch),
|
|
942
|
+
`computeVisualizationCandidates(viewRoot)` (descriptors → scales),
|
|
943
|
+
`refreshVisualizations` (view change), `visualizationColorFor` /
|
|
944
|
+
`makeNodeVisShape` (per-node paint), `displayNodeName` (readable names),
|
|
945
|
+
`drawLegendCard` / `addLegends`, `populateVisualizationMenus`.
|
|
946
|
+
|
|
947
|
+
### Value grouping (normalization + synonym dictionary)
|
|
948
|
+
|
|
949
|
+
Applies to **node property values only**, and only for colouring/legends:
|
|
950
|
+
taxonomy and sequence elements are used verbatim, and node names, exports,
|
|
951
|
+
search, autocomplete and the node-data dialog always see the raw values.
|
|
952
|
+
Grouping runs **before** classification, so distinct-value counts, the
|
|
953
|
+
category/range/wide bands, and the entropy score are all computed on groups.
|
|
954
|
+
|
|
955
|
+
Each raw value maps to its **display form** by these steps, in order:
|
|
956
|
+
|
|
957
|
+
1. **Qualifier cut** — only when the ref's local name (the part after the
|
|
958
|
+
last `:` in the ref, compared case-insensitively) is exactly `host` or
|
|
959
|
+
exactly `country`. For `host`, cut at the first `;`; for `country`, at
|
|
960
|
+
the first `:`. No other refs are cut (`host_group`,
|
|
961
|
+
`isolation_country` etc. keep their full values).
|
|
962
|
+
2. **Parenthesis repair** — if a cut left an unclosed `(`, truncate at the
|
|
963
|
+
first unmatched `(`. (`Saimiri boliviensis (squirrel monkey; voucher:
|
|
964
|
+
SBB04)` → cut at `;` → repair → `Saimiri boliviensis`.)
|
|
965
|
+
3. **Spelling fold** — trim; replace every `_` with a space; collapse each
|
|
966
|
+
whitespace run to a single space; trim again (a leading/trailing `_`
|
|
967
|
+
survives the underscore replacement as a bare space, so the fold is not
|
|
968
|
+
done until this second trim). A value that becomes empty is dropped.
|
|
969
|
+
4. **Dictionary lookup** — lowercase the whole folded value and look it up
|
|
970
|
+
in the synonym table below (each canonical name matches itself too). If
|
|
971
|
+
there is no hit and the value ends in a parenthetical, retry once with
|
|
972
|
+
one trailing `(...)` removed (`Bos taurus (cattle)` → `bos taurus`). On
|
|
973
|
+
a hit, the display form is the canonical name. Matching is **whole-value
|
|
974
|
+
only, never substring** — `ferret badger` and `42-day-old pig` keep
|
|
975
|
+
their own groups.
|
|
976
|
+
|
|
977
|
+
The **group key** is the display form lowercased; values sharing a key are
|
|
978
|
+
one group (one legend row, one colour, counts summed). The legend shows the
|
|
979
|
+
group's **representative**: the canonical name for dictionary hits;
|
|
980
|
+
otherwise the group's most frequent display spelling (ties broken by
|
|
981
|
+
code-point order, ascending) with its first character uppercased.
|
|
982
|
+
|
|
983
|
+
The dictionary (`VIS_SYNONYMS` in forester.js — one constant, extend it
|
|
984
|
+
there). Synonyms are matched lowercase. **Cross-implementation contract:**
|
|
985
|
+
this dictionary, the element-slot ids (`tax:code`, `seq:name`, ...), and the
|
|
986
|
+
verbatim-for-elements rule above are carried identically on the desktop —
|
|
987
|
+
extend either side and the other, never just one.
|
|
988
|
+
|
|
989
|
+
| canonical | synonyms |
|
|
990
|
+
| --- | --- |
|
|
991
|
+
| Human | humans, homo sapiens, h. sapiens |
|
|
992
|
+
| Cow | bovine, calf, cattle, bull, heifer, bos taurus, b. taurus |
|
|
993
|
+
| Chicken | broiler chicken, broiler, hen, rooster, gallus gallus, g. gallus, gallus gallus domesticus |
|
|
994
|
+
| Mouse | house mouse, murine, mus musculus, m. musculus |
|
|
995
|
+
| Rat | brown rat, norway rat, black rat, rattus norvegicus, r. norvegicus, rattus rattus |
|
|
996
|
+
| Ferret | domestic ferret, mustela putorius furo, mustela furo, m. putorius furo |
|
|
997
|
+
| Guinea pig | cavy, domestic guinea pig, cavia porcellus, c. porcellus |
|
|
998
|
+
| Rhesus monkey | rhesus macaque, macaca mulatta, m. mulatta |
|
|
999
|
+
| Rabbit | european rabbit, oryctolagus cuniculus, o. cuniculus |
|
|
1000
|
+
| Dog | canine, canis familiaris, canis lupus familiaris, c. familiaris |
|
|
1001
|
+
| Cat | feline, domestic cat, felis catus, f. catus, felis silvestris catus |
|
|
1002
|
+
| Duck | mallard, mallard duck, domestic duck, anas platyrhynchos, a. platyrhynchos |
|
|
1003
|
+
| Pig | swine, porcine, hog, piglet, sus scrofa, s. scrofa, sus scrofa domesticus |
|
|
1004
|
+
| Horse | equine, mare, stallion, equus caballus, e. caballus |
|
|
1005
|
+
| Sheep | ovine, lamb, ewe, ovis aries, o. aries |
|
|
1006
|
+
| Goat | caprine, capra hircus, c. hircus |
|
|
1007
|
+
| Camel | dromedary, bactrian camel, camelus dromedarius, camelus bactrianus, c. dromedarius |
|
|
1008
|
+
|
|
1009
|
+
This is deliberately **display grouping, not data cleaning**: spelling and
|
|
1010
|
+
a short list of unambiguous synonyms, nothing semantic beyond it. It goes
|
|
1011
|
+
one step further than the desktop (which folds spellings and
|
|
1012
|
+
`human → Homo sapiens` only) — the dictionary, the parenthesis repair, and
|
|
1013
|
+
folding to capitalized common names (`Human`, not `Homo sapiens`) are this
|
|
1014
|
+
viewer's own choices.
|
|
1015
|
+
|
|
1016
|
+
### Readable tip names
|
|
1017
|
+
|
|
1018
|
+
Database exports often name their tips with identifiers
|
|
1019
|
+
(`PATRIC.10334.249.FJ478159…`, `11320.305060`) while carrying the readable
|
|
1020
|
+
name in a property such as `BVBRC:genome_name`. When at least 80% of the tip
|
|
1021
|
+
names look like identifiers and a `…name` property is well-covered, mostly
|
|
1022
|
+
distinct and mostly wordy, that property is **displayed as the tip label**
|
|
1023
|
+
instead (`forester.nodeLabelProperty` makes the call, under test). Readable
|
|
1024
|
+
names are never overridden, exports and the node-data dialog keep the real
|
|
1025
|
+
name, and searching Node Name still searches the real name.
|
|
1026
|
+
|
|
1027
|
+
Shortened names drop the boring part first: when every tip shares a long
|
|
1028
|
+
prefix ("Influenza A virus …"), Short Names strips it before truncating, so
|
|
1029
|
+
what survives is the part that tells the tips apart ("A/duck/V..668/2017"
|
|
1030
|
+
rather than 300 identical "Influenz.." labels).
|
|
1031
|
+
|
|
1032
|
+
Both menus live in the single control panel, above Display Data, which is where
|
|
1033
|
+
the desktop puts them.
|
|
1034
|
+
|
|
1035
|
+
### Initial label fields
|
|
1036
|
+
|
|
1037
|
+
Which of the three label checkboxes — Node Name, Taxonomy, Sequence — start
|
|
1038
|
+
checked is decided per tree by `forester.suggestLabelFields(root, extractors)`
|
|
1039
|
+
(pure, under test). The viewer hands it, per external node, exactly the text
|
|
1040
|
+
each field would print with the subfield cascade already applied (one good
|
|
1041
|
+
taxonomy identifier, one good sequence identifier, and the **displayed** node
|
|
1042
|
+
name — when the readable-tip-name substitution applies, the substituted name
|
|
1043
|
+
is what gets judged; only Short Names shortening is a later display nicety).
|
|
1044
|
+
Two rules, in order:
|
|
1045
|
+
|
|
1046
|
+
1. **Redundancy.** For each ordered pair of fields (priority: name >
|
|
1047
|
+
taxonomy > sequence), if one field's text is *contained* in the other's —
|
|
1048
|
+
compared lowercased with spaces and underscores stripped — on **≥ 90%** of
|
|
1049
|
+
the nodes carrying both (and at least 2 such nodes), the contained field
|
|
1050
|
+
starts unchecked; on mutual containment the higher-priority field is kept.
|
|
1051
|
+
This is what removes ` | Feline calicivirus` from tips already named
|
|
1052
|
+
`Feline_calicivirus|CH-JL2|…`, and ` | MOUSE` from `22_MOUSE`.
|
|
1053
|
+
2. **Length budget.** If the median length of the combined remaining label
|
|
1054
|
+
(fragments joined with `" | "`) still exceeds **50 characters**, only the
|
|
1055
|
+
single most *identifying* field stays: highest ratio of distinct
|
|
1056
|
+
(normalized) values to **all external nodes** — judged over every tip, not
|
|
1057
|
+
just the labelled ones, so a field carried by a handful of tips cannot win
|
|
1058
|
+
and leave the rest unlabelled. The top ratio is found first; every field
|
|
1059
|
+
within **0.05** of it then competes by the priority order, with one
|
|
1060
|
+
override: a lower-priority contender whose median length is under **60%**
|
|
1061
|
+
of the current pick's (substantially more economical) takes it.
|
|
1062
|
+
|
|
1063
|
+
A field with no printable values is never checked (apaf's sequences carry
|
|
1064
|
+
only domain architectures, so its Sequence box starts unchecked). The choice
|
|
1065
|
+
is logged to the console at launch, and the user can recheck anything — the
|
|
1066
|
+
rules only set the initial state.
|
|
1067
|
+
|
|
1068
|
+
### Visual styles (the desktop's `style:` namespace)
|
|
1069
|
+
|
|
1070
|
+
phyloXML written by the desktop, ViPR or BV-BRC can carry per-node rendering
|
|
1071
|
+
instructions as properties in the reserved `style:` namespace. Five are
|
|
1072
|
+
honoured: `style:font_color`, `style:node_color`, `style:node_shape`
|
|
1073
|
+
(circle / rectangle / diamond), `style:font_size` and `style:font_style`
|
|
1074
|
+
(italic / bold / bold_italic). The rest of the vocabulary (font name,
|
|
1075
|
+
`node_size`, `node_transparency`, `node_fill_type`) is not, yet.
|
|
1076
|
+
|
|
1077
|
+
The **Visual Styles** checkbox — the desktop's "Visual Styles/Branch Colors",
|
|
1078
|
+
shown when the tree carries either, on by default — turns them off and on,
|
|
1079
|
+
and gates phyloXML `<color>` branch colours too, exactly as on the desktop.
|
|
1080
|
+
An active Color visualization outranks `style:font_color`, also as on the
|
|
1081
|
+
desktop: set the Color menu to default to see the tree as its file styled it.
|
|
1082
|
+
`style:` never appears in the Color / Shape menus — it is rendering, not data.
|
|
1083
|
+
|
|
1084
|
+
### Moving the legends
|
|
1085
|
+
|
|
1086
|
+
The legends drawn over the tree are **dragged with the mouse** — grab one
|
|
1087
|
+
anywhere and both move together, keeping their stacked order. The old Vis
|
|
1088
|
+
Legend fieldset (Show / Dir / four arrows / R) is gone; so is the shift- or
|
|
1089
|
+
alt-click placement it documented. `visualizationsLegendXpos` and
|
|
1090
|
+
`visualizationsLegendYpos` still set where they start out.
|
|
1091
|
+
|
|
1092
|
+
## The layouts, alignment track and time axes (developer spec)
|
|
1093
|
+
|
|
1094
|
+
The 2026 additions beyond the visualization system, specified tightly enough
|
|
1095
|
+
to rebuild. All pure logic lives in forester.js under `npm test`; the viewer
|
|
1096
|
+
draws.
|
|
1097
|
+
|
|
1098
|
+
### The unrooted layout
|
|
1099
|
+
|
|
1100
|
+
`forester.equalAngleLayout(root, startAngle, lengthOf)` — Meacham's
|
|
1101
|
+
equal-angle rule, one pass, no daylight iterations. The root sits at (0,0)
|
|
1102
|
+
and owns `[startAngle, startAngle + 2π)`; each child receives a wedge of its
|
|
1103
|
+
parent's proportional to the external nodes it encloses, sits at its wedge's
|
|
1104
|
+
**mid-angle** at distance `lengthOf(child)`, and recurses into its own wedge.
|
|
1105
|
+
Angles are absolute screen radians (y down), inherited and subdivided — never
|
|
1106
|
+
re-referenced to the incoming branch, whose direction is implicitly the wedge
|
|
1107
|
+
midpoint. Writes `ux, uy, uangle` onto every node; returns `{maxRad}`.
|
|
1108
|
+
`startAngle` is `-π/2` (first wedge opens upward) plus the shared rotation
|
|
1109
|
+
offset. Spoke lengths: phylogram — `distToRoot` differences × a factor
|
|
1110
|
+
fitting the deepest tip into `0.42 × min(displayWidth, displayHeight)`;
|
|
1111
|
+
cladogram — one constant step per level (the desktop's integer-division bug
|
|
1112
|
+
here was fixed, not ported). Straight branch lines.
|
|
1113
|
+
|
|
1114
|
+
Circular and unrooted form the **radial family** (`radialDisplay()`): they
|
|
1115
|
+
share rotation, the label-direction flip, single-axis zoom, and the label
|
|
1116
|
+
mathematics — `spokeAngle(d)` is `uangle` in unrooted and the cluster angle
|
|
1117
|
+
minus π/2 in circular; `labelAngleDeg` rotates a label along its spoke and
|
|
1118
|
+
`labelFlip` adds 180° on the left half (`spokeAngle mod 2π ∈ (π/2, 3π/2)`).
|
|
1119
|
+
`layoutPointXY(d)` resolves a node's position in any layout for every
|
|
1120
|
+
consumer (overview dots, hit navigator, node transforms). Unrooted disables
|
|
1121
|
+
aligned phylograms and label auto-hiding, as the desktop does.
|
|
1122
|
+
|
|
1123
|
+
### The alignment track
|
|
1124
|
+
|
|
1125
|
+
Data model: per-tip `sequences[0].mol_seq = {is_aligned, value}` (the gapped
|
|
1126
|
+
row); alignment length = the **max** row length (never assume rectangular —
|
|
1127
|
+
a short row's tail reads as gaps). Gate: `showMsa` state (auto-on when
|
|
1128
|
+
`alignedMolSeqs && maxMolSeqLength > 0`) AND rectangular layout.
|
|
1129
|
+
|
|
1130
|
+
Geometry: the track reserves `MSA_TRACK_GAP(8) + band` from `_w`, where
|
|
1131
|
+
`band = clamp(viewportWidth × 0.6, 120 px, whatever leaves the tree ≥ 220
|
|
1132
|
+
px)` — budgeted from the **viewport**, not the zoomed layout width. Its right
|
|
1133
|
+
edge lands exactly on the canvas edge (the fit translates the layout by
|
|
1134
|
+
`rootOffset`, so the track anchors at `displayWidth − rootOffset − band`).
|
|
1135
|
+
Rows tile the cluster height: each shared boundary is derived **once** as the
|
|
1136
|
+
midpoint between adjacent tip rows (per-row `y ± half` rounds a pixel apart
|
|
1137
|
+
and paints a seam); a cell's width is the *next* cell's rounded left edge
|
|
1138
|
+
minus its own, so cells abut at fractional widths. Column width is fixed
|
|
1139
|
+
(7 px); letters draw when rows are ≥ 8 px tall, in monospace with black or
|
|
1140
|
+
white ink by luminance (< 140 → white); when letters are off, same-colour
|
|
1141
|
+
runs merge into single rects and gap runs into single lines — that is what
|
|
1142
|
+
keeps big trees drawable. Only a true alignment edge gets a boundary line, so
|
|
1143
|
+
a scroll cutoff is distinguishable from the end.
|
|
1144
|
+
|
|
1145
|
+
Palettes (forester, frozen, byte-matched against the desktop): seven
|
|
1146
|
+
Zappo-style amino-acid classes, one colour per base (T ≡ U), grey ambiguity;
|
|
1147
|
+
amino-vs-nucleotide judged from the residues of the first non-empty row
|
|
1148
|
+
(> 90% ACGTUN of non-gaps → nucleotide). Gap characters: `- . ~ space`.
|
|
1149
|
+
Conservation (`forester.msaConservation`, scored over the **visible window**
|
|
1150
|
+
only): identity = most-common-residue count / rows (gaps stay in the
|
|
1151
|
+
denominator); information = `(log₂K − H)/log₂K × nonGapFraction`, K = 4 or
|
|
1152
|
+
20; consensus = most common non-gap residue, ties alphabetical. The hover
|
|
1153
|
+
readout (`forester.msaResidueInfo`, `msaUngappedPosition`) names the residue
|
|
1154
|
+
(desktop vocabulary, selenocysteine and pyrrolysine included), its class
|
|
1155
|
+
(purine/pyrimidine for bases) and Kyte-Doolittle hydropathy. Scrolling: a
|
|
1156
|
+
lazily-created fixed HTML range input plus wheel-over-track, both moving
|
|
1157
|
+
`_msaColOffset`; the tree never moves.
|
|
1158
|
+
|
|
1159
|
+
### The time axes
|
|
1160
|
+
|
|
1161
|
+
Data model: per-node `date = {unit, desc, value, minimum, maximum}` —
|
|
1162
|
+
**minimum is the younger bound, maximum the older**, everywhere. Detection
|
|
1163
|
+
(`forester.timeAxisInfo`): unit sets decide (`mya ma myr(s) my ga gya bya
|
|
1164
|
+
kya million/billion years` → geologic; `year(s) yr(s) cal ce ad calendar…` →
|
|
1165
|
+
calendar); unitless values fall back to magnitude — a strict majority in
|
|
1166
|
+
[1500, 2200] reads as years, `max > 10 && min ≤ 5% of max` as ages.
|
|
1167
|
+
Non-finite values are ignored (a `1e400` in a file once hung the tick
|
|
1168
|
+
loops). Calibration: the **largest** date value is the root age (geologic)
|
|
1169
|
+
or the present (calendar) — no unit conversion is done, so a Ga-valued tree
|
|
1170
|
+
is misbanded exactly as on the desktop.
|
|
1171
|
+
|
|
1172
|
+
Rendering (phylogram + rectangular only; the layout never changes): age→x is
|
|
1173
|
+
`anchorX + (anchorAge − age) × corr` with `corr` the branch-length scale's
|
|
1174
|
+
slope, anchored at the **deepest dated tip** rather than the root — the root
|
|
1175
|
+
and its direct children carry a synthetic half-average branch length, which
|
|
1176
|
+
would shift every band. The geologic axis draws
|
|
1177
|
+
`forester.geoBandRanks(rootAge)` — the finest of Period/Epoch, Era/Period,
|
|
1178
|
+
Eon/Era that still covers the range — as two 13 px rows of ICS intervals
|
|
1179
|
+
(official colours; ink by the same luminance rule), clipped to
|
|
1180
|
+
`[youngestTipAge, rootAge]`, then a ruler with ~8 ticks at 1/2/5 × 10ᵏ steps;
|
|
1181
|
+
a fossil-only tree's youngest age is labelled even when it is not a round
|
|
1182
|
+
tick. The calendar axis is a whole-year ruler over
|
|
1183
|
+
`[present − maxDistToRoot, present]`. Uncertainty bars use
|
|
1184
|
+
`x ± (bound − value) × s` where `s` is `+corr` for ages and `−corr` for
|
|
1185
|
+
years (so the earlier bound always lands left): internal → 7 px translucent
|
|
1186
|
+
blue `rgba(70,130,220,.35)`; tips → 5 px sepia `rgba(150,100,55,.86)` with
|
|
1187
|
+
±4 px end caps. The MSA footer and the time axis occupy the same bottom
|
|
1188
|
+
strip side by side (track right, axis left), so their reserves take
|
|
1189
|
+
`max(56, 52 or 26)`, not the sum. The ICS table (`forester.geoIntervals`
|
|
1190
|
+
etc., 69 intervals, frozen) is byte-identical to the desktop's; reference:
|
|
1191
|
+
Cohen, K.M., Harper, D.A.T., Gibbard, P.L. & Car, N. (2025, updated),
|
|
1192
|
+
Episodes 48: 105-115; www.stratigraphy.org.
|
|
1193
|
+
|
|
1194
|
+
## Node selection
|
|
1195
|
+
|
|
1196
|
+
With `enableManualNodeSelection` on, the node menu gains **Select/Deselect Node**
|
|
1197
|
+
and **Select/Deselect All Ext Nodes**, and the selection is readable from outside
|
|
1198
|
+
the viewer:
|
|
1199
|
+
|
|
1200
|
+
```js
|
|
1201
|
+
var selected = archaeopteryx.getSelectedNodes(); // array of node objects
|
|
1202
|
+
```
|
|
1203
|
+
|
|
1204
|
+
Selected nodes are drawn in the selection colour, which is fixed so that it
|
|
1205
|
+
stays distinguishable from the two search colours.
|
|
1206
|
+
|
|
1207
|
+
### The "Submit Selected" button is dormant
|
|
1208
|
+
|
|
1209
|
+
There is also a **Submit Selected** button in the source, which would dispatch a
|
|
1210
|
+
`submit_selected_nodes_event` on `document` for the surrounding application to
|
|
1211
|
+
listen for. **It is not wired up**: the call that would add the button to the
|
|
1212
|
+
control panel is commented out, so the button never appears and the event is
|
|
1213
|
+
never fired. Poll `getSelectedNodes()` instead.
|
|
1214
|
+
|
|
1215
|
+
This is left as it is on purpose, rather than either finished or deleted, until
|
|
1216
|
+
there is a reason to decide one way or the other. If you are looking for the
|
|
1217
|
+
event because something upstream expects it, that is the reason — say so.
|