gui.QTLResultsPanel.java Source code

Java tutorial

Introduction

Here is the source code for gui.QTLResultsPanel.java

Source

/* Copyright (c) 2005-2016 Biomathematics and Statistics Scotland
 * http://www.bioss.ac.uk/ 
 * 
 * This file is part of TetraploidMap.
 *
 *    TetraploidMap is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    TetraploidMap 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 General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with TetraploidMap.  If not, see <http://www.gnu.org/licenses/>.
 */

package gui;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import javax.swing.DefaultListModel;
import javax.swing.JFileChooser;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.ValueMarker;
import org.jfree.chart.plot.XYPlot;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import analyses.altqtl.AltQtlDialog;
import analyses.perm.PermDialog;
import analyses.snpperm.SNPPermDialog;
import analyses.snpqtlsimple.SnpQtlSimpleDialog;
import data.OrderedResult;
import data.PermResult;
import data.QTLResult;
import data.Trait;
import doe.MsgBox;

public class QTLResultsPanel extends JPanel implements ListSelectionListener {
    private static final long serialVersionUID = -641427706834428089L;
    private OrderedResult order;
    private QTLResult qtlResult;
    private JList<Trait> traitList;
    private JList<String> modelList;
    private DefaultListModel<Trait> traitModel;
    private JSplitPane splits, simplesplit;
    private JTextArea details, lodDetails, simpleright;
    private JScrollPane simpleDetails;
    private QTLResultsToolBar toolbar;
    private ChartPanel chartPanel;
    private Trait currentTrait, prevtrait;
    private int currentTraitI = -1;
    private int prevselectedsimplemodelindex = -1;

    /** QTLResultsPanel().
     * 
     * @param qtlResult = the QTL results to show.
     * @param order = the ordered result data this QTL was created from. 
     */
    public QTLResultsPanel(QTLResult qtlResult, OrderedResult order) {
        this.qtlResult = qtlResult;
        this.order = order;

        // Trait listbox
        traitModel = new DefaultListModel<Trait>();
        for (Trait trait : qtlResult.getTraits()) {
            traitModel.addElement(trait);
        }
        traitList = new JList<Trait>(traitModel);
        traitList.addListSelectionListener(this);
        traitList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        JScrollPane sp1 = new JScrollPane(traitList);
        sp1.setPreferredSize(new Dimension(125, 50));

        // Details text box
        details = new JTextArea();
        details.setFont(new Font("Monospaced", Font.PLAIN, 11));
        details.setMargin(new Insets(2, 5, 2, 5));
        details.setEditable(false);
        details.setTabSize(6);
        JScrollPane sp4;
        if (AppFrame.tpmmode == AppFrame.TPMMODE_QTL) {
            simpleDetails = new JScrollPane();
            simpleDetails.setFont(new Font("Monospaced", Font.PLAIN, 11));
            simplesplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
            simpleright = new JTextArea();
            simpleright.setFont(new Font("Monospaced", Font.PLAIN, 11));
            simpleright.setMargin(new Insets(2, 5, 2, 5));
            simpleright.setEditable(false);
            simpleright.setTabSize(6);
            simplesplit.setRightComponent(new JScrollPane(simpleright));

            simplesplit.setLeftComponent(simpleDetails);
            sp4 = new JScrollPane(simplesplit);
        } else {
            // TPM MODE NONSNP
            simpleright = new JTextArea();
            simpleright.setFont(new Font("Monospaced", Font.PLAIN, 11));
            simpleright.setMargin(new Insets(2, 5, 2, 5));
            simpleright.setEditable(false);
            sp4 = new JScrollPane(simpleright);
        }

        lodDetails = new JTextArea();
        lodDetails.setFont(new Font("Monospaced", Font.PLAIN, 11));
        lodDetails.setMargin(new Insets(2, 5, 2, 5));
        lodDetails.setEditable(false);
        lodDetails.setTabSize(6);
        JScrollPane sp3 = new JScrollPane(lodDetails);
        JTabbedPane tabs = new JTabbedPane();
        JScrollPane sp2 = new JScrollPane(details);
        tabs.add(sp2, "Full Model");
        tabs.add(sp4, "Simple Model");
        tabs.add(sp3, "LOD Details");

        // The splitpane
        splits = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
        splits.setTopComponent(new JPanel());
        splits.setBottomComponent(tabs);
        splits.setResizeWeight(0.5);

        // pane2
        JSplitPane splits2 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
        splits2.setLeftComponent(sp1);
        splits2.setRightComponent(splits);

        setLayout(new BorderLayout());
        add(new GradientPanel("QTL Analysis Results"), BorderLayout.NORTH);
        // add(sp1, BorderLayout.WEST);
        // add(splits);
        add(splits2);
        add(toolbar = new QTLResultsToolBar(this), BorderLayout.EAST);
    }

    /** listens for change to selected trait.
     * 
     */
    public void valueChanged(ListSelectionEvent e) {
        if (e.getValueIsAdjusting())
            return;
        Trait trait = (Trait) traitList.getSelectedValue();
        if (trait != prevtrait) {
            displayTrait(trait);
            prevtrait = trait;
        }
        if (currentTraitI == -1)
            return;
        if (AppFrame.tpmmode == AppFrame.TPMMODE_NONSNP)
            return;
        int i = modelList.getSelectedIndex();
        if (i != prevselectedsimplemodelindex) {
            prevselectedsimplemodelindex = i;
            if (i < 1) {
                modelList.clearSelection();
                return;
            }
            String l = modelList.getSelectedValue().toString();
            String modelid = l.split(" ")[0];
            String info = trait.get_modelCoefficient(modelid.trim());
            if (!info.isEmpty()) {
                simpleright.setText(info);
            } else {
                modelList.clearSelection();
            }
        }

    }

    private void displayTrait(Trait trait) {
        // modelPanel.setTrait(trait);
        simpleright.setText("");
        if (trait == null) {
            details.setText("");
            lodDetails.setText("");
            splits.setTopComponent(new JPanel());
            currentTrait = null;
            currentTraitI = -1;
            toolbar.enableButtons(false, null);
            return;
        }

        StringBuffer text = new StringBuffer(1000);

        text.append("QTL position:         " + Prefs.d3.format(trait.qtlPosition) + " cM");
        if (trait.qtlEffects2 != null) {
            text.append("\t" + Prefs.d3.format(trait.qtlPosition2) + " cM");
        }
        text.append("\n");

        text.append("Maximum LOD score:    " + Prefs.d3.format(trait.maxLOD));
        if (trait.qtlEffects2 != null) {
            text.append("\t\t" + Prefs.d3.format(trait.maxLOD2));
        }
        text.append("\n");

        text.append("% variance explained: " + Prefs.d3.format(trait.varExplained));
        if (trait.qtlEffects2 != null) {
            text.append("\t\t" + Prefs.d3.format(trait.varExplained2));
        }
        text.append("\n");

        text.append("Error mean square:    " + Prefs.d3.format(trait.errMS));
        if (trait.qtlEffects2 != null) {
            text.append("\t\t" + Prefs.d3.format(trait.errMS2));
        }
        text.append("\n\n");

        if (trait.qtlEffects != null) {
            text.append("QTL Effects\n");
            if (AppFrame.tpmmode == AppFrame.TPMMODE_QTL) {
                text.append("Genotype\tMean\t\t\ts.e.");
                text.append("\n");
                String[] labels = { "Const", "M2", "M3", "M4", "M6", "M7", "M8" };
                for (int i = 0; i < 7; i++) {
                    text.append(labels[i]);
                    text.append("\t\t" + trait.qtlEffects[i]);
                    text.append("\t\t" + trait.seEffects[i]);
                    text.append("\n");
                }
            } else {
                // NONSNP
                text.append("Genotype   Mean      s.e.");
                if (trait.qtlEffects2 != null) {
                    text.append("\t\tGenotype   Mean      s.e.");
                }
                text.append("\n");

                for (int i = 0; i < 6; i++) {
                    text.append("  " + trait.qtlEffects[i]);
                    if (trait.qtlEffects2 != null && i < 2) {
                        text.append("\t\t  " + trait.qtlEffects2[i]);
                    }
                    text.append("\n");
                }
            }
        }

        // Lod details
        PermResult result = trait.getPermResult();
        if (result != null) {
            StringBuffer lod = new StringBuffer(1000);

            lod.append("90% = " + Prefs.d2.format(result.getSig90()) + "\n");
            lod.append("95% = " + Prefs.d2.format(result.getSig95()) + "\n\n");

            for (int i = 0; i < result.lodScores.length; i++) {
                if (result.lodScores[i] != 0.0) {
                    lod.append(Prefs.d2.format(result.lodScores[i]) + "\n");
                }
            }

            lodDetails.setText(lod.toString());
            lodDetails.setCaretPosition(0);
        } else {
            lodDetails.setText("");
        }

        setsimple(trait);
        details.setText(text.toString());
        currentTrait = trait;
        currentTraitI = traitList.getSelectedIndex();
        toolbar.enableButtons(true, qtlResult.getTraitFile());

        splits.setTopComponent(getChart(trait));
    }

    private void setsimple(Trait trait) {
        if (AppFrame.tpmmode == AppFrame.TPMMODE_QTL) {
            DefaultListModel<String> simplemodelModel = new DefaultListModel<String>();
            simplemodelModel.addElement("Model       Adj R^2   SIC");
            for (String l : trait.getBestSimpleModels()) {
                simplemodelModel.addElement(l);
            }
            modelList = new JList<String>(simplemodelModel);
            modelList.addListSelectionListener(this);
            modelList.setFont(new Font("Monospaced", Font.PLAIN, 11));
            modelList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
            simpleDetails = new JScrollPane(modelList);
            // simple_details.setFont(new Font("Monospaced", Font.PLAIN, 11));
            simpleDetails.setPreferredSize(new Dimension(200, 50));
            simplesplit.setLeftComponent(simpleDetails);
        } else {
            setTrait_simple(trait, simpleright);
        }
    }

    private void setTrait_simple(Trait trait, JTextArea ta) {
        if (trait == null || trait.qtlEffects == null) {
            ta.setText("");
            return;
        }

        StringBuffer text = new StringBuffer(1000);

        text.append("Model           sig     lod     R2      mean_1  se_1    mean_2  se_2\n");
        for (int i = 0; i < 10; i++) {
            String line = (i + 1) + " " + getModel(i + 1);
            while (line.length() < 15) {
                line += " ";
            }
            for (int j = 0; j < 7; j++) {
                if (j <= 5) {
                    if (trait.modelScores[i][j] >= 0) {
                        line += " ";
                    }
                    line += Prefs.d3.format(trait.modelScores[i][j]);
                } else if (trait.modelScoresExtra != null) {
                    if (trait.modelScoresExtra[i][0] >= 0) {
                        line += " ";
                    }
                    line += Prefs.d3.format(trait.modelScoresExtra[i][0]);
                } else {
                    line += "N/A";
                }
                while (line.length() < 15 + (j + 1) * 8) {
                    line += " ";
                }
            }
            text.append(line + "\n");
        }
        ta.setText(text.toString());
    }

    private JPanel getChart(Trait trait) {
        JFreeChart chart = ChartFactory.createXYLineChart(null, "Position (cM)", "LOD Score", null,
                PlotOrientation.VERTICAL, true, true, false);

        setChartData(chart, trait);

        RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
        chart.setRenderingHints(rh);
        chart.removeLegend();

        XYPlot plot = chart.getXYPlot();
        plot.setBackgroundPaint(new Color(255, 255, 220));
        plot.setDomainGridlinePaint(new Color(128, 128, 128));
        plot.setRangeGridlinePaint(new Color(128, 128, 128));

        ValueAxis axis = plot.getRangeAxis();
        if (trait.maxLOD <= 3) {
            axis.setUpperBound(3);
        }

        PermResult result = trait.getPermResult();
        if (result != null) {
            float[] dashPattern = { 5, 5 };
            BasicStroke s1 = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10, dashPattern, 0);

            ValueMarker m1 = new ValueMarker(result.getSig90(), new Color(0, 0, 60), s1, null, null, 1.0f);
            ValueMarker m2 = new ValueMarker(result.getSig95(), new Color(0, 0, 60), s1, null, null, 1.0f);

            plot.addRangeMarker(m1);
            plot.addRangeMarker(m2);

            if (result.getSig95() > trait.maxLOD && result.getSig95() >= 3) {
                axis.setUpperBound(result.getSig95() * (1.05));
            }
        }

        chartPanel = new ChartPanel(chart);
        chartPanel.setPopupMenu(null);
        return chartPanel;
    }

    private void setChartData(JFreeChart chart, Trait trait) {
        XYSeriesCollection data = new XYSeriesCollection();
        XYSeries series1 = new XYSeries("Position vs LOD Score");
        XYSeries series2 = new XYSeries("Position vs 'simple'");

        for (int i = 0; i < trait.getPositions().size(); i++) {
            series1.add(trait.getPositions().get(i), trait.getLODs().get(i));
        }

        chart.getXYPlot().setDataset(data);

        // If altQTL has been run, plot its data too
        if (trait.getLODs2() != null && trait.getLODs2().size() > 0) {
            for (int i = 0; i < trait.getPositions().size(); i++) {
                series2.add(trait.getPositions().get(i), trait.getLODs2().get(i));
            }

            data.addSeries(series2);
        }

        data.addSeries(series1);
    }

    void savePNG() {
        try {
            chartPanel.doSaveAs();
        } catch (Exception e) {
            doe.MsgBox.msg("TetraploidMap could not save the chart due to the " + "following error:\n" + e,
                    doe.MsgBox.ERR);
        }
    }

    void saveTXT() {
        JFileChooser fc = new JFileChooser();
        fc.setCurrentDirectory(new File(Prefs.gui_dir));
        fc.setDialogTitle("Save QTL Results");

        while (fc.showSaveDialog(MsgBox.frm) == JFileChooser.APPROVE_OPTION) {
            File file = fc.getSelectedFile();

            // Make sure it has an appropriate extension
            if (!file.exists()) {
                if (file.getName().indexOf(".") == -1) {
                    file = new File(file.getPath() + ".csv");
                }
            }

            // Confirm overwrite
            if (file.exists()) {
                int response = MsgBox.yesnocan(file + " already exists.\nDo " + "you want to replace it?", 1);

                if (response == JOptionPane.NO_OPTION) {
                    continue;
                } else if (response == JOptionPane.CANCEL_OPTION || response == JOptionPane.CLOSED_OPTION) {
                    return;
                }
            }

            // Otherwise it's ok to save...
            Prefs.gui_dir = "" + fc.getCurrentDirectory();
            saveTXTFile(file);
            return;
        }
    }

    void saveTXTFile(File file) {
        Trait trait = currentTrait;

        try {
            BufferedWriter out = new BufferedWriter(new FileWriter(file));

            for (int i = 0; i < trait.getPositions().size(); i++) {
                out.write(trait.getPositions().get(i) + ", " + trait.getLODs().get(i));
                if (trait.getLODs2() != null) {
                    out.write(", " + trait.getLODs2().get(i));
                }
                out.newLine();
            }

            out.close();
        } catch (Exception e) {
            doe.MsgBox.msg("TetraploidMap could not save the chart due to the " + "following error:\n" + e,
                    doe.MsgBox.ERR);
        }
    }

    private String getModel(int i) {
        switch (i) {
        case 0:
            return "Full Model";
        case 1:
            return "C1 vs rest";
        case 2:
            return "C2 vs rest";
        case 3:
            return "C3 vs rest";
        case 4:
            return "C4 vs rest";
        case 5:
            return "Q12 vs rest";
        case 6:
            return "Q13 vs rest";
        case 7:
            return "Q14 vs rest";
        case 8:
            return "Q23 vs rest";
        case 9:
            return "Q24 vs rest";
        case 10:
            return "Q34 vs rest";
        default:
            return "";
        }
    }

    void runRescan() {
        int model = getSelectedModel(false, getBestModel());
        if (model == -1)
            return;

        int cmLength = (int) order.getDistanceTotal();

        AltQtlDialog dialog = new AltQtlDialog(MsgBox.frm, qtlResult.getTraitFile(), currentTrait, model, cmLength);
        if (dialog.isOK()) {
            displayTrait(currentTrait);
            AppFrameMenuBar.aFileSave.setEnabled(true);
        }

    }

    void runSNPQTLRescan() {
        int cmLength = (int) order.getDistanceTotal();
        SnpQtlSimpleDialog dialog = new SnpQtlSimpleDialog(MsgBox.frm, currentTraitI, currentTrait, cmLength,
                qtlResult);
        if (dialog.isOK()) {
            displayTrait(currentTrait);
            AppFrameMenuBar.aFileSave.setEnabled(true);
        }

    }

    void runPerm() {
        Object[] values = { "Full Model", "Reduced Model" };
        Object selected = JOptionPane.showInputDialog(MsgBox.frm, "Please select which model to use:",
                "Select Model", JOptionPane.QUESTION_MESSAGE, null, values, values[0]);

        if (selected == null)
            return;

        // Full model or reduced?
        boolean fullModel = selected == values[0];

        int cmLength = (int) order.getDistanceTotal();
        String tName = currentTrait.getName();

        PermDialog dialog = new PermDialog(MsgBox.frm, qtlResult.getTraitFile(), fullModel, tName, cmLength);
        if (dialog.isOK()) {
            PermResult result = dialog.getResult();
            currentTrait.setPermResult(result);

            displayTrait(currentTrait);
            AppFrameMenuBar.aFileSave.setEnabled(true);
        }

    }

    void runSNPQTLPerm() {

        String tName = currentTrait.getName();

        SNPPermDialog dialog = new SNPPermDialog(MsgBox.frm, qtlResult.getTraitFile(), tName, order);
        if (dialog.isOK()) {
            PermResult result = dialog.getResult();
            currentTrait.setPermResult(result);

            displayTrait(currentTrait);
            AppFrameMenuBar.aFileSave.setEnabled(true);
        }

    }

    // Works out which of the simple models is a suitable replacement for the
    // Full Model (if any)
    private int getBestModel() {
        int bestModel = 0;
        float bestScore = 0;

        for (int i = 0; i < 10; i++) {
            float score = currentTrait.modelScores[i][0];

            if (score >= 0.05 && score > bestScore) {
                bestModel = i;
                bestScore = score;
            }
        }

        // System.out.println("Best is " + (bestModel));

        return bestModel;
    }

    // Displays a dialog on screen that allows the user to select from the
    // available models. allowZero=Full Model available, best=initial selection
    private int getSelectedModel(boolean allowZero, int best) {
        int length = allowZero ? 11 : 10;
        Object[] values = new Object[length];

        int m = allowZero ? 0 : 1;
        for (int i = 0; i < length; i++) {
            values[i] = getModel(m++);
        }

        Object selected = JOptionPane.showInputDialog(MsgBox.frm, "Please select which model to use:",
                "Select Model", JOptionPane.QUESTION_MESSAGE, null, values, values[best]);

        if (selected == null)
            return -1;

        for (int i = 0; i < length; i++) {
            if (selected == values[i]) {
                if (allowZero) {
                    return i;
                } else {
                    return i + 1;
                }
            }
        }

        return -1;
    }
}