/*
* TempestPanel.java
*
* Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard
*
* This file is part of BEAST.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership and licensing.
*
* BEAST is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* BEAST is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with BEAST; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
package dr.app.tempest;
import dr.app.gui.chart.*;
import dr.app.gui.util.LongTask;
import dr.evolution.tree.*;
import dr.evolution.tree.TreeUtils;
import dr.evolution.util.TaxonList;
import dr.math.MathUtils;
import dr.stats.DiscreteStatistics;
import dr.stats.Regression;
import dr.stats.Variate;
import dr.util.NumberFormatter;
import figtree.panel.FigTreePanel;
import figtree.treeviewer.TreePaneSelector;
import figtree.treeviewer.TreeSelectionListener;
import figtree.treeviewer.TreeViewer;
import jam.framework.Exportable;
import jam.panels.SearchPanel;
import jam.panels.SearchPanelListener;
import jam.table.TableRenderer;
import jam.toolbar.Toolbar;
import jam.toolbar.ToolbarAction;
import jam.toolbar.ToolbarButton;
import jam.util.IconUtils;
import jebl.evolution.graphs.Node;
import jebl.evolution.taxa.Taxon;
import jebl.evolution.trees.RootedTree;
import javax.swing.*;
import javax.swing.plaf.BorderUIResource;
import javax.swing.table.AbstractTableModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.PrintWriter;
import java.io.Writer;
import java.text.DecimalFormat;
import java.util.*;
import java.util.List;
/**
* @author Andrew Rambaut
* @version $Id: PriorsPanel.java,v 1.9 2006/09/05 13:29:34 rambaut Exp $
*/
public class TempestPanel extends JPanel implements Exportable {
public static final String COLOUR = "Colour...";
public static final String CLEAR_COLOURING = "Clear Colouring...";
StatisticsModel statisticsModel;
JTable statisticsTable = null;
private Tree tree = null;
private Tree currentTree = null;
private Tree bestFittingRootTree = null;
private final TempestFrame frame;
private final JTabbedPane tabbedPane = new JTabbedPane();
private final JTextArea textArea = new JTextArea();
private final JCheckBox showMRCACheck = new JCheckBox("Show ancestor traces");
// JTreeDisplay treePanel;
private final SamplesPanel samplesPanel;
private final FigTreePanel treePanel;
private SearchPanel filterPanel;
private JPopupMenu filterPopup;
JChartPanel rootToTipPanel;
JChart rootToTipChart;
ScatterPlot rootToTipPlot;
private static final boolean SHOW_NODE_DENSITY = true;
JChartPanel nodeDensityPanel;
JChart nodeDensityChart;
ScatterPlot nodeDensityPlot;
JChartPanel residualPanel;
JChart residualChart;
ScatterPlot residualPlot;
ErrorBarPlot errorBarPlot;
ParentPlot mrcaPlot;
Map<Node, Integer> pointMap = new HashMap<Node, Integer>();
Set<Integer> selectedPoints = new HashSet<Integer>();
private boolean bestFittingRoot;
private TemporalRooting.RootingFunction rootingFunction;
private TemporalRooting temporalRooting = null;
public TempestPanel(TempestFrame parent, TaxonList taxa, Tree tree) {
frame = parent;
samplesPanel = new SamplesPanel(parent, taxa);
tabbedPane.addTab("Sample Dates", samplesPanel);
statisticsModel = new StatisticsModel();
statisticsTable = new JTable(statisticsModel);
statisticsTable.getColumnModel().getColumn(0).setCellRenderer(
new TableRenderer(SwingConstants.RIGHT, new Insets(0, 4, 0, 4)));
statisticsTable.getColumnModel().getColumn(1).setCellRenderer(
new TableRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4)));
JScrollPane scrollPane = new JScrollPane(statisticsTable,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
Box controlPanel1 = new Box(BoxLayout.PAGE_AXIS);
controlPanel1.setOpaque(false);
JPanel panel3 = new JPanel(new BorderLayout(0, 0));
panel3.setOpaque(false);
rootingCheck = new JCheckBox("Best-fitting root");
panel3.add(rootingCheck, BorderLayout.CENTER);
controlPanel1.add(panel3);
final JComboBox rootingFunctionCombo = new JComboBox(TemporalRooting.RootingFunction.values());
JPanel panel4 = new JPanel(new BorderLayout(0,0));
panel4.setOpaque(false);
panel4.add(new JLabel("Function: "), BorderLayout.WEST);
panel4.add(rootingFunctionCombo, BorderLayout.CENTER);
controlPanel1.add(panel4);
JPanel panel1 = new JPanel(new BorderLayout(0, 0));
panel1.setOpaque(false);
panel1.add(scrollPane, BorderLayout.CENTER);
panel1.add(controlPanel1, BorderLayout.NORTH);
// Set up tree panel
Toolbar toolBar = new Toolbar();
toolBar.setOpaque(false);
toolBar.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.darkGray));
toolBar.setRollover(true);
toolBar.setFloatable(false);
Icon colourToolIcon = IconUtils.getIcon(this.getClass(), "images/coloursTool.png");
final ToolbarAction colourToolbarAction = new ToolbarAction("Colour", COLOUR, colourToolIcon) {
public void actionPerformed(ActionEvent e){
colourSelected();
}
};
ToolbarButton colourToolButton = new ToolbarButton(colourToolbarAction, true);
colourToolButton.setFocusable(false);
toolBar.addComponent(colourToolButton);
toolBar.addFlexibleSpace();
filterPopup = new JPopupMenu();
final ButtonGroup bg = new ButtonGroup();
boolean first = true;
for (TreeViewer.TextSearchType searchType : TreeViewer.TextSearchType.values()) {
final JCheckBoxMenuItem menuItem = new JCheckBoxMenuItem(searchType.toString());
if (first) {
menuItem.setSelected(true);
first = false;
}
filterPopup.add(menuItem);
bg.add(menuItem);
}
filterPanel = new SearchPanel("Filter", filterPopup, true);
filterPanel.setOpaque(false);
// filterPanel.getSearchText().requestFocus();
filterPanel.addSearchPanelListener(new SearchPanelListener() {
/**
* Called when the user requests a search by pressing return having
* typed a search string into the text field. If the continuousUpdate
* flag is true then this method is called when the user types into
* the text field.
*
* @param searchString the user's search string
*/
public void searchStarted(String searchString) {
Enumeration e = bg.getElements();
String value = null;
while (e.hasMoreElements()) {
AbstractButton button = (AbstractButton)e.nextElement();
if (button.isSelected()) {
value = button.getText();
}
}
for (TreeViewer.TextSearchType searchType : TreeViewer.TextSearchType.values()) {
if (searchType.toString().equals(value)) {
treePanel.getTreeViewer().selectTaxa("!name", searchType, searchString, false);
}
}
}
/**
* Called when the user presses the cancel search button or presses
* escape while the search is in focus.
*/
public void searchStopped() {
// treeViewer.clearSelectedTaxa();
}
});
JPanel panel5 = new JPanel(new FlowLayout());
panel5.setOpaque(false);
panel5.add(filterPanel);
toolBar.addComponent(panel5);
treePanel = new FigTreePanel(FigTreePanel.Style.SIMPLE);
JPanel panel2 = new JPanel(new BorderLayout(0, 0));
panel2.setOpaque(false);
panel2.add(treePanel, BorderLayout.CENTER);
panel2.add(toolBar, BorderLayout.NORTH);
tabbedPane.add("Tree", panel2);
treePanel.getTreeViewer().setSelectionMode(TreePaneSelector.SelectionMode.TAXA);
treePanel.getTreeViewer().addTreeSelectionListener(new TreeSelectionListener() {
public void selectionChanged() {
treeSelectionChanged();
}
});
rootToTipChart = new JChart(new LinearAxis(), new LinearAxis(Axis.AT_ZERO, Axis.AT_MINOR_TICK));
ChartSelector selector1 = new ChartSelector(rootToTipChart);
rootToTipPanel = new JChartPanel(rootToTipChart, "", "time", "divergence");
JPanel panel = new JPanel(new BorderLayout());
panel.add(rootToTipPanel, BorderLayout.CENTER);
panel.add(showMRCACheck, BorderLayout.SOUTH);
panel.setOpaque(false);
tabbedPane.add("Root-to-tip", panel);
residualChart = new JChart(new LinearAxis(), new LinearAxis(Axis.AT_ZERO, Axis.AT_MINOR_TICK));
ChartSelector selector2 = new ChartSelector(residualChart);
residualPanel = new JChartPanel(residualChart, "", "time", "residual");
residualPanel.setOpaque(false);
tabbedPane.add("Residuals", residualPanel);
// textArea.setEditable(false);
JPanel panel6 = new JPanel(new BorderLayout(0, 0));
panel6.setOpaque(false);
panel6.add(tabbedPane, BorderLayout.CENTER);
// panel6.add(textArea, BorderLayout.SOUTH);
if (SHOW_NODE_DENSITY) {
nodeDensityChart = new JChart(new LinearAxis(), new LinearAxis(Axis.AT_ZERO, Axis.AT_MINOR_TICK));
nodeDensityPanel = new JChartPanel(nodeDensityChart, "", "time", "node density");
JPanel panel7 = new JPanel(new BorderLayout());
panel7.add(nodeDensityPanel, BorderLayout.CENTER);
panel7.setOpaque(false);
ChartSelector selector3 = new ChartSelector(nodeDensityChart);
tabbedPane.add("Node density", panel7);
}
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, panel1, tabbedPane);
splitPane.setDividerLocation(220);
splitPane.setContinuousLayout(true);
splitPane.setBorder(BorderFactory.createEmptyBorder());
splitPane.setOpaque(false);
setOpaque(false);
setLayout(new BorderLayout(0, 0));
setBorder(new BorderUIResource.EmptyBorderUIResource(new Insets(12, 12, 12, 12)));
add(splitPane, BorderLayout.CENTER);
rootingCheck.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
setBestFittingRoot(rootingCheck.isSelected(), (TemporalRooting.RootingFunction) rootingFunctionCombo.getSelectedItem());
}
});
rootingFunctionCombo.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
setBestFittingRoot(rootingCheck.isSelected(), (TemporalRooting.RootingFunction) rootingFunctionCombo.getSelectedItem());
}
});
showMRCACheck.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
setupPanel();
}
});
setTree(tree);
}
public List<String> getSelectedTips() {
List<String> tips = new ArrayList<String>();
jebl.evolution.trees.Tree tree = treePanel.getTreeViewer().getTrees().get(0);
for (Node node : treePanel.getTreeViewer().getSelectedTips()) {
tips.add(tree.getTaxon(node).getName());
}
return tips;
}
private static Color lastColor = Color.GRAY;
private void colourSelected() {
Color color = JColorChooser.showDialog(this, "Select Colour", lastColor);
if (color != null) {
treePanel.getTreeViewer().annotateSelectedTips("!color", color);
lastColor = color;
}
setupPanel();
}
private void treeSelectionChanged() {
Set<Node> selectedTips = treePanel.getTreeViewer().getSelectedTips();
frame.getCopyAction().setEnabled(selectedTips != null && selectedTips.size() > 0);
selectedPoints = new HashSet<Integer>();
for (Node node : selectedTips) {
selectedPoints.add(pointMap.get(node));
}
if (rootToTipPlot != null) {
rootToTipPlot.setSelectedPoints(selectedPoints);
}
if (residualPlot != null) {
residualPlot.setSelectedPoints(selectedPoints);
}
if (SHOW_NODE_DENSITY && nodeDensityPlot != null) {
nodeDensityPlot.setSelectedPoints(selectedPoints);
}
selectMRCA();
}
private void plotSelectionChanged(final Set<Integer> selectedPoints) {
this.selectedPoints = selectedPoints;
Set<String> selectedTaxa = new HashSet<String>();
for (Integer i : selectedPoints) {
selectedTaxa.add(tree.getTaxon(i).toString());
}
treePanel.getTreeViewer().selectTaxa(selectedTaxa);
selectMRCA();
}
private void selectMRCA() {
if (mrcaPlot == null) return;
if (selectedPoints != null && selectedPoints.size() > 0) {
Set<String> selectedTaxa = new HashSet<String>();
for (Integer i : selectedPoints) {
selectedTaxa.add(tree.getTaxon(i).toString());
}
Regression r = temporalRooting.getRootToTipRegression(currentTree);
NodeRef mrca = TreeUtils.getCommonAncestorNode(currentTree, selectedTaxa);
double mrcaDistance1 = temporalRooting.getRootToTipDistance(currentTree, mrca);
double mrcaTime1 = r.getX(mrcaDistance1);
if (tree.isExternal(mrca)) {
mrca = tree.getParent(mrca);
}
double mrcaDistance = temporalRooting.getRootToTipDistance(currentTree, mrca);
double mrcaTime = r.getX(mrcaDistance);
mrcaPlot.setSelectedPoints(selectedPoints, mrcaTime, mrcaDistance);
} else {
mrcaPlot.clearSelection();
}
repaint();
}
public void timeScaleChanged() {
bestFittingRootTree = null;
if (rootingCheck.isSelected()) {
rootingCheck.setSelected(false);
} else {
setupPanel();
}
}
public JComponent getExportableComponent() {
return (JComponent) tabbedPane.getSelectedComponent();
}
public void setTree(Tree tree) {
this.tree = tree;
setupPanel();
}
public void setBestFittingRoot(boolean bestFittingRoot, final TemporalRooting.RootingFunction rootingFunction) {
this.bestFittingRoot = bestFittingRoot;
if (this.rootingFunction != rootingFunction) {
bestFittingRootTree = null;
this.rootingFunction = rootingFunction;
}
if (this.bestFittingRoot && bestFittingRootTree == null) {
findRoot();
}
setupPanel();
}
public Tree getTree() {
return tree;
}
public Tree getTreeAsViewed() {
return currentTree;
}
public void writeDataFile(Writer writer) {
PrintWriter pw = new PrintWriter(writer);
String labels[] = temporalRooting.getTipLabels(currentTree);
double yValues[] = temporalRooting.getRootToTipDistances(currentTree);
if (temporalRooting.isContemporaneous()) {
double meanY = DiscreteStatistics.mean(yValues);
pw.println("tip\tdistance\tdeviation");
for (int i = 0; i < yValues.length; i++) {
pw.println(labels[i] + "\t" + "\t" + yValues[i] + "\t" + (yValues[i] - meanY));
}
} else {
double xValues[] = temporalRooting.getTipDates(currentTree);
Regression r = temporalRooting.getRootToTipRegression(currentTree);
double[] residuals = temporalRooting.getRootToTipResiduals(currentTree, r);
pw.println("tip\tdate\tdistance\tresidual");
for (int i = 0; i < xValues.length; i++) {
pw.println(labels[i] + "\t" + xValues[i] + "\t" + yValues[i] + "\t" + residuals[i]);
}
}
}
public void setupPanel() {
StringBuilder sb = new StringBuilder();
NumberFormatter nf = new NumberFormatter(6);
if (tree != null) {
temporalRooting = new TemporalRooting(tree);
currentTree = this.tree;
if (bestFittingRoot && bestFittingRootTree != null) {
currentTree = bestFittingRootTree;
sb.append("Best-fitting root");
} else {
sb.append("User root");
}
if (temporalRooting.isContemporaneous()) {
if (tabbedPane.getSelectedIndex() == 2) {
tabbedPane.setSelectedIndex(1);
}
tabbedPane.setEnabledAt(2, false);
} else {
tabbedPane.setEnabledAt(2, true);
}
RootedTree jtree = dr.evolution.tree.TreeUtils.asJeblTree(currentTree);
List<Color> colours = new ArrayList<Color>();
for (Node tip : jtree.getExternalNodes()) {
Taxon taxon = jtree.getTaxon(tip);
colours.add((Color)taxon.getAttribute("!color"));
}
if (temporalRooting.isContemporaneous()) {
double[] dv = temporalRooting.getRootToTipDistances(currentTree);
List<Double> values = new ArrayList<Double>();
for (double d : dv) {
values.add(d);
}
rootToTipChart.removeAllPlots();
NumericalDensityPlot dp = new NumericalDensityPlot(values, 20, null);
dp.setLineColor(new Color(9, 70, 15));
double yOffset = (Double) dp.getYData().getMax() / 2;
List<Double> dummyValues = new ArrayList<Double>();
for (int i = 0; i < values.size(); i++) {
// add a random y offset to give some visual spread
double y = MathUtils.nextGaussian() * ((Double) dp.getYData().getMax() * 0.05);
dummyValues.add(yOffset + y);
}
rootToTipPlot = new ScatterPlot(values, dummyValues);
rootToTipPlot.setColours(colours);
rootToTipPlot.setMarkStyle(Plot.CIRCLE_MARK, 8, new BasicStroke(0.0F), new Color(44, 44, 44), new Color(129, 149, 149));
rootToTipPlot.setHilightedMarkStyle(new BasicStroke(0.5F), new Color(44, 44, 44), UIManager.getColor("List.selectionBackground"));
rootToTipPlot.addListener(new Plot.Adaptor() {
@Override
public void markClicked(int index, double x, double y, boolean isShiftDown) {
rootToTipPlot.selectPoint(index, isShiftDown);
}
public void selectionChanged(final Set<Integer> selectedPoints) {
plotSelectionChanged(selectedPoints);
}
});
rootToTipChart.addPlot(rootToTipPlot);
rootToTipChart.addPlot(dp);
rootToTipPanel.setXAxisTitle("root-to-tip divergence");
rootToTipPanel.setYAxisTitle("proportion");
residualChart.removeAllPlots();
sb.append(", contemporaneous tips");
sb.append(", mean root-tip distance: " + nf.format(DiscreteStatistics.mean(dv)));
sb.append(", coefficient of variation: " + nf.format(DiscreteStatistics.stdev(dv) / DiscreteStatistics.mean(dv)));
sb.append(", stdev: " + nf.format(DiscreteStatistics.stdev(dv)));
sb.append(", variance: " + nf.format(DiscreteStatistics.variance(dv)));
showMRCACheck.setVisible(false);
} else {
Regression r = temporalRooting.getRootToTipRegression(currentTree);
double[] residuals = temporalRooting.getRootToTipResiduals(currentTree, r);
pointMap.clear();
for (int i = 0; i < currentTree.getExternalNodeCount(); i++) {
NodeRef tip = currentTree.getExternalNode(i);
Node node = jtree.getNode(Taxon.getTaxon(currentTree.getNodeTaxon(tip).getId()));
node.setAttribute("residual", residuals[i]);
pointMap.put(node, i);
}
rootToTipChart.removeAllPlots();
if (showMRCACheck.isSelected()) {
double[] dv = temporalRooting.getParentRootToTipDistances(currentTree);
List<Double> parentDistances = new ArrayList<Double>();
for (int i = 0; i < dv.length; i++) {
parentDistances.add(i, dv[i]);
}
List<Double> parentTimes = new ArrayList<Double>();
for (int i = 0; i < parentDistances.size(); i++) {
parentTimes.add(i, r.getX(parentDistances.get(i)));
}
mrcaPlot = new ParentPlot(r.getXData(), r.getYData(), parentTimes, parentDistances);
mrcaPlot.setLineColor(new Color(105, 202, 105));
mrcaPlot.setLineStroke(new BasicStroke(0.5F));
rootToTipChart.addPlot(mrcaPlot);
}
if (true) {
double[] datePrecisions = temporalRooting.getTipDatePrecisions(currentTree);
Variate.D ed = new Variate.D();
for (int i = 0; i < datePrecisions.length; i++) {
ed.add(datePrecisions[i]);
}
errorBarPlot = new ErrorBarPlot(ErrorBarPlot.Orientation.HORIZONTAL, r.getXData(), r.getYData(), ed);
errorBarPlot.setLineColor(new Color(44, 44, 44));
errorBarPlot.setLineStroke(new BasicStroke(1.0F));
rootToTipChart.addPlot(errorBarPlot);
}
rootToTipPlot = new ScatterPlot(r.getXData(), r.getYData());
rootToTipPlot.addListener(new Plot.Adaptor() {
public void selectionChanged(final Set<Integer> selectedPoints) {
plotSelectionChanged(selectedPoints);
}
});
rootToTipPlot.setColours(colours);
rootToTipPlot.setMarkStyle(Plot.CIRCLE_MARK, 8, new BasicStroke(0.0F), new Color(44, 44, 44), new Color(129, 149, 149));
rootToTipPlot.setHilightedMarkStyle(new BasicStroke(0.5F), new Color(44, 44, 44), UIManager.getColor("List.selectionBackground"));
rootToTipChart.addPlot(rootToTipPlot);
rootToTipChart.addPlot(new RegressionPlot(r));
rootToTipChart.getXAxis().addRange(r.getXIntercept(), (Double) r.getXData().getMax());
rootToTipPanel.setXAxisTitle("time");
rootToTipPanel.setYAxisTitle("root-to-tip divergence");
residualChart.removeAllPlots();
Variate.D values = (Variate.D) r.getYResidualData();
NumericalDensityPlot dp = new NumericalDensityPlot(values, 20);
dp.setLineColor(new Color(103, 128, 144));
double yOffset = (Double) dp.getYData().getMax() / 2;
Double[] dummyValues = new Double[values.getCount()];
for (int i = 0; i < dummyValues.length; i++) {
// add a random y offset to give some visual spread
double y = MathUtils.nextGaussian() * ((Double) dp.getYData().getMax() * 0.05);
dummyValues[i] = yOffset + y;
}
Variate.D yOffsetValues = new Variate.D(dummyValues);
residualPlot = new ScatterPlot(values, yOffsetValues);
residualPlot.addListener(new Plot.Adaptor() {
@Override
public void markClicked(int index, double x, double y, boolean isShiftDown) {
rootToTipPlot.selectPoint(index, isShiftDown);
}
@Override
public void selectionChanged(final Set<Integer> selectedPoints) {
plotSelectionChanged(selectedPoints);
}
});
residualPlot.setColours(colours);
residualPlot.setMarkStyle(Plot.CIRCLE_MARK, 8, new BasicStroke(0.0F), new Color(44, 44, 44), new Color(129, 149, 149));
residualPlot.setHilightedMarkStyle(new BasicStroke(0.5F), new Color(44, 44, 44), UIManager.getColor("List.selectionBackground"));
residualChart.addPlot(residualPlot);
residualChart.addPlot(dp);
residualPanel.setXAxisTitle("residual");
residualPanel.setYAxisTitle("proportion");
// residualChart.removeAllPlots();
// residualPlot = new ScatterPlot(r.getXData(), r.getYResidualData());
// residualPlot.addListener(new Plot.Adaptor() {
// public void selectionChanged(final Set<Integer> selectedPoints) {
// plotSelectionChanged(selectedPoints);
// }
// });
// residualChart.addPlot(residualPlot);
// residualPanel.setXAxisTitle("residual");
// residualPanel.setYAxisTitle("proportion");
if (SHOW_NODE_DENSITY) {
Regression r2 = temporalRooting.getNodeDensityRegression(currentTree);
nodeDensityChart.removeAllPlots();
nodeDensityPlot = new ScatterPlot(r2.getXData(), r2.getYData());
nodeDensityPlot.addListener(new Plot.Adaptor() {
public void selectionChanged(final Set<Integer> selectedPoints) {
plotSelectionChanged(selectedPoints);
}
});
nodeDensityPlot.setColours(colours);
nodeDensityPlot.setMarkStyle(Plot.CIRCLE_MARK, 8, new BasicStroke(0.0F), new Color(44, 44, 44), new Color(129, 149, 149));
nodeDensityPlot.setHilightedMarkStyle(new BasicStroke(0.5F), new Color(44, 44, 44), UIManager.getColor("List.selectionBackground"));
nodeDensityChart.addPlot(nodeDensityPlot);
nodeDensityChart.addPlot(new RegressionPlot(r2));
nodeDensityChart.getXAxis().addRange(r2.getXIntercept(), (Double) r2.getXData().getMax());
nodeDensityPanel.setXAxisTitle("time");
nodeDensityPanel.setYAxisTitle("node density");
}
sb.append(", dated tips");
sb.append(", date range: " + nf.format(temporalRooting.getDateRange()));
sb.append(", slope (rate): " + nf.format(r.getGradient()));
sb.append(", x-intercept (TMRCA): " + nf.format(r.getXIntercept()));
sb.append(", corr. coeff: " + nf.format(r.getCorrelationCoefficient()));
sb.append(", R^2: " + nf.format(r.getRSquared()));
showMRCACheck.setVisible(true);
}
treePanel.setTree(jtree);
treePanel.setColourBy("residual");
} else {
treePanel.setTree(null);
rootToTipChart.removeAllPlots();
sb.append("No trees loaded");
}
textArea.setText(sb.toString());
statisticsModel.fireTableStructureChanged();
repaint();
}
private javax.swing.Timer timer = null;
private void findRoot() {
// bestFittingRootTree = temporalRooting.findRoot(tree);
final FindRootTask analyseTask = new FindRootTask();
final ProgressMonitor progressMonitor = new ProgressMonitor(frame,
"Finding best-fit root",
"", 0, tree.getNodeCount());
progressMonitor.setMillisToPopup(0);
progressMonitor.setMillisToDecideToPopup(0);
timer = new javax.swing.Timer(10, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
progressMonitor.setProgress(analyseTask.getCurrent());
if (progressMonitor.isCanceled() || analyseTask.done()) {
progressMonitor.close();
analyseTask.stop();
timer.stop();
}
}
});
analyseTask.go();
timer.start();
}
class FindRootTask extends LongTask {
public FindRootTask() {
}
public int getCurrent() {
return temporalRooting.getCurrentRootBranch();
}
public int getLengthOfTask() {
return temporalRooting.getTotalRootBranches();
}
public String getDescription() {
return "Calculating demographic reconstruction...";
}
public String getMessage() {
return null;
}
public Object doWork() {
bestFittingRootTree = temporalRooting.findRoot(tree, rootingFunction);
EventQueue.invokeLater(
new Runnable() {
public void run() {
setupPanel();
}
});
return null;
}
}
public TemporalRooting getTemporalRooting() {
return temporalRooting;
}
class StatisticsModel extends AbstractTableModel {
String[] rowNamesDatedTips = {"Date range", "Slope (rate)", "X-Intercept (TMRCA)", "Correlation Coefficient", "R squared", "Residual Mean Squared"};
String[] rowNamesContemporaneousTips = {"Mean root-tip", "Coefficient of variation", "Stdev", "Variance"};
private DecimalFormat formatter = new DecimalFormat("0.####E0");
private DecimalFormat formatter2 = new DecimalFormat("####0.####");
public StatisticsModel() {
}
public int getColumnCount() {
return 2;
}
public int getRowCount() {
if (temporalRooting == null) {
return 0;
} else if (temporalRooting.isContemporaneous()) {
return rowNamesContemporaneousTips.length;
} else {
return rowNamesDatedTips.length;
}
}
public Object getValueAt(int row, int col) {
double value = 0;
if (temporalRooting.isContemporaneous()) {
if (col == 0) {
return rowNamesContemporaneousTips[row];
}
double values[] = temporalRooting.getRootToTipDistances(currentTree);
switch (row) {
case 0:
value = DiscreteStatistics.mean(values);
break;
case 1:
value = DiscreteStatistics.stdev(values) / DiscreteStatistics.mean(values);
break;
case 2:
value = DiscreteStatistics.stdev(values);
break;
case 3:
value = DiscreteStatistics.variance(values);
break;
}
} else {
Regression r = temporalRooting.getRootToTipRegression(currentTree);
if (col == 0) {
return rowNamesDatedTips[row];
}
switch (row) {
case 0:
value = temporalRooting.getDateRange();
break;
case 1:
value = r.getGradient();
break;
case 2:
value = r.getXIntercept();
break;
case 3:
value = r.getCorrelationCoefficient();
break;
case 4:
value = r.getRSquared();
break;
case 5:
value = r.getResidualMeanSquared();
break;
}
}
if (value > 0 && (Math.abs(value) < 0.1 || Math.abs(value) >= 100000.0)) {
return formatter.format(value);
} else return formatter2.format(value);
}
public String getColumnName(int column) {
if (column > 0) {
return "";
}
if (temporalRooting == null) {
return "No tree loaded";
} else if (temporalRooting.isContemporaneous()) {
return "Contemporaneous Tips";
} else {
return "Dated Tips";
}
}
public Class getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append(getColumnName(0));
for (int j = 1; j < getColumnCount(); j++) {
buffer.append("\t");
buffer.append(getColumnName(j));
}
buffer.append("\n");
for (int i = 0; i < getRowCount(); i++) {
buffer.append(getValueAt(i, 0));
for (int j = 1; j < getColumnCount(); j++) {
buffer.append("\t");
buffer.append(getValueAt(i, j));
}
buffer.append("\n");
}
return buffer.toString();
}
}
private JCheckBox rootingCheck;
}