
net.maizegenetics.analysis.popgen.LinkageDisequilibriumComponent Maven / Gradle / Ivy
// LinkageDisequilibriumComponent.java
//
// (c) 1999-2001 PAL Development Core Team
//
// This package may be distributed under the
// terms of the Lesser GNU General Public License (LGPL)
package net.maizegenetics.analysis.popgen;
import net.maizegenetics.dna.snp.GenotypeTable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.text.DecimalFormat;
/**
* An AWT Component for displaying information on linkage disequilibrium.
*
* Nice schematics are produced if an annotation alignment is used to construct
* LinkageDisequilibrium. It can portray things both on the gene and chromosomal
* scale.
*
*
* @author Ed Buckler
* @version $Id: LinkageDisequilibriumComponent.java
*/
public class LinkageDisequilibriumComponent extends JComponent {
public final static int P_VALUE = 0;
public final static int DPRIME = 1;
public final static int RSQUARE = 2;
double minimumChromosomeLength = 10;
LinkageDisequilibrium theLD;
GenotypeTable theAA;
boolean includeBlockSchematic, chromosomalScale;
boolean includeLabels = true;
int totalVariableSites, totalLoci, totalChromosomes, totalIntervals, totalBlocks;
double[] startPos, endPos; //These are the relative positions of the polymorphisms
double[] blockBeginPos, blockEndPos;
String[] blockNames;
int[] xPos, yPos, xEndPos; //these hold positions of the upper left corners for each site
int[] blockBeginX, blockEndX;//These are the absolute positions of the genes & chromosomes
int ih, iw;
double[] blockStart, blockEnd;
//this will range from 0 to 1
String upperLabel, lowerLabel;
double[][] diseq;
Color theColor = new Color(0, 0, 0);
int distanceBetweenGraphAndGene = 40;
int hoff = 70, h2off = 70, voff = 20;
boolean probability = true, upperProb = false, lowerProb = true;
//viewer attribute variables
int myWindowSize;
int myWindowX;
int myWindowY;
//stat and end coordinates for LD plot including chromosome jumps
int myXStart;
int myXEnd;
int myYStart;
int myYEnd;
//used to correct for chromosome jumps when accessing indexed info from LD or alignment
int[] jump;
public LinkageDisequilibriumComponent(LinkageDisequilibrium theLD, boolean includeBlockSchematic, boolean chromosomalScale, int windowSize, int windowX, int windowY) {
this.theLD = theLD;
theAA = theLD.getAlignment();
this.includeBlockSchematic = includeBlockSchematic;
this.chromosomalScale = chromosomalScale;
myWindowSize = windowSize;
myWindowX = windowX;
myWindowY = windowY;
totalVariableSites = theLD.getSiteCount();
this.diseq = new double[windowSize][windowSize];
setXStart();
setYStart();
jump = new int[totalVariableSites+theAA.numChromosomes()-1];
String locus = theAA.chromosomeName(0);
int jumpValue = 0;
for (int i = 0; i< jump.length; i++) {
if (!locus.equals(theAA.chromosomeName(i+jumpValue))) {
locus = theAA.chromosomeName(i+jumpValue);
jumpValue--;
jump[i] = 1;
} else {
jump[i] = jumpValue;
}
}
setUpperCorner(RSQUARE);
setLowerCorner(P_VALUE);
if (theAA != null) {
countGenesAndChromosomes();
calculateStartAndEndPositions();
} else {
includeBlockSchematic = false;
}
xPos = new int[windowSize + 1];
yPos = new int[windowSize + 1];
xEndPos = new int[windowSize + 1];
setToolTipText("");
try {
jbInit();
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
* This sets the X start and end positions
*/
public void setXStart() {
myXStart = (int)Math.floor(myWindowX-myWindowSize/2.0);
myXEnd = myXStart + myWindowSize;
}
/**
* This sets the Y start and end positions
*/
public void setYStart() {
myYStart = (int)Math.floor(myWindowY-myWindowSize/2.0);
myYEnd = myYStart + myWindowSize;
}
/**
* This sets a new viewable size
*/
public void setWindowSize(int newSize, int newX, int newY, int ldMeasureLower, int ldMeasureUpper) {
myWindowSize = newSize;
myWindowX = newX;
myWindowY = newY;
diseq = new double[myWindowSize][myWindowSize];
xPos = new int[myWindowSize + 1];
yPos = new int[myWindowSize + 1];
xEndPos = new int[myWindowSize + 1];
setXStart();
setYStart();
setLowerCorner(ldMeasureLower);
setUpperCorner(ldMeasureUpper);
if (theAA != null) {
countGenesAndChromosomes();
calculateStartAndEndPositions();
}
}
/**
* This sets a new X position
*/
public void setWindowX(int newX, int ldMeasureLower, int ldMeasureUpper) {
myWindowX = newX;
setXStart();
calculateStartAndEndPositions();
setLowerCorner(ldMeasureLower);
setUpperCorner(ldMeasureUpper);
}
/**
* This sets a new Y position
*/
public void setWindowY(int newY, int ldMeasureLower, int ldMeasureUpper) {
myWindowY = newY;
setYStart();
calculateStartAndEndPositions();
setLowerCorner(ldMeasureLower);
setUpperCorner(ldMeasureUpper);
}
/**
* This determines what is displayed in the lower left corner.
* Options are: P_VALUE, DPRIME, and RSQUARE
*/
public void setLowerCorner(int ldMeasure) {
for (int r = 0; r < myWindowSize; r++) {
if (jump[r+myXStart] != 1) {
for (int c = Math.max(r+myXStart-myYStart, 0); c < myWindowSize; c++) {
if (jump[c+myYStart] != 1) {
switch (ldMeasure) {
case P_VALUE: {
diseq[r][c] = theLD.getPVal(c+myYStart+jump[c+myYStart], r+myXStart+jump[r+myXStart]);
lowerLabel = "P value";
break;
}
case DPRIME: {
diseq[r][c] = theLD.getDPrime(c+myYStart+jump[c+myYStart], r+myXStart+jump[r+myXStart]);
lowerLabel = "D'";
break;
}
case RSQUARE: {
diseq[r][c] = theLD.getRSqr(c+myYStart+jump[c+myYStart], r+myXStart+jump[r+myXStart]);
lowerLabel = "R^2";
break;
}
}
}
}
}
}
lowerProb = (ldMeasure == P_VALUE) ? true : false;
}
/**
* This determines what is displayed in the upper right corner.
* Options are: P_VALUE, DPRIME, and RSQUARE
*/
public void setUpperCorner(int ldMeasure) {
for (int c = 0; c < myWindowSize; c++) {
if (jump[c+myYStart] != 1) {
for (int r = Math.max(c+myYStart-myXStart, 0); r < myWindowSize; r++) {
if (jump[r+myXStart] != 1) {
switch (ldMeasure) {
case P_VALUE: {
diseq[r][c] = theLD.getPVal(r+myXStart+jump[r+myXStart], c+myYStart+jump[c+myYStart]);
upperLabel = "P value";
break;
}
case DPRIME: {
diseq[r][c] = theLD.getDPrime(r+myXStart+jump[r+myXStart], c+myYStart+jump[c+myYStart]);
upperLabel = "D'";
break;
}
case RSQUARE: {
diseq[r][c] = theLD.getRSqr(r+myXStart+jump[r+myXStart], c+myYStart+jump[c+myYStart]);
upperLabel = "R squared";
break;
}
}
}
}
}
}
upperProb = (ldMeasure == P_VALUE) ? true : false;
}
/**
* This sets the scale of the LD view, either sites are organized by chromosomes if
* chromosomalScale is true, otherwise they are organized by genes
*/
public void setScaleOfView(boolean chromosomalScale) {
this.chromosomalScale = chromosomalScale;
countGenesAndChromosomes();
calculateStartAndEndPositions();
}
/**
* This sets whether a schematic is displayed. If true a schematic of genes or
* chromosomes is displayed, otherwise no schematic is displayed
*/
public void setShowSchematic(boolean includeBlockSchematic) {
if (theAA == null) {
return; //if there is no annotation don't produce the schematic
}
this.includeBlockSchematic = includeBlockSchematic;
countGenesAndChromosomes();
calculateStartAndEndPositions();
}
public void setShowLabels(boolean includeLabels) {
if (this.includeLabels == includeLabels) {
return;
}
this.includeLabels = includeLabels;
countGenesAndChromosomes();
calculateStartAndEndPositions();
}
/**
* this counts the number of separate blocks
* if on chromosomal scale then chromosomes are counted otherwise only loci are counted
* It then deteremines the total span in terms of cM or bases depending on scale
*/
private void countGenesAndChromosomes() {
totalLoci = totalChromosomes = 0;
String currLocus = "";
for (int r = 0; r < totalVariableSites; r++)
{
if (!currLocus.equals(theAA.chromosomeName(r))) {
totalLoci++;
currLocus = theAA.chromosomeName(r);
}
}
//the number of separate totalBlocks
totalBlocks = (chromosomalScale) ? totalChromosomes : totalLoci;
if (totalBlocks == 0) {
totalBlocks = 1;
}
blockStart = new double[totalBlocks];
blockEnd = new double[totalBlocks];
blockNames = new String[totalBlocks];
for (int i = 0; i < totalChromosomes; i++) {
blockStart[i] = 999999;
blockEnd[i] = -999999;
}
int c = -1;
currLocus = "unknown locus";
for (int r = 0; r < totalVariableSites; r++) {
if (!currLocus.equals(theAA.chromosomeName(r))) {
c++;
currLocus = theAA.chromosomeName(r);
blockNames[c] = currLocus;
}
if (blockStart[c] > theAA.chromosomalPosition(r)) {
blockStart[c] = theAA.chromosomalPosition(r);
}
if (blockEnd[c] < theAA.chromosomalPosition(r)) {
blockEnd[c] = theAA.chromosomalPosition(r);
}
}
for (int i = 0; i < totalBlocks; i++) {
if ((chromosomalScale) && ((blockEnd[i] - blockStart[i]) < minimumChromosomeLength)) {
blockEnd[i] = blockStart[i] + minimumChromosomeLength;
} else if ((blockEnd[i] - blockStart[i]) < 1) {
blockEnd[i] = blockStart[i] + 1;
}
}
}
/**
* this determines to relative positions of the sites and cartoons (everything ranges from 0..1)
*
*/
void calculateStartAndEndPositions() {
//This will determine were all the relative positions of the sites go
double proportionPerPolymorphism;// proportionPerUnit = 0.0f;
if (includeBlockSchematic) {
proportionPerPolymorphism = 1 / (double) myWindowSize;
blockBeginPos = new double[totalBlocks]; //These hold the start and end points of the genes
blockEndPos = new double[totalBlocks];
} else {
totalIntervals = myWindowSize;
proportionPerPolymorphism = 1 / (double) totalIntervals;
}
startPos = new double[myWindowSize];
endPos = new double[myWindowSize];
startPos[0] = 0;
endPos[0] = 0;
for (int r = 0; r < myWindowSize; r++) {
startPos[r] = r * proportionPerPolymorphism;
} //end of going through sites
if (includeBlockSchematic) {
for (int b = 0; b < totalBlocks; b++) {
blockBeginPos[b] = b / (double)totalBlocks;
blockEndPos[b] = (b + 1) / (double)totalBlocks;
}
int currB = 0;
for (int i = 1; i < myXStart; i++) {
if (!theAA.chromosomeName(i+jump[i]).equals(theAA.chromosomeName(i+jump[i] - 1))) {
currB++;
}
}
endPos[0] = blockBeginPos[currB] + (theAA.chromosomalPosition(myXStart+jump[myXStart]) - blockStart[currB]) / (blockEnd[currB] - blockStart[currB]) / totalBlocks;
for (int r = myXStart+1; r < myXEnd; r++) {
if (!theAA.chromosomeName(r+jump[r]).equals(theAA.chromosomeName(r+jump[r] - 1))) {
currB++;
}
endPos[r-myXStart] = blockBeginPos[currB] + (theAA.chromosomalPosition(r+jump[r]) - blockStart[currB]) / (blockEnd[currB] - blockStart[currB]) / totalBlocks;
}
}
}
private void jbInit() throws Exception {
this.setBackground(Color.red);
this.setSize(400, 400);
}
private Color getMagnitudeColor(int r, int c) {
if (r + myWindowX == c + myWindowY) {
return theColor.getHSBColor(0.999f, (float) diseq[r][c], 1f);
}
if (Double.isNaN(diseq[r][c])) {
return theColor.lightGray;
}
if (diseq[r][c] > 0.999) {
return theColor.getHSBColor(1f, 1f, 1f);
}
if (diseq[r][c] < -998.0) {
return theColor.lightGray;
}
if ((((float) diseq[r][c]) + (1.0000f - ((float) diseq[r][c])) / 2) < 0.52f) {
return theColor.getHSBColor(((float) diseq[r][c]) + (1.0000f - ((float) diseq[r][c])) / 2 - .5f, ((float) diseq[r][c]) + (1.0000f - ((float) diseq[r][c])) / 2 - .5f, 1f);
} else {
return theColor.getHSBColor(((float) diseq[r][c]) + (1.0000f - ((float) diseq[r][c])) / 2, ((float) diseq[r][c]) + (1.0000f - ((float) diseq[r][c])) / 2, 1f);
}
}
private Color getProbabilityColor(int r, int c) {
double p1 = 0.01, p2 = 0.001, p3 = 0.0001;
if (Double.isNaN(diseq[r][c])) {
return theColor.lightGray;
}
if (diseq[r][c] < -998.0) {
return theColor.lightGray;
}
if (diseq[r][c] > p1) {
return theColor.white;
}
if (diseq[r][c] > p2) {
return theColor.blue;
}
if (diseq[r][c] > p3) {
return theColor.green;
}
return theColor.red;
}
private void addPolymorphismLabels(Graphics g, int ih) {
String s;
g.setFont(new java.awt.Font("Dialog", 0, 9));
g.setColor(theColor.black);
for (int c = myYStart; c < myYEnd; c++) {
if (jump[c] != 1) {
s = theAA.chromosomeName(c+jump[c]) + "s" + theAA.chromosomalPosition(c+jump[c]);
} else {
s = "";
}
g.drawString(s, 4, yPos[c-myYStart] + ih - 1);
}
}
/**
* This converts all those relative positions to real coordinates based on the size of the component
*/
private void calculateCoordinates(Graphics gr) {
Dimension d = this.getSize();
double iwf, ihf, xSize, ySize;
ySize = d.height - voff - distanceBetweenGraphAndGene;
ihf = ySize / (double) myWindowSize;
xSize = d.width - hoff - h2off;
iwf = xSize / (double) myWindowSize;
ih = (int) Math.round(ihf);
iw = (int) Math.round(iwf);
for (int r = 0; r < myWindowSize; r++) {
xPos[r] = (int) ((startPos[r] * xSize) + (double) hoff);
yPos[r] = (int) ((startPos[r] * ySize) + (double) voff);
} //end of going through sites
xPos[myWindowSize] = (int) d.width - h2off;
yPos[myWindowSize] = (int) ySize + voff;
if (includeBlockSchematic) {
for (int r = 0; r < myWindowSize; r++) {
xEndPos[r] = (int) Math.round((endPos[r] * xSize) + hoff);
} //end of going through sites
blockBeginX = new int[totalBlocks];
blockEndX = new int[totalBlocks];
for (int b = 0; b < totalBlocks; b++) {
blockBeginX[b] = (int) Math.round((blockBeginPos[b] * xSize) + hoff);
blockEndX[b] = (int) Math.round((blockEndPos[b] * xSize) + hoff);
}
}
}
protected void paintComponent(Graphics g) {
if (diseq == null) {
return;
}
// super.paintComponent(g);
Dimension d = this.getSize();
calculateCoordinates(g);
g.setColor(theColor.white);
g.fillRect(0, 0, d.width, d.height);
g.setColor(theColor.darkGray);
g.fillRect(xPos[0], yPos[0], xPos[myWindowSize] - xPos[0], yPos[myWindowSize] - yPos[0] + 2);
//checks to see if a jump has occured
for (int r = myXStart; r < myXEnd; r++) {
if (jump[r] == 1) {
g.setColor(theColor.darkGray);
for (int c = myYStart; c < myYEnd; c++) {
g.fillRect(xPos[r-myXStart], yPos[c-myYStart], iw + 1, ih + 1);
}
} else {
for (int c = myYStart; c < myYEnd; c++) {
if (jump[c] == 1){
g.setColor(theColor.darkGray);
} else if (((c+jump[c] < r+jump[r]) && (upperProb == true)) || ((c+jump[c] > r+jump[r]) && (lowerProb == true))) {
g.setColor(getProbabilityColor(r-myXStart, c-myYStart));
} else if (r == c) {
g.setColor(theColor.black);
} else {
g.setColor(getMagnitudeColor(r-myXStart, c-myYStart));
}
g.fillRect(xPos[r-myXStart], yPos[c-myYStart], iw + 1, ih + 1);
}
}
}
// Removed grid lines because the cover too much
// on large graphs. -terryc
/*
g.setColor(theColor.darkGray);
for(int r=0; r0.01", xStart + barWidth + 5, currY + 10);
currY += yInc;
g.setColor(theColor.blue);
g.fillRect(xStart, currY, barWidth, yInc);
g.setColor(Color.black);
g.drawRect(xStart, currY, barWidth, yInc);
g.drawString("<0.01", xStart + barWidth + 5, currY + 10);
currY += yInc;
g.setColor(theColor.green);
g.fillRect(xStart, currY, barWidth, yInc);
g.setColor(Color.black);
g.drawRect(xStart, currY, barWidth, yInc);
g.drawString("<0.001", xStart + barWidth + 5, currY + 10);
currY += yInc;
g.setColor(theColor.red);
g.fillRect(xStart, currY, barWidth, yInc);
g.setColor(Color.black);
g.drawRect(xStart, currY, barWidth, yInc);
g.drawString("<0.0001", xStart + barWidth + 5, currY + 10);
} else {
yInc = (yEnd - yStart) / 11;
dF = new DecimalFormat("0.00");
for (double d = 1.0000f; d >= 0.5; d -= 0.05) {
g.setColor(theColor.getHSBColor((float) d, (float) d, 1f));
g.fillRect(xStart, currY, barWidth, yInc);
g.setColor(Color.black);
g.drawRect(xStart, currY, barWidth, yInc);
g.drawString(dF.format(d - (1.0001f - d)), xStart + barWidth + 5, currY + 10);
currY += yInc;
}
g.setColor(theColor.getHSBColor((float) 0, (float) 0, 1f));
g.fillRect(xStart, currY, barWidth, yInc);
g.setColor(Color.black);
g.drawRect(xStart, currY, barWidth, yInc);
g.drawString(dF.format(0.00), xStart + barWidth + 5, currY + 10);
}
}
private void addGenePicture(Graphics g, int ih, int iw) {
//This will add the gene picture to the left of the polymorphisms
int yOfLinkBlock, yOfGene, yOfGeneLabel;//,totalBases,spacer, cpos;
int halfIW = iw / 2;
// MultiAlleleSiteCharacteristic theMSC, lastMSC;
Dimension d = this.getSize();
yOfLinkBlock = yPos[myWindowSize];
yOfGene = yOfLinkBlock + (distanceBetweenGraphAndGene / 2);
yOfGeneLabel = yOfLinkBlock + (int) (0.8f * (double) distanceBetweenGraphAndGene);
for (int r = 0; r < myWindowSize; r++) {
if (jump[r+myXStart] != 1) {
g.drawLine(xPos[r] + halfIW, yOfLinkBlock + 1, xEndPos[r], yOfGene);
}
} //end of going through sites
for (int b = 0; b < totalBlocks; b++) {
g.setColor(iterColor(b));
g.drawLine(blockBeginX[b], yOfGene, blockEndX[b], yOfGene);
g.drawLine(blockBeginX[b], yOfGene + 1, blockEndX[b], yOfGene + 1);
g.drawString(blockNames[b], blockBeginX[b], yOfGeneLabel);
}
}
private Color iterColor(int iter) {
Color newColor;
if (iter % 5 == 0) {
newColor = theColor.blue;
} else if (iter % 5 == 1) {
newColor = theColor.red;
} else if (iter % 5 == 2) {
newColor = theColor.green;
} else if (iter % 5 == 3) {
newColor = theColor.cyan;
} else {
newColor = theColor.orange;
}
return newColor;
}
public String getToolTipText(MouseEvent e) {
Point graphPoint = getLocationOnScreen();
Point mousePoint = e.getLocationOnScreen();
double graphX = graphPoint.getX();
double graphY = graphPoint.getY();
double mouseX = mousePoint.getX() - graphX;
double mouseY = mousePoint.getY() - graphY;
DecimalFormat format = new DecimalFormat("0.###E0");
for (int r = myXStart; r < myXEnd; r++) {
if (jump[r] != 1 && mouseX > xPos[r-myXStart] && mouseX < xPos[r-myXStart+1]) {
for (int c = myYStart; c < myYEnd; c++) {
if (jump[c] != 1 && mouseY > yPos[c-myYStart] && mouseY < yPos[c-myYStart+1]) {
return theAA.siteName(r+jump[r]) + ": " + theAA.chromosomalPosition(r+jump[r]) + ", " + theAA.siteName(c+jump[c]) + ": " + theAA.chromosomalPosition(c+jump[c]) + ", Value: " + format.format(diseq[r-myXStart][c-myYStart]);
}
}
}
}
return null;
}
public void setGraphSize(int graphX, int graphY) {
setPreferredSize(new Dimension(graphX, graphY));
setSize(new Dimension(graphX, graphY));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy