org.csml.tommo.sugar.modules.MappingQuality.java Source code

Java tutorial

Introduction

Here is the source code for org.csml.tommo.sugar.modules.MappingQuality.java

Source

/**
 *    Copyright Masao Nagasaki
 *    Nagasaki Lab
 *    Laboratory of Biomedical Information Analysis,
 *    Department of Integrative Genomics,
 *    Tohoku Medical Megabank Organization, Tohoku University 
 *    @since 2013
 *
 *    This file is part of SUGAR (Subtile-based GUI-Assisted Refiner).
 *    SUGAR is an extension of FastQC (copyright 2010-12 Simon Andrews)
 *
 *    FastQC 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.
 *
 *    FastQC 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 FastQC; if not, write to the Free Software
 *    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
package org.csml.tommo.sugar.modules;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedSet;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.imageio.ImageIO;
import javax.swing.JLabel;
import javax.swing.JPanel;

import org.csml.tommo.sugar.analysis.JSONFileSerializable;
import org.csml.tommo.sugar.analysis.JSONSerializationUtils;
import org.csml.tommo.sugar.analysis.TileNumeration;
import org.csml.tommo.sugar.dialogs.FileOptionsPanel;
import org.csml.tommo.sugar.heatmap.ColorPaintScale;
import org.csml.tommo.sugar.heatmap.IMixOperation;
import org.csml.tommo.sugar.heatmap.MappingQualityMatrix;
import org.csml.tommo.sugar.modules.heatmap.MappingQualityCellRenderer;
import org.csml.tommo.sugar.modules.heatmap.MappingQualityResultPanel;
import org.csml.tommo.sugar.modules.heatmap.MappingQualityTableModel;
import org.csml.tommo.sugar.sequence.LaneCoordinates;
import org.csml.tommo.sugar.sequence.SAMInfo;
import org.csml.tommo.sugar.sequence.SequenceCoordinates;
import org.csml.tommo.sugar.sequence.TileCoordinates;
import org.csml.tommo.sugar.utils.Options;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.json.simple.parser.ParseException;

import uk.ac.babraham.FastQC.Report.HTMLReportArchive;
import uk.ac.babraham.FastQC.Sequence.BAMFile;
import uk.ac.babraham.FastQC.Sequence.FastBAMFile;
import uk.ac.babraham.FastQC.Sequence.Sequence;
import uk.ac.babraham.FastQC.Sequence.SequenceFile;

public class MappingQuality implements SugarModule, Serializable, JSONFileSerializable {

    private static final String MAPPING_QUALITY_NOT_FOUND_MESSAGE = "Mapping quality data was not found in the input file. Mapping quality can be found in BAM/SAM files only.";

    /**
     * 
     */
    private static final long serialVersionUID = -899746876143478258L;

    private static final String JSON_ATTR_MAPPING_QUALITY_MATRIX_MAP_VALUES = "mappingQualityMatrixMap.values";
    private static final String JSON_ATTR_MAPPING_QUALITY_MATRIX_MAP_KEYS = "mappingQualityMatrixMap.keys";
    private static final String JSON_ATTR_MAX_SEQUENCE_LENGTH = "maxSequenceLength";

    private TileTree tileTree;
    private Map<TileCoordinates, MappingQualityMatrix> mappingQualityMatrixMap = new HashMap<TileCoordinates, MappingQualityMatrix>();

    private int matrixSize;
    private SequenceFile sequenceFile;

    private Integer maxSequenceLength = 0;

    public MappingQuality(TileTree tileTree, SequenceFile sequenceFile) {
        super();
        this.tileTree = tileTree;
        this.sequenceFile = sequenceFile;
        initOptions();
    }

    @Override
    public String description() {
        return "Analysis of mapping quality heatmaps";
    }

    @Override
    public JPanel getResultsPanel() {
        JPanel result = null;
        if (isActive()) {
            result = new MappingQualityResultPanel(this);
        } else {
            result = createEmptyPanel();
        }
        return result;
    }

    @Override
    public boolean ignoreFilteredSequences() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public void makeReport(HTMLReportArchive report) throws IOException {

        if (isActive()) {
            SortedSet<String> flowCells = getTileTree().getFlowCells();
            TileNumeration tileNumeration = getTileTree().getTileNumeration();

            report.htmlDocument().append("Matrix Size: " + getMatrixSize() + "<br/>");
            report.htmlDocument().append("Tile Numeration: " + tileNumeration.getName() + "<br/>");
            report.htmlDocument().append("<br/>");

            for (String flowCell : flowCells) {
                SortedSet<Integer> lanes = getTileTree().getLanes(flowCell);

                report.htmlDocument().append("FlowCell: " + flowCell + "<br/>");

                for (Integer lane : lanes) {
                    report.htmlDocument().append("Lane: " + lane + "<br/>");

                    LaneCoordinates laneCoordinates = new LaneCoordinates(flowCell, lane);
                    //               writeTable2HTML(report, laneCoordinates, tileNumeration);
                    writeImage2HTML(report, laneCoordinates, tileNumeration);
                    report.htmlDocument().append("<br/>");
                }
            }
        } else {
            report.htmlDocument().append(MAPPING_QUALITY_NOT_FOUND_MESSAGE);
        }
    }

    private void writeImage2HTML(HTMLReportArchive report, LaneCoordinates laneCoordinates,
            TileNumeration tileNumeration) throws IOException {
        final MappingQualityTableModel model = new MappingQualityTableModel(this, laneCoordinates, tileNumeration);

        ZipOutputStream zip = report.zipFile();
        StringBuffer b = report.htmlDocument();
        StringBuffer d = report.dataDocument();

        int imgSize = Options.getHeatmapImageSize() + 1; // add one pixel for internal grid
        int width = imgSize * model.getColumnCount() + 1; // add one pixel for left border
        int topBottomSeparator = 50;
        if (model.getTopBottomSeparatorColumn() > 0) {
            width += topBottomSeparator; // add top bottom separator
        }
        int height = imgSize * model.getRowCount() + 1; // add one pixel for top border
        int yOffset = 10 * model.getTileNumeration().getCycleSize(); // space for column header
        int xOffset = 50; // space for row header
        BufferedImage fullImage = new BufferedImage(width + xOffset, height + yOffset, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2 = (Graphics2D) fullImage.getGraphics();
        Color headerBackground = new Color(0x00, 0x00, 0x80);
        Color headerForeground = Color.WHITE;

        // Do the headers
        d.append("#Tiles: ");

        d.append("\t");
        g2.setColor(headerBackground);
        g2.fillRect(0, height, width + xOffset, yOffset);
        g2.fillRect(width, 0, xOffset, height + yOffset);
        g2.setColor(headerForeground);
        g2.setFont(g2.getFont().deriveFont(7f).deriveFont(Font.BOLD));

        drawColumnHeader(g2, model, imgSize, topBottomSeparator, height, d);

        g2.setFont(g2.getFont().deriveFont(9f).deriveFont(Font.BOLD));
        drawRowHeader(g2, model, imgSize, width, xOffset);

        long before = System.currentTimeMillis();

        for (int r = 0; r < model.getRowCount(); r++) {
            int separator = 0;

            for (int c = 0; c < model.getColumnCount(); c++) {
                TileCoordinates tileCoordinate = model.getCoordinateAt(r, c);
                MappingQualityMatrix matrix = getMeanQualityMatrix(tileCoordinate);

                if (matrix != null) {
                    if (r < MappingQualityMatrix.THRESHOLDS.length) {
                        ColorPaintScale paintScale = MappingQualityCellRenderer.getThresholdPaintScale();
                        BufferedImage image = (BufferedImage) matrix
                                .createBufferedImageForThreshold(MappingQualityMatrix.THRESHOLDS[r], paintScale);
                        g2.drawImage(image, 1 + imgSize * c + separator, 1 + imgSize * r,
                                imgSize * (c + 1) + separator, imgSize * (r + 1), 0, 0, image.getWidth(),
                                image.getHeight(), null);
                    } else {
                        ColorPaintScale paintScale = MappingQualityCellRenderer.getAveragePaintScale();
                        BufferedImage image = (BufferedImage) matrix.createBufferedImage(paintScale);
                        g2.drawImage(image, 1 + imgSize * c + separator, 1 + imgSize * r,
                                imgSize * (c + 1) + separator, imgSize * (r + 1), 0, 0, image.getWidth(),
                                image.getHeight(), null);
                    }

                } else {
                    d.append("Missing matrix for: " + tileCoordinate + "\n");
                }
                if (c == model.getTopBottomSeparatorColumn()) {
                    separator = topBottomSeparator;
                }
            }
        }

        g2.dispose();
        String imgFileName = "quality_matrix_" + laneCoordinates.getFlowCell() + "_" + laneCoordinates.getLane()
                + ".png";
        zip.putNextEntry(new ZipEntry(report.folderName() + "/Images/" + imgFileName));
        ImageIO.write(fullImage, "png", zip);
        b.append("<img src=\"Images/" + imgFileName + "\" alt=\"full image\">\n");

        long after = System.currentTimeMillis();
        d.append("Creating report time: " + (after - before));
    }

    protected void drawRowHeader(Graphics2D g2, final MappingQualityTableModel model, int imgSize, int width,
            int xOffset) {
        for (int r = 0; r < model.getRowCount(); r++) {
            int i = r / model.getTileNumeration().getCycleSize();
            String s = i < MappingQualityMatrix.THRESHOLDS.length ? "MAPQ<" + MappingQualityMatrix.THRESHOLDS[i]
                    : "AVERAGE";

            if (model.getCycleID(r) == 0) {
                int stringHeight = g2.getFont().getSize(); //g2.getFontMetrics().getHeight();
                int stringWidth = g2.getFontMetrics().stringWidth(s);
                g2.drawString(s, 1 + width + (xOffset - 1 - stringWidth) / 2,
                        imgSize * (r + 1) - (imgSize - stringHeight) / 2);
            }
        }
    }

    protected void drawColumnHeader(Graphics2D g2, final MappingQualityTableModel model, int imgSize,
            int topBottomSeparator, int height, StringBuffer d) {

        // draw Top-Bottom header
        if (model.getTopBottomSeparatorColumn() > 0) {
            String s = "Top-Bottom";
            int stringWidth = g2.getFontMetrics().stringWidth(s);
            g2.drawString(s, 1 + imgSize * (model.getTopBottomSeparatorColumn() + 1)
                    + (topBottomSeparator - 1 - stringWidth) / 2, 1 + height + 10 - 3);
        }

        int separator = 0;
        for (int c = 0; c < model.getColumnCount(); c++) {
            d.append(model.getColumnName(c));
            d.append("\t");

            if (c == model.getTopBottomSeparatorColumn()) {
                d.append("Top-Bottom");
                d.append("\t");
            }
            Integer[] tiles = model.getTilesForColumn(c);
            for (int i = 0; i < tiles.length; i++) {
                String s = tiles[i].toString();
                int stringWidth = g2.getFontMetrics().stringWidth(s);
                g2.drawString(s, 1 + imgSize * c + (imgSize - 1 - stringWidth) / 2 + separator,
                        1 + height + 10 * (i + 1) - 3);
            }
            if (c == model.getTopBottomSeparatorColumn()) {
                separator = topBottomSeparator;
            }
        }
        d.append("\n");
    }

    @Override
    public String name() {
        return "Mapping quality heatmaps per tile";
    }

    @Override
    public void processSequence(Sequence sequence) {
        if (sequence instanceof SAMInfo) {
            SequenceCoordinates seqCoord = SequenceCoordinates.createSequenceCoordinates(sequence);

            if (sequence.getQualityString().length() > maxSequenceLength) {
                maxSequenceLength = sequence.getQualityString().length();
            }
            storeMappingQuality(seqCoord, (SAMInfo) sequence);
        }
    }

    private void storeMappingQuality(SequenceCoordinates seqCoord, SAMInfo seq) {
        TileCoordinates tileCoordinates = new TileCoordinates(seqCoord.getFlowCell(), seqCoord.getLane(),
                seqCoord.getTile());
        MappingQualityMatrix matrix = mappingQualityMatrixMap.get(tileCoordinates);

        Rectangle tileRange = tileTree.getRange(tileCoordinates);
        if (matrix == null) {
            matrix = new MappingQualityMatrix(tileRange, matrixSize);
            mappingQualityMatrixMap.put(tileCoordinates, matrix);
        }
        matrix.addQualityValue(seqCoord.getX(), seqCoord.getY(), seq.getMappingQuality());
    }

    @Override
    public boolean raisesError() {
        return false;
    }

    @Override
    public boolean raisesWarning() {
        return false;
    }

    @Override
    public void reset() {
        maxSequenceLength = 0;
        mappingQualityMatrixMap.clear();
    }

    @Override
    public boolean isProcessed() {
        return mappingQualityMatrixMap.size() > 0;
    }

    public void initOptions() {
        matrixSize = Options.getMatrixSize();
    }

    public void initOptions(FileOptionsPanel optionsPanel) {
        matrixSize = optionsPanel.getMatrixSize();
    }

    public TileTree getTileTree() {
        return tileTree;
    }

    public MappingQualityMatrix getMeanQualityMatrix(TileCoordinates coordinate) {
        return mappingQualityMatrixMap.get(coordinate);
    }

    public int getMatrixSize() {
        return matrixSize;
    }

    public MappingQualityMatrix createMixedQualityMatrix(TileCoordinates tileCoordinatesTop,
            TileCoordinates tileCoordinatesBottom, IMixOperation mixOperation) {

        MappingQualityMatrix result = null;
        MappingQualityMatrix matrixTop = getMeanQualityMatrix(tileCoordinatesTop);
        MappingQualityMatrix matrixBottom = getMeanQualityMatrix(tileCoordinatesBottom);

        if (matrixTop != null && matrixBottom != null) {
            result = MappingQualityMatrix.createMixedMatrix(matrixTop, matrixBottom, mixOperation);
        }

        return result;
    }

    public Integer getMaxSequenceLength() {
        return maxSequenceLength;
    }

    public JPanel createEmptyPanel() {
        JPanel panel = new JPanel(new BorderLayout());
        String message = "<html>" + MAPPING_QUALITY_NOT_FOUND_MESSAGE + "</html>";
        panel.add(new JLabel(message), BorderLayout.CENTER);
        return panel;
    }

    // customized JSON Serialization

    @Override
    public String toJSONString() {
        JSONObject obj = toJSONObject();
        return obj.toString();
    }

    @Override
    public void writeJSONString(Writer out) throws IOException {
        JSONObject obj = toJSONObject();
        JSONValue.writeJSONString(obj, out);
    }

    @Override
    public JSONObject toJSONObject() {

        JSONObject obj = new JSONObject();

        // maxSequenceLength
        obj.put(JSON_ATTR_MAX_SEQUENCE_LENGTH, maxSequenceLength);

        // meanQualityMatrixMap
        JSONSerializationUtils.saveMapInJSONObject(obj, mappingQualityMatrixMap, "mappingQualityMatrixMap");

        return obj;

    }

    @Override
    public void fromJSONObject(JSONObject jsonObject) {

        // maxSequenceLength
        maxSequenceLength = new Integer(jsonObject.get(JSON_ATTR_MAX_SEQUENCE_LENGTH).toString());

        // tileIDsMap
        JSONArray keyArray = (JSONArray) jsonObject.get(JSON_ATTR_MAPPING_QUALITY_MATRIX_MAP_KEYS);
        JSONArray valueArray = (JSONArray) jsonObject.get(JSON_ATTR_MAPPING_QUALITY_MATRIX_MAP_VALUES);

        for (int i = 0; i < keyArray.size(); i++) {
            JSONObject jsonTileCoordinate = (JSONObject) keyArray.get(i);
            TileCoordinates tc = new TileCoordinates();
            tc.fromJSONObject(jsonTileCoordinate);
            Rectangle tileRange = tileTree.getRange(tc);

            JSONObject jsonMatrix = (JSONObject) valueArray.get(i);
            MappingQualityMatrix matrix = new MappingQualityMatrix(tileRange, matrixSize);
            matrix.fromJSONObject(jsonMatrix);
            mappingQualityMatrixMap.put(tc, matrix);

        }

    }

    @Override
    public void toJSONFile(File file) throws IOException {
        JSONSerializationUtils.writeJSONFile(file, this);
    }

    @Override
    public void fromJSONFile(File file) throws IOException, ParseException {
        JSONSerializationUtils.fromJSONFile(file, this);
    }

    public SequenceFile getSequenceFile() {
        return sequenceFile;
    }

    // customized JSON Serialization

    public boolean isActive() {
        return sequenceFile instanceof BAMFile || sequenceFile instanceof FastBAMFile;
    }
}