/*
* Copyright (c) 2009 The Jackson Laboratory
*
* This software was developed by Gary Churchill's Lab at The Jackson
* Laboratory (see http://research.jax.org/faculty/churchill).
*
* This 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.
*
* This software 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 this software. If not, see <http://www.gnu.org/licenses/>.
*/
package org.jax.qtl.graph;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.event.MouseEvent;
import java.awt.font.FontRenderContext;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.List;
import javax.swing.ToolTipManager;
import org.jax.qtl.cross.Cross;
import org.jax.qtl.cross.CrossChromosome;
import org.jax.qtl.cross.GeneticMarker;
/**
* <p>Title: QTL data analysis</p>
*
* <p>Description: </p>
*
* <p>Company: The Jackson Laboratory</p>
*
* @author Lei Wu
* @version 1.0
*/
public class GeneticMapPlot extends OneDimensionPlot {
/**
* every {@link java.io.Serializable} is supposed to have one of these
*/
private static final long serialVersionUID = 3913294152871247405L;
Cross cross;
int numChr;
boolean startFromZero = true; // default is all chromosome pos start from zero
double maxPos;
// graph parameters
int markerLength = 10;
Insets plotInset = new Insets(this.distToAxis+10, this.distToAxis, this.distToAxis + this.tickHeight+10, this.distToAxis);
/**
* Constructor
* @param cross
* the cross whose genetic map we're plotting
*/
public GeneticMapPlot(Cross cross) {
super(); // for adding mouse and mouse motion listeners
this.cross = cross;
this.numChr = cross.getNumberOfChromosomes();
List<CrossChromosome> genotypeData = cross.getGenotypeData();
this.maxPos = 0.0;
for(CrossChromosome currChromosome: genotypeData)
{
List<GeneticMarker> markers =
currChromosome.getAnyGeneticMap().getMarkerPositions();
for(GeneticMarker geneticMarker: markers)
{
double currPos = geneticMarker.getMarkerPositionCentimorgans();
if(currPos > this.maxPos)
{
this.maxPos = currPos;
}
}
}
// set the initial size
setPreferredSize(new Dimension(600,400));
setMinimumSize(new Dimension(60,40));
// add GenoDataSelectionChangeListener to all markers in the scanone data
// TODO add interaction back
// cross.addGenoDataSelectionChangeListeners(this);
// set title and labels
setTitle("Genetic Map for " + cross.toString());
setXlabel("Chromosome");
setYlabel("Location (cM)");
}
/**
* {@inheritDoc}
*/
@Override
void plot() {
int leftConerX = this.inset.left + this.plotInset.left;
int leftConerY = this.inset.top + this.plotInset.top;
int width = this.plotWidth - (this.plotInset.left + this.plotInset.right);
int height = this.plotHeight - (this.plotInset.top + this.plotInset.bottom);
double yscaler = height/this.maxPos;
// draw ticks and labels on y axis
for (int i=0; i<this.maxPos; i+=20) {
drawYtickAndLabel(yscaler, i, 0);
}
List<CrossChromosome> genotypeData = this.cross.getGenotypeData();
for (int i=0; i<this.numChr; i++) {
CrossChromosome currChromosome = genotypeData.get(i);
List<GeneticMarker> markers = currChromosome.getAnyGeneticMap().getMarkerPositions();
if(markers.size() > 0)
{
double x = leftConerX + (i+1) * width * 1.0/(this.numChr + 1);
int numMarkers = markers.size();
GeneticMarker firstMarker = markers.get(0);
GeneticMarker lastMarker = markers.get(numMarkers - 1);
double adjustment = 0;
if (!this.startFromZero) {
adjustment = firstMarker.getMarkerPositionCentimorgans();
}
double y1 =
leftConerY + height -
(firstMarker.getMarkerPositionCentimorgans() - adjustment)*yscaler;
double y2 =
leftConerY + height -
(lastMarker.getMarkerPositionCentimorgans() - adjustment)*yscaler;
// draw horizontal lines for each marker
for (int j=0; j<numMarkers; j++) {
GeneticMarker currentMarker = markers.get(j);
double y =
leftConerY + height -
(currentMarker.getMarkerPositionCentimorgans() - adjustment) * yscaler;
// TODO add back interaction
// if (currentMarker.getMap().isSelected())
// big.setColor(highlightColor);
// else
this.big.setColor(this.normalColor);
Rectangle2D currentShape = new Rectangle2D.Double((x - this.markerLength/2), (int)y, this.markerLength, 1);
this.allDots.add(new Dot(currentMarker, currentShape));
this.big.fill(currentShape);
// big.drawLine((int)(x - markerLength/2), (int)y, (int)(x + markerLength/2), (int)y);
}
// draw vertical line for each chromosome
this.big.setColor(this.normalColor);
this.big.drawLine((int)x, (int)y1, (int)x, (int)y2);
// draw ticks and label on x axis
int baseY = this.inset.top+this.plotHeight;
this.big.setColor(this.lineColor);
this.big.setStroke(this.normalLinetype);
this.big.drawLine( (int) x, baseY, (int) x, baseY - this.tickHeight);
String label = currChromosome.getChromosomeName();
FontRenderContext context = this.big.getFontRenderContext();
Rectangle2D labelBounds = this.tickLabelFont.getStringBounds(label, context);
int labelWidth = (int) labelBounds.getWidth();
int labelHeight = (int) labelBounds.getHeight();
int labelStartX = this.inset.left + this.plotInset.left + (int)(width * 1.0/(this.numChr + 1)*(i+1) - labelWidth/2);
int labelStartY = getHeight() - this.inset.bottom + labelHeight;
this.big.setFont(this.tickLabelFont);
this.big.drawString(label, labelStartX, labelStartY);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void mouseMoved(MouseEvent e) {
// show the tip notes for all selected dots
for(Dot currentDot: this.allDots)
{
if (currentDot.getShape().contains(new Point2D.Double(e.getX(), e.getY()))) {
String tip = "<html>" + currentDot.getMarker1().getMarkerName() + ":" +
"<p>Chromosome: " + currentDot.getMarker1().getChromosomeName() +
"<p>Location(cM): " + ONE_DIGIT_FORMATTER.format(
currentDot.getMarker1().getMarkerPositionCentimorgans());
ToolTipManager.sharedInstance().setEnabled(true);
GeneticMapPlot.this.setToolTipText(tip);
setCursor(this.handCursor);
return;
}
else {
ToolTipManager.sharedInstance().setEnabled(false);
setCursor(this.normalCursor);
}
}
}
}