gui.GraphsPanel.java Source code

Java tutorial

Introduction

Here is the source code for gui.GraphsPanel.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.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import org.apache.commons.collections15.Factory;
import org.apache.commons.collections15.Transformer;
import data.Cluster;
import data.Dendrogram.DenNode;
import edu.uci.ics.jung.algorithms.layout.BalloonLayout;
import edu.uci.ics.jung.algorithms.layout.PolarPoint;
import edu.uci.ics.jung.algorithms.layout.RadialTreeLayout;
import edu.uci.ics.jung.graph.Forest;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
import edu.uci.ics.jung.visualization.Layer;
import edu.uci.ics.jung.visualization.VisualizationImageServer;
import edu.uci.ics.jung.visualization.VisualizationServer;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.CrossoverScalingControl;
import edu.uci.ics.jung.visualization.control.EditingModalGraphMouse;
import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
import edu.uci.ics.jung.visualization.control.ScalingControl;
import edu.uci.ics.jung.visualization.decorators.EdgeShape;
import edu.uci.ics.jung.visualization.transform.MutableTransformer;
import edu.uci.ics.jung.visualization.transform.MutableTransformerDecorator;

/** GraphsPanel().
 * 
 * <p>TV: This class is called from DendrogramsNode in order to construct the panel which
 * displays the Dendrogram and the DendrogramChart on a DendrogramCanvas. This panel is
 * displayed when the option "Dendrograms" is selected in the NavPanel.
 */
public class GraphsPanel extends JPanel {

    private static final long serialVersionUID = -4147730595796800970L;
    private Forest<TreeNode, TreeNode> graph;
    private VisualizationViewer<TreeNode, TreeNode> vv;
    private VisualizationServer.Paintable balloonrings;
    private VisualizationServer.Paintable radialrings;
    private BalloonLayout<TreeNode, TreeNode> balloonLayout;
    private RadialTreeLayout<TreeNode, TreeNode> radialLayout;
    private Float thresh = 0.5f;
    private Float sensitivity = 0.5f;
    private JLabel label;
    public GraphZoomScrollPane zoomScrollPane;
    GraphToolBar toolbar;
    Cluster cluster;
    public ScalingControl scaler = new CrossoverScalingControl();

    /** TV constructor for DendrogramsPanel to be called when clustering SNP marker data.
     * 
     */
    public GraphsPanel(Cluster cluster, int tpmmode) {
        this.cluster = cluster;
        graph = cluster.getGraph();

        final Factory<TreeNode> vertexFactory = new Factory<TreeNode>() {
            TreeNode treeVertice;

            public TreeNode create() {
                return treeVertice;
            }
        };
        final Factory<TreeNode> edgeFactory = new Factory<TreeNode>() {
            TreeNode treeEdge;

            public TreeNode create() {
                return treeEdge;
            }
        };

        balloonLayout = new BalloonLayout<TreeNode, TreeNode>(graph);
        radialLayout = new RadialTreeLayout<TreeNode, TreeNode>((Forest<TreeNode, TreeNode>) graph, 10000, 10000);
        vv = new VisualizationViewer<TreeNode, TreeNode>(radialLayout);
        vv.setBackground(Color.white);
        vv.setVertexToolTipTransformer(vertexString);
        vv.setEdgeToolTipTransformer(edgeString);
        vv.getRenderContext().setArrowFillPaintTransformer(arrowFillColor);
        vv.getRenderContext().setArrowDrawPaintTransformer(arrowColor);
        vv.getRenderContext().setEdgeDrawPaintTransformer(edgeColor);

        balloonrings = new BalloonRings(balloonLayout);
        radialrings = new RadialRings(radialLayout);

        EditingModalGraphMouse<TreeNode, TreeNode> gm = new EditingModalGraphMouse<TreeNode, TreeNode>(
                vv.getRenderContext(), vertexFactory, edgeFactory);
        gm.add(new ReEditingPopupGraphMousePlugin<TreeNode, TreeNode>(vertexFactory, edgeFactory));
        gm.setMode(ModalGraphMouse.Mode.TRANSFORMING);
        vv.setGraphMouse(gm);

        vv.getRenderContext().setVertexFillPaintTransformer(vertexColor);
        vv.getRenderContext().setVertexShapeTransformer(vertexSize);
        vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.Line<TreeNode, TreeNode>());

        // final GraphZoomScrollPane
        zoomScrollPane = new GraphZoomScrollPane(this.vv);
        this.setLayout(new BorderLayout());
        this.add(zoomScrollPane, BorderLayout.CENTER);

        this.toolbar = new GraphToolBar(this);// , canvas, chart);
        this.add(toolbar, BorderLayout.EAST);

        this.label = new JLabel("<html><table><tr><td> Similarity: " + Prefs.d3.format(this.getThreshold())
                + "</td><td> Groups: " + cluster.getAvLnkDendrogram().getGroupCount(this.getThreshold())
                + "</td><td>        </td><td> Distinctiveness: " + Prefs.d3.format(this.getSensitivity())
                + "</td><td> Subgroups: " + cluster.getAvLnkDendrogram().getWeakEdgeCount(this.getSensitivity())
                + "</td></tr></table></html>");
        this.label.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
        this.add(label, BorderLayout.SOUTH);

        this.setVisible(true);
    }

    // TV vertex to String transformer for displaying ToolTip on Mouse Over
    Transformer<TreeNode, String> vertexString = new Transformer<TreeNode, String>() {
        public String transform(TreeNode i) {
            DenNode dNode = (DenNode) ((DefaultMutableTreeNode) i).getUserObject();
            if (i.isLeaf()) {
                return "(" + dNode.getCMarker().marker.getPrefix() + ") : " + dNode.getCMarker().marker.getName();
            } else {
                return "similarity = " + dNode.getSimilarity();
            }
        }
    };

    // TV edge to String transformer for displaying ToolTip on Mouse Over
    Transformer<TreeNode, String> edgeString = new Transformer<TreeNode, String>() {
        public String transform(TreeNode i) {
            DenNode dNode = (DenNode) ((DefaultMutableTreeNode) i).getUserObject();

            DenNode pdNode = (DenNode) ((DefaultMutableTreeNode) i.getParent()).getUserObject();

            if (i.isLeaf()) {
                return "(" + dNode.getCMarker().marker.getPrefix() + ") : " + dNode.getCMarker().marker.getName();
            } else {
                return "distinctiveness = " + (dNode.getSimilarity() - pdNode.getSimilarity());
            }
        }
    };

    Transformer<TreeNode, Paint> arrowColor = new Transformer<TreeNode, Paint>() {
        public Paint transform(TreeNode i) {
            return Color.LIGHT_GRAY;
        }
    };
    Transformer<TreeNode, Paint> arrowFillColor = new Transformer<TreeNode, Paint>() {
        public Paint transform(TreeNode i) {
            return Color.LIGHT_GRAY;
        }
    };

    /** TODO: re-implement the algorithm from vertexColor.
     * Vertice Coloring algorithm executed before the vertexColor Transformer is
     * called
     * TV same algorithm initially set as a vertexColor Transformer COPY FROM
     * PROCESSCLUSTERRESULTS
     */
    public void colorizeTree(TreeNode root, float threshold) {
        // System.out.println("___GraphsPanel: COLORIZE TREE!");
        Color customColor = Color.WHITE;

        // int groupCount =
        // cluster.getAvLnkDendrogram().getGroupCount(threshold);
        // int counter = groupCount;

        // traverese the tree
        // Enumeration en =
        // ((DefaultMutableTreeNode)root).depthFirstEnumeration();
        @SuppressWarnings("unchecked")
        Enumeration<DefaultMutableTreeNode> en = ((DefaultMutableTreeNode) root).breadthFirstEnumeration();
        while (en.hasMoreElements()) {
            // Unfortunately the enumeration isn't genericised so we need to
            // downcast
            // when calling nextElement():
            DefaultMutableTreeNode node = en.nextElement();
            DenNode dNode = (DenNode) node.getUserObject();
            Integer level = node.getLevel();

            if (level == 0) {
                dNode.setColor(customColor);
            } else if (level != 0) {
                DenNode pdNode = (DenNode) ((DefaultMutableTreeNode) (node.getParent())).getUserObject();
                if ((dNode.getSimilarity() > threshold) && (pdNode.getColor().equals(Color.WHITE))) {
                    dNode.setColor(
                            Color.getHSBColor((float) (Math.cos(dNode.getSimilarity() * 90) * 360), 0.8f, 0.8f));
                } else {
                    dNode.setColor(pdNode.getColor());
                }
                // TV
                if (((dNode.getSimilarity() - pdNode.getSimilarity()) > this.getSensitivity())) {
                    dNode.setEdgeColor(Color.BLACK);
                }
            }
        }
    }

    /** TV method which returns a unique color every time it is called.
     * 
     */
    public Color getUniqueColor(int colorSeed) {
        Random r = new Random(colorSeed);
        Color c = new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255));
        return c;
    }

    /** detectWeakEdges().
     * 
     */
    public void detectWeakEdges(TreeNode root, float sensitivity) {
        Color customColor = Color.WHITE;

        // TV get a snapshot of original colors for each node with singleton
        Color prevColor = null;

        @SuppressWarnings("unchecked")
        Enumeration<DefaultMutableTreeNode> en = ((DefaultMutableTreeNode) root).breadthFirstEnumeration();
        while (en.hasMoreElements()) {
            DefaultMutableTreeNode node = en.nextElement();
            DenNode dNode = (DenNode) node.getUserObject();
            Integer level = node.getLevel();

            if (prevColor == null) {
                prevColor = dNode.getColor();
            }

            if (level == 0) {
                dNode.setColor(customColor);
            } else if (level != 0) {
                DenNode pdNode = (DenNode) ((DefaultMutableTreeNode) (node.getParent())).getUserObject();
                if ((dNode.getSimilarity() > this.getThreshold()) && (pdNode.getColor().equals(Color.WHITE))) {
                    dNode.setColor(
                            Color.getHSBColor((float) (Math.cos(dNode.getSimilarity() * 90) * 360), 0.8f, 0.8f));
                } else {
                    dNode.setColor(pdNode.getColor());
                }
                dNode.resetEdgeColor();
                if (((dNode.getSimilarity() - pdNode.getSimilarity()) > sensitivity)) {
                    dNode.setEdgeColor(Color.RED);
                }
            }
        }
    }

    Transformer<TreeNode, Paint> edgeColor = new Transformer<TreeNode, Paint>() {
        public Paint transform(TreeNode i) {
            DenNode dNode = (DenNode) ((DefaultMutableTreeNode) i).getUserObject();
            return dNode.getEdgeColor();
        }
    };

    /*
     * Transforms the shape of the edge arrow Transformer<TreeNode, Stroke>
     * edgeShape = new Transformer<TreeNode, Stroke>() { public Stroke
     * transform(TreeNode i) { DenNode dNode = (DenNode)
     * ((DefaultMutableTreeNode)i).getUserObject(); return
     * dNode.getEdgeStroke(); } };
     */

    // TV vertex to paint transformer
    /*
     * DONE: Write TV Algorithm for Visual Clustering with the following steps:
     * 1. Initially assume 16 clusters for level==4 2. Merge clusters that have
     * the same parent and its similarity > 0.5 (or a user input value) 3.
     * Repeat step 2 until no more merging occurs. 4. Paint the new clusters
     * with the same color
     * 
     * RRTODO: First apply transformation to all nodes and in the end return the
     * color ???? RRTODO: everytime initialize the graph so that all dNodes are
     * set to white ???? TODO: make the Transformer to complete faster by
     * correcting the false recursive behaviour
     */
    Transformer<TreeNode, Paint> vertexColor = new Transformer<TreeNode, Paint>() {
        public Paint transform(TreeNode i) {
            DenNode dNode = (DenNode) ((DefaultMutableTreeNode) i).getUserObject();
            return dNode.getColor();
        }
    };

    Transformer<TreeNode, Shape> vertexSize = new Transformer<TreeNode, Shape>() {
        public Shape transform(TreeNode i) {

            DenNode dNode = (DenNode) ((DefaultMutableTreeNode) i).getUserObject();
            Ellipse2D circle = new Ellipse2D.Double(-5, -5, 10, 10);
            Rectangle2D rectang = new Rectangle2D.Double(-5, -5, 10, 10);
            if (!i.isLeaf()) {
                return AffineTransform
                        .getScaleInstance(3 - 2 * dNode.getSimilarity(), 3 - 2 * dNode.getSimilarity())
                        .createTransformedShape(circle);
            } else if (i.isLeaf()) {
                return AffineTransform.getScaleInstance(3, 3).createTransformedShape(rectang);
            } else {
                return circle;
            }
        }
    };
    // TV new stroke Transformer for the edges
    float[] dash = { 10.0f };
    final Stroke edgeStroke = new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, dash,
            0.0f);
    Transformer<String, Stroke> edgeStrokeTransformer = new Transformer<String, Stroke>() {
        public Stroke transform(String s) {
            return edgeStroke;
        }
    };

    /** Save Graph as JPEG.
     * 
     */
    public void writeJpegImage(File file) {
        int width = vv.getWidth();
        int height = vv.getHeight();

        BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics = bi.createGraphics();
        vv.paint(graphics);
        graphics.dispose();

        try {
            ImageIO.write(bi, "jpeg", file);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /** Save Full Graph as JPEG.
     * 
     */
    public void writeFullJpegImage(File file) {

        VisualizationImageServer<TreeNode, TreeNode> vis = new VisualizationImageServer<TreeNode, TreeNode>(
                vv.getGraphLayout(), vv.getGraphLayout().getSize());

        // Configure the VisualizationImageServer the same way as the
        // VisualizationViewer
        vis.setBackground(Color.white);
        // vis.setVertexToolTipTransformer(vertexString);
        vis.getRenderContext().setArrowFillPaintTransformer(arrowFillColor);
        vis.getRenderContext().setArrowDrawPaintTransformer(arrowColor);
        vis.getRenderContext().setEdgeFillPaintTransformer(edgeColor);
        // TV: apply color and shape transformers
        vis.getRenderContext().setVertexFillPaintTransformer(vertexColor);
        vis.getRenderContext().setVertexShapeTransformer(vertexSize);
        vis.getRenderContext().setEdgeShapeTransformer(new EdgeShape.Line<TreeNode, TreeNode>());

        // TV: scale the tree graph to fit the panel area when first costructed
        //
        // ScalingControl scaler = new CrossoverScalingControl();
        vis.scaleToLayout(scaler);

        // Create the buffered image
        BufferedImage image = (BufferedImage) vis.getImage(
                new Point2D.Double(vv.getGraphLayout().getSize().getWidth() / 2,
                        vv.getGraphLayout().getSize().getHeight() / 2),
                new Dimension(vv.getGraphLayout().getSize()));

        // Write image to a png file
        // File outputfile = new File("graph.png");

        try {
            ImageIO.write(image, "png", file); // outputfile);
        } catch (IOException e) {
            // Exception handling
            e.printStackTrace();
        }
    }

    /** open PrinterDialog().
     * 
     */
    public void print() {
        class VVP implements Printable {
            VisualizationViewer<TreeNode, TreeNode> vv;

            public VVP(VisualizationViewer<TreeNode, TreeNode> vv) {
                this.vv = vv;
            }

            public int print(Graphics g, PageFormat pf, int pageIndex) {
                if (pageIndex > 0) {
                    return Printable.NO_SUCH_PAGE;
                } else {
                    Graphics2D g2d = (Graphics2D) g;
                    vv.setDoubleBuffered(false);
                    g2d.translate(pf.getImageableX(), pf.getImageableX());
                    vv.paint(g2d);
                    vv.setDoubleBuffered(true);
                    return (Printable.PAGE_EXISTS);
                }
            }
        }

        Printable[] toPrint = { new VVP(vv) };
        new PrinterDialog(toPrint);
    }

    public VisualizationViewer<TreeNode, TreeNode> getVV() {
        return this.vv;
    }

    public RadialTreeLayout<TreeNode, TreeNode> getRadialLayout() {
        return this.radialLayout;
    }

    public BalloonLayout<TreeNode, TreeNode> getBalloonLayout() {
        return this.balloonLayout;
    }

    public VisualizationServer.Paintable getBalloonRings() {
        return this.balloonrings;
    }

    public VisualizationServer.Paintable getRadialRings() {
        return this.radialrings;
    }

    public void setThreshold(Float thresh) {
        this.thresh = thresh;
        // this.colorizeGraph(root, thresh);
    }

    public Float getThreshold() {
        return this.thresh;
    }

    // TV Sensitivity
    public void setSensitivity(Float sensitivity) {
        this.sensitivity = sensitivity;
        // this.colorizeGraph(root, thresh);
    }

    public Float getSensitivity() {
        return this.sensitivity;
    }

    public Graph<TreeNode, TreeNode> getGraph() {
        return this.graph;
    }

    /*
     * public int print(Graphics graphics, PageFormat pageFormat, int pageIndex)
     * throws PrinterException { if (pageIndex > 0) { return
     * Printable.NO_SUCH_PAGE; } else { Graphics2D g2d = (Graphics2D) graphics;
     * vv.setDoubleBuffered(false); g2d.translate(pageFormat.getImageableX(),
     * pageFormat.getImageableY()); vv.paint(g2d); vv.setDoubleBuffered(true);
     * 
     * return (Printable.PAGE_EXISTS); } }
     */

    // RADIAL RINGS
    class RadialRings implements VisualizationServer.Paintable {

        Collection<Double> depths;
        private RadialTreeLayout<TreeNode, TreeNode> radialLayout;

        public RadialRings(RadialTreeLayout<TreeNode, TreeNode> radialLayout) {
            this.radialLayout = radialLayout;
            depths = getDepths(this.radialLayout);
        }

        private Collection<Double> getDepths(RadialTreeLayout<TreeNode, TreeNode> radialLayout) {
            Set<Double> depths = new HashSet<Double>();
            // TV System.out.println("____GraphsPanel.RadialRings.getDepths()
            // depths = " + depths.size());
            Map<TreeNode, PolarPoint> polarLocations = radialLayout.getPolarLocations();
            // TV System.out.println("____GraphsPanel.RadialRings.getDepths()
            // polarLocations = " + polarLocations.size());
            for (TreeNode v : graph.getVertices()) {
                PolarPoint pp = polarLocations.get(v);
                depths.add(pp.getRadius());
            }
            // TV System.out.println("____GraphsPanel.RadialRings.getDepths()
            // depths = " + depths.size());
            return depths;
        }

        public void paint(Graphics g) {
            g.setColor(Color.lightGray);

            Graphics2D g2d = (Graphics2D) g;
            Point2D center = radialLayout.getCenter();

            Ellipse2D ellipse = new Ellipse2D.Double();
            for (double d : depths) {
                ellipse.setFrameFromDiagonal(center.getX() - d, center.getY() - d, center.getX() + d,
                        center.getY() + d);
                Shape shape = vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT)
                        .transform(ellipse);
                g2d.draw(shape);
            }
        }

        public boolean useTransform() {
            return true;
        }
    }

    // BALLOON RINGS
    class BalloonRings implements VisualizationServer.Paintable {

        BalloonLayout<TreeNode, TreeNode> layout;

        public BalloonRings(BalloonLayout<TreeNode, TreeNode> layout) {
            this.layout = layout;
        }

        public void paint(Graphics g) {
            g.setColor(Color.gray);

            Graphics2D g2d = (Graphics2D) g;

            Ellipse2D ellipse = new Ellipse2D.Double();
            for (TreeNode v : layout.getGraph().getVertices()) {
                Double radius = layout.getRadii().get(v);
                if (radius == null)
                    continue;
                Point2D p = layout.transform(v);
                ellipse.setFrame(-radius, -radius, 2 * radius, 2 * radius);
                AffineTransform at = AffineTransform.getTranslateInstance(p.getX(), p.getY());
                Shape shape = at.createTransformedShape(ellipse);

                MutableTransformer viewTransformer = vv.getRenderContext().getMultiLayerTransformer()
                        .getTransformer(Layer.VIEW);

                if (viewTransformer instanceof MutableTransformerDecorator) {
                    shape = vv.getRenderContext().getMultiLayerTransformer().transform(shape);
                } else {
                    shape = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, shape);
                }

                g2d.draw(shape);
            }
        }

        public boolean useTransform() {
            return true;
        }
    }

    public Cluster getCluster() {
        return this.cluster;
    }

    /** setLabelState().
     * 
     */
    public void setLabelState() {
        label.setText("<html><table><tr><td> Similarity: " + Prefs.d3.format(this.getThreshold())
                + "</td><td> Groups: " + cluster.getAvLnkDendrogram().getGroupCount(this.getThreshold())
                + "</td><td>        </td><td> Distinctiveness: " + Prefs.d3.format(this.getSensitivity())
                + "</td><td> Subgroups: " + cluster.getAvLnkDendrogram().getWeakEdgeCount(this.getSensitivity())
                + "</td></tr></table></html>");
    }

}