/*
* Copyright (c) 2003-2012 Fred Hutchinson Cancer Research Center
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fhcrc.cpl.viewer.ms2;
import org.apache.log4j.Logger;
import org.fhcrc.cpl.toolbox.commandline.arguments.ArgumentValidationException;
import org.fhcrc.cpl.toolbox.gui.chart.PanelWithHeatMap;
import org.fhcrc.cpl.toolbox.datastructure.Pair;
import org.fhcrc.cpl.toolbox.ApplicationContext;
import org.jfree.chart.renderer.PaintScale;
import org.jfree.chart.renderer.LookupPaintScale;
import javax.swing.*;
import java.util.List;
import java.util.ArrayList;
import java.awt.*;
/**
* Utilities for working with 2D-fractionated MS/MS experiments
*/
public class Fractionation2DUtilities
{
protected static Logger _log = Logger.getLogger(Fractionation2DUtilities.class);
public static final int BY_ROW = 0;
public static final int BY_COLUMN = 1;
public Fractionation2DUtilities()
{
}
/**
* Describes the structure of an AMT database containing one or more fractionated
* experiments
*/
public static class FractionatedAMTDatabaseStructure
{
protected List<FractionatedExperimentStructure> experimentStructures;
protected List<Integer> beginningIndicesForExperiments;
public FractionatedAMTDatabaseStructure()
{
experimentStructures = new ArrayList<FractionatedExperimentStructure>();
beginningIndicesForExperiments = new ArrayList<Integer>();
}
public FractionatedAMTDatabaseStructure(
List<FractionatedExperimentStructure> experimentStructures)
{
this();
for (FractionatedExperimentStructure experimentStructure : experimentStructures)
addExperimentStructure(experimentStructure);
}
public FractionatedAMTDatabaseStructure(FractionatedExperimentStructure experimentStructure,
int numExperiments)
{
this();
for (int i=0; i<numExperiments; i++)
addExperimentStructure(experimentStructure);
}
public String toString()
{
StringBuffer resultBuf = new StringBuffer("Experiments: " + getNumExperiments()+"\n");
for (int i=0; i<experimentStructures.size(); i++)
{
FractionatedExperimentStructure experimentStruct = experimentStructures.get(i);
resultBuf.append("Experiment " + i + ": " + experimentStruct + "\n");
}
return resultBuf.toString();
}
public void addExperimentStructure(FractionatedExperimentStructure experimentStructure)
{
beginningIndicesForExperiments.add(getNumFractions());
experimentStructures.add(experimentStructure);
}
public FractionatedExperimentStructure getExperimentStructure(int experimentNumber)
{
return experimentStructures.get(experimentNumber);
}
public int getNumFractions()
{
int numFractions = 0;
for (FractionatedExperimentStructure experimentStructure : experimentStructures)
{
numFractions += experimentStructure.getNumFractions();
}
return numFractions;
}
/**
* experiment number is zero-based
* @param index
* @return
*/
public Pair<Integer,int[]> calculateExperimentAndPosition(int index)
{
int experimentNumber;
for (experimentNumber = 0; experimentNumber < getNumExperiments(); experimentNumber++)
{
if ((experimentNumber >= getNumExperiments() - 1) ||
beginningIndicesForExperiments.get(experimentNumber+1) > index)
break;
}
int indexInExperiment = index - beginningIndicesForExperiments.get(experimentNumber);
int[] positionInExperiment =
experimentStructures.get(experimentNumber).convertIndexToPosition(indexInExperiment);
return new Pair<Integer,int[]>(experimentNumber, positionInExperiment);
}
public int getNumExperiments()
{
return experimentStructures.size();
}
}
public static class FractionatedExperimentStructure
{
public int rows;
public int columns;
public int organization;
public FractionatedExperimentStructure(int columns, int rows, int organization)
{
this.rows=rows;
this.columns=columns;
this.organization=organization;
}
public String toString()
{
return "cols=" + columns + ", rows=" + rows +
", organization=" + (organization == BY_ROW? "row" : "col");
}
public int getNumFractions()
{
return rows * columns;
}
/**
* Takes a single index in a 1D array representing the experiment and returns the
* equivalent 2D location
* @param index
* @return a position in two coordinates. First column, then row
*/
public int[] convertIndexToPosition(int index)
{
int[] result = new int[2];
switch(organization)
{
case BY_ROW:
result[0] = index % columns;
result[1] = index / columns;
break;
case BY_COLUMN:
result[0] = index / rows;
result[1] = index % rows;
break;
default:
throw new IllegalArgumentException("Illegal organization argument to convertIndexToPosition: " +
organization);
}
return result;
}
/**
* Takes data representing a value for each index in a 1D array representing the experiment
* and converts it into a 2D array.
* @param values
* @return
*/
public double[][] convertIndicesToPositions(double[] values)
{
double[][] result = new double[columns][rows];
switch(organization)
{
case BY_ROW:
for (int i=0; i<rows; i++)
for (int j=0; j<columns; j++)
result[j][i] = values[(columns * i) + j];
break;
case BY_COLUMN:
for (int i=0; i<rows; i++)
for (int j=0; j<columns; j++)
result[i][j] = values[(columns * i) + j];
break;
default:
throw new IllegalArgumentException(
"Illegal organization argument to convertIndexToPosition: " +
organization);
}
return result;
}
}
/**
* num rows, num columns, fraction order, number of experiments
* @param structureArgument
* @return
*/
public static FractionatedAMTDatabaseStructure
digestAmtStructureArgument(String structureArgument)
throws ArgumentValidationException
{
String[] chunks = structureArgument.split(",");
if (chunks.length != 4)
throw new ArgumentValidationException("argument amtdbdimensions must have four comma-separated parts (rows, columns, organization, # experiments)." +
" Specified argument has " + chunks.length);
int lastCommaIndex = structureArgument.lastIndexOf(",");
FractionatedExperimentStructure experimentStructure =
digestDimensionArgument(structureArgument.substring(0, lastCommaIndex));
try
{
int numExperiments = Integer.parseInt(chunks[3]);
return new FractionatedAMTDatabaseStructure(experimentStructure, numExperiments);
}
catch (Exception e)
{
throw new ArgumentValidationException(e);
}
}
/**
* Takes a String argument defining the structure of an MS/MS experiment
* (number of rows, number of columns, and whether the runs appear in the order
* of rows or columns) and parses it
* @param dimensionArgument
* @return
* @throws ArgumentValidationException
*/
public static FractionatedExperimentStructure
digestDimensionArgument(String dimensionArgument)
throws ArgumentValidationException
{
int dbRows, dbCols, organization;
String[] chunks = dimensionArgument.split(",");
if (chunks.length != 3)
throw new ArgumentValidationException("argument amtdbdimensions must have three comma-separated parts (rows, columns, organization)." +
" Specified argument has " + chunks.length);
try
{
dbRows = Integer.parseInt(chunks[0]);
dbCols = Integer.parseInt(chunks[1]);
}
catch (NumberFormatException e)
{
throw new ArgumentValidationException("Could not understand the number of rows and columns passed in argument amtdbdimensions");
}
if (chunks[2].equalsIgnoreCase("row"))
organization = BY_ROW;
else if (chunks[2].equalsIgnoreCase("col"))
organization = BY_COLUMN;
else
throw new ArgumentValidationException("Please specify 'row' or 'col' (without quotes) for the organization of database rows");
return new FractionatedExperimentStructure(dbCols, dbRows, organization);
}
public static void showHeatMapChart(FractionatedAMTDatabaseStructure amtDatabaseStructure,
double[] dataForChart, String chartName, boolean showLegend)
{
int chartWidth=1000;
int chartHeight=1000;
double globalMinValue = Double.MAX_VALUE;
double globalMaxValue = Double.MIN_VALUE;
for (double value : dataForChart)
{
if (value < globalMinValue)
globalMinValue = value;
if (value > globalMaxValue)
globalMaxValue = value;
}
_log.debug("showHeatMapChart: experiment structures:");
List<double[][]> amtPeptidesInExperiments = new ArrayList<double[][]>();
for (int i=0; i<amtDatabaseStructure.getNumExperiments(); i++)
{
int experimentWidth = amtDatabaseStructure.getExperimentStructure(i).columns;
int experimentHeight = amtDatabaseStructure.getExperimentStructure(i).rows;
_log.debug("\t" + amtDatabaseStructure.getExperimentStructure(i));
amtPeptidesInExperiments.add(new double[experimentWidth][experimentHeight]);
}
for (int i=0; i<dataForChart.length; i++)
{
Pair<Integer, int[]> positionInExperiments =
amtDatabaseStructure.calculateExperimentAndPosition(i);
int xpos = positionInExperiments.second[0];
int ypos = positionInExperiments.second[1];
int experimentIndex = positionInExperiments.first;
//System.err.println("i, xpos, ypos: " + i + ", " + xpos + ", " + ypos);
amtPeptidesInExperiments.get(experimentIndex)[xpos][ypos] =
dataForChart[i];
}
JDialog cd = new JDialog(ApplicationContext.getFrame(),"Heat Map(s)");
cd.setSize(chartWidth,chartHeight);
cd.setPreferredSize(new Dimension(chartWidth,chartHeight));
cd.setLayout(new FlowLayout());
int nextPerfectSquareRoot = 0;
while (true)
{
nextPerfectSquareRoot++;
if ((nextPerfectSquareRoot * nextPerfectSquareRoot) >= amtPeptidesInExperiments.size())
{
break;
}
}
int plotWidth = (int) ((double) (chartWidth - 20)/
(double) nextPerfectSquareRoot);
int plotHeight = (int) ((double) (chartHeight - 20)/
(double) nextPerfectSquareRoot);
_log.debug("Rescaled chart dimensions: " + plotWidth + "x" + plotHeight);
LookupPaintScale paintScale =
PanelWithHeatMap.createPaintScale(globalMinValue, globalMaxValue, Color.BLUE, Color.RED);
for (int i=0; i<amtPeptidesInExperiments.size(); i++)
{
PanelWithHeatMap pwhm =
new PanelWithHeatMap(amtPeptidesInExperiments.get(i),"Experiment " + (i+1));
// pwhm.setPalette(PanelWithHeatMap.PALETTE_BLUE_RED);
pwhm.setPaintScale(paintScale);
pwhm.setPreferredSize(new Dimension(plotWidth, plotHeight));
pwhm.setAxisLabels("AX Fraction","RP Fraction");
if (!showLegend)
pwhm.getChart().removeLegend();
cd.add(pwhm);
}
cd.setTitle(chartName);
cd.setVisible(true);
}
}