com.hfg.graphics.Gene2D Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of com_hfg Show documentation
Show all versions of com_hfg Show documentation
com.hfg xml, html, svg, and bioinformatics utility library
package com.hfg.graphics;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Paint;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.util.ArrayList;
import java.util.List;
import com.hfg.bio.seq.Exon;
import com.hfg.bio.Strand;
import com.hfg.bio.seq.SeqMappingCoverage;
import com.hfg.bio.taxonomy.ncbi.NCBITaxon;
import com.hfg.html.Link;
import com.hfg.svg.SvgNode;
import com.hfg.svg.SvgGroup;
import com.hfg.svg.SvgRect;
import com.hfg.svg.SvgText;
import com.hfg.util.collection.CollectionUtil;
import com.hfg.util.StringBuilderPlus;
import com.hfg.javascript.TooltipJS;
//------------------------------------------------------------------------------
/**
Simple 2D representation of a gene.
@author J. Alex Taylor, hairyfatguy.com
*/
//------------------------------------------------------------------------------
// com.hfg XML/HTML Coding Library
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
// [email protected]
//------------------------------------------------------------------------------
public class Gene2D extends Rectangle implements Comparable
{
//##########################################################################
// PRIVATE FIELDS
//##########################################################################
private String mId;
private String mDescription;
private List mURLs;
private int mStartLocation;
private int mEndLocation;
private Strand mStrand;
private List mExons;
private NCBITaxon mSpecies;
private SeqMappingCoverage mSeqMappingCoverage;
private int mDisplayStart;
private int mDisplayEnd;
private boolean mInDisplayRegion;
private boolean mShowId = true;
private int mLabelWidthInPixels = -1;
private Color mColor;
// private Font mLabelFont = new Font("Monospaced", Font.PLAIN, 9);
private Font mLabelFont = Font.decode("Courier-PLAIN-9");
private double mXScalingFactor;
private int mHeightInPixels = 10;
private static Color DEFAULT_COLOR = Color.black;
private static FontRenderContext sFRC;
static
{
Frame frame = new Frame();
frame.addNotify();
Image image =frame.createImage(1, 1);
sFRC = ((Graphics2D) image.getGraphics()).getFontRenderContext();
}
//##########################################################################
// CONSTRUCTORS
//##########################################################################
//--------------------------------------------------------------------------
public Gene2D()
{
}
//##########################################################################
// PUBLIC METHODS
//##########################################################################
//--------------------------------------------------------------------------
public void addExon(Exon inValue)
{
if (null == mExons)
{
mExons = new ArrayList<>();
}
mExons.add(inValue);
if (mStrand == null && inValue.getStrand() != null)
{
mStrand = inValue.getStrand();
}
if (mStartLocation == 0
|| inValue.getLeft() < mStartLocation)
{
mStartLocation = inValue.getLeft();
}
if (mEndLocation == 0
|| inValue.getRight() > mEndLocation)
{
mEndLocation = inValue.getRight();
}
}
//--------------------------------------------------------------------------
public List getExons()
{
return mExons;
}
//--------------------------------------------------------------------------
public void setId(String inValue)
{
mId = inValue;
mLabelWidthInPixels = -1;
}
//--------------------------------------------------------------------------
public String getId()
{
return mId;
}
//--------------------------------------------------------------------------
public void setDescription(String inValue)
{
mDescription = inValue;
}
//--------------------------------------------------------------------------
public String getDescription()
{
return mDescription;
}
//--------------------------------------------------------------------------
public void setSeqMappingCoverage(SeqMappingCoverage inValue)
{
mSeqMappingCoverage = inValue;
}
//--------------------------------------------------------------------------
public SeqMappingCoverage getSeqMappingCoverage()
{
return mSeqMappingCoverage;
}
//--------------------------------------------------------------------------
public void setSpecies(NCBITaxon inValue)
{
mSpecies = inValue;
}
//--------------------------------------------------------------------------
public NCBITaxon getSpecies()
{
return mSpecies;
}
//--------------------------------------------------------------------------
/**
Add a URL to the gene. If more than one URL is defined, a popup menu will be
shown.
@param inValue the link to associate with the gene
*/
public void addURL(Link inValue)
{
if (null == mURLs)
{
mURLs = new ArrayList<>(2);
}
mURLs.add(inValue);
}
//--------------------------------------------------------------------------
public List getURLs()
{
return mURLs;
}
//--------------------------------------------------------------------------
/**
Basepair location of the start of the gene.
@return the 1-based location
*/
public int getStartLocation()
{
return mStartLocation;
}
//--------------------------------------------------------------------------
public void setStartLocation(int inValue)
{
mStartLocation = inValue;
}
//--------------------------------------------------------------------------
public int getEndLocation()
{
return mEndLocation;
}
//--------------------------------------------------------------------------
public void setEndLocation(int inValue)
{
mEndLocation = inValue;
}
//--------------------------------------------------------------------------
/**
The leftmost basepair of the gene (the start location if the gene is on the
forward strand or the end location if the gene is on the reverse strand).
@return the 1-based left location
*/
public int getLeft()
{
return Math.min(mStartLocation, mEndLocation);
}
//--------------------------------------------------------------------------
/**
The rightmost basepair of the gene (the end location if the gene is on the
forward strand or the start location if the gene is on the reverse strand).
@return the 1-based right location
*/
public int getRight()
{
return Math.max(mStartLocation, mEndLocation);
}
//--------------------------------------------------------------------------
public Strand getStrand()
{
return mStrand;
}
//--------------------------------------------------------------------------
public void setStrand(Strand inValue)
{
mStrand = inValue;
}
//--------------------------------------------------------------------------
public void setColor(Color inValue)
{
mColor = inValue;
}
//--------------------------------------------------------------------------
public Color getColor()
{
return mColor;
}
//--------------------------------------------------------------------------
public void setLabelFont(Font inValue)
{
mLabelFont = inValue;
}
//--------------------------------------------------------------------------
public void setXScalingFactor(double inValue)
{
mXScalingFactor = inValue;
}
//--------------------------------------------------------------------------
public void setShowId(boolean inValue)
{
mShowId = inValue;
}
//--------------------------------------------------------------------------
public boolean getShowId()
{
return mShowId;
}
//--------------------------------------------------------------------------
public void setDisplayRegion(int inDisplayStart, int inDisplayEnd)
{
mDisplayStart = Math.max(getLeft(), inDisplayStart);
mDisplayEnd = Math.min(getRight(), inDisplayEnd);
mInDisplayRegion = (mDisplayEnd >= mDisplayStart ? true : false);
}
//--------------------------------------------------------------------------
public boolean inDisplayRegion()
{
return mInDisplayRegion;
}
//--------------------------------------------------------------------------
public SvgNode toSVG()
{
SvgGroup group = new SvgGroup();
if (mInDisplayRegion)
{
Color color = (mColor != null ? mColor : DEFAULT_COLOR);
// Draw the gene line.
int width = getScaledWidthInPixels(mDisplayStart, mDisplayEnd);
group.addRect(new Rectangle(new Point(0, mHeightInPixels/2),
new Dimension(width, 1))).setFill(color);
if (mShowId && mId != null)
{
Rectangle textBoundBox = TextUtil.getStringRect(mId, mLabelFont);
SvgText label = group.addText(mId, mLabelFont, new Point(width + 1, (int) (mHeightInPixels/2f + textBoundBox.getHeight()/2f) - 2));
TooltipJS tooltip = new TooltipJS();
tooltip.addTooltip(label, getTooltipContent());
}
List exons = drawExons();
if (CollectionUtil.hasValues(exons))
{
group.addSubtags(exons);
}
TooltipJS tooltip = new TooltipJS();
tooltip.addTooltip(group, getTooltipContent());
}
return group;
}
//--------------------------------------------------------------------------
public void draw(Graphics2D g2)
{
if (mInDisplayRegion)
{
// Save settings
Paint origPaint = g2.getPaint();
Font origFont = g2.getFont();
Color color = (mColor != null ? mColor : DEFAULT_COLOR);
g2.setPaint(color);
// Draw the gene line.
// System.out.println(getId() + ": " + mDisplayStart + "-" + mDisplayEnd);/////////////////////////
int width = getScaledWidthInPixels(mDisplayStart, mDisplayEnd);
g2.fillRect(0, mHeightInPixels/2, width, 1);
if (mShowId && mId != null)
{
FontRenderContext frc = g2.getFontRenderContext();
TextLayout layout = new TextLayout(mId, mLabelFont, frc);
g2.drawString(mId, width + 1, (int) (mHeightInPixels/2 + layout.getBounds().getHeight()/2));
}
drawExons(g2);
// Restore settings
g2.setPaint(origPaint);
g2.setFont(origFont);
}
}
//--------------------------------------------------------------------------
public int getScaledWidthInPixels()
{
return getScaledWidthInPixels(getDisplayBoundedValue(getLeft()),
getDisplayBoundedValue(getRight()));
}
//--------------------------------------------------------------------------
public void setHeightInPixels(int inValue)
{
mHeightInPixels = inValue;
}
//--------------------------------------------------------------------------
public int getHeightInPixels()
{
return mHeightInPixels;
}
//--------------------------------------------------------------------------
public int getLabelWidthInPixels()
{
if (mLabelWidthInPixels == -1)
{
if (mId != null
&& mId.length() > 0)
{
TextLayout layout = new TextLayout(mId, mLabelFont, sFRC);
mLabelWidthInPixels = (int) layout.getBounds().getWidth() + 1;
}
else
{
mLabelWidthInPixels = 0;
}
}
return mLabelWidthInPixels;
}
//--------------------------------------------------------------------------
public String getTooltipContent()
{
StringBuilderPlus text = new StringBuilderPlus().setDelimiter("
");
if (getId() != null
&& (null == getDescription()
|| getDescription().indexOf(getId()) < 0))
{
text.append(getId());
}
if (getDescription() != null)
{
text.delimitedAppend(getDescription());
}
if (mSeqMappingCoverage != null)
{
text.delimitedAppend("Mapping: ");
if (mSeqMappingCoverage.getMappedLength() != null)
{
text.append(mSeqMappingCoverage.getMappedLength());
}
else
{
text.append("?");
}
text.append("/");
if (mSeqMappingCoverage.getQueryLength() != null)
{
text.append(mSeqMappingCoverage.getQueryLength());
}
else
{
text.append("?");
}
if (mSeqMappingCoverage.getPercentIdentity() != null)
{
text.append(String.format(" (%d%% identity)", mSeqMappingCoverage.getPercentIdentity().intValue()));
}
text.append("");
}
if (getSpecies() != null)
{
text.delimitedAppend("");
text.append(getSpecies().getScientificName());
text.append("");
}
text.delimitedAppend("");
if (getStrand() != null)
{
text.append("Strand: (");
text.append(getStrand().getSymbol());
text.append(") ");
}
text.append("bp.: ");
text.append(getLeft());
text.append(getLeft() != getRight() ?" - " + getRight() : "");
text.append("");
return text.toString();
}
//--------------------------------------------------------------------------
@Override
public boolean equals(Object o2)
{
return (o2 instanceof Gene2D && compareTo((Gene2D)o2) == 0 ? true : false);
}
//--------------------------------------------------------------------------
public int compareTo(Gene2D gene2)
{
int returnValue = 0;
if (this != gene2)
{
if (getLeft() > gene2.getLeft())
{
returnValue = 1;
}
else if (getLeft() < gene2.getLeft())
{
returnValue = -1;
}
else
{
if (getRight() > gene2.getRight())
{
returnValue = 1;
}
else if (getRight() < gene2.getRight())
{
returnValue = -1;
}
else
{
// Somewhat arbitrary but this method will really only need to
// be called to order by position. Returning 0 when the objects
// are not the same can screw up hashes.
returnValue = 1;
}
}
}
return returnValue;
}
//##########################################################################
// PRIVATE METHODS
//##########################################################################
//--------------------------------------------------------------------------
private boolean isExonInDisplayRegion(Exon inExon)
{
boolean returnValue = false;
if (inExon.getRight() >= mDisplayStart
&& inExon.getLeft() <= mDisplayEnd)
{
returnValue = true;
}
return returnValue;
}
//--------------------------------------------------------------------------
private void drawExons(Graphics2D g2)
{
if (mExons != null)
{
// Save settings
Paint origPaint = g2.getPaint();
for (Exon exon : mExons)
{
if (isExonInDisplayRegion(exon))
{
// Calculate exon location
int xOffset = getBoundedAndScaledLocation(exon.getLeft());
// Draw the exon
int width = getScaledWidthInPixels(getDisplayBoundedValue(exon.getLeft()),
getDisplayBoundedValue(exon.getRight()));
if (exon.getColor() != null)
{
g2.setPaint(exon.getColor());
}
g2.fillRect(xOffset, 0, width, mHeightInPixels);
if (exon.getColor() != null)
{
// Restore settings
g2.setPaint(origPaint);
}
}
}
}
}
//--------------------------------------------------------------------------
private List drawExons()
{
List exons = null;
if (mExons != null)
{
exons = new ArrayList<>(mExons.size());
for (Exon exon : mExons)
{
if (isExonInDisplayRegion(exon))
{
// Calculate exon location
int xOffset = getBoundedAndScaledLocation(exon.getLeft());
// Draw the exon
int width = getScaledWidthInPixels(getDisplayBoundedValue(exon.getLeft()),
getDisplayBoundedValue(exon.getRight()));
SvgRect svgExon = new SvgRect(new Rectangle(new Point(xOffset, 0),
new Dimension(width, mHeightInPixels)));
Color color = (mColor != null ? mColor : DEFAULT_COLOR);
if (exon.getColor() != null)
{
color = exon.getColor();
}
svgExon.setFill(color);
exons.add(svgExon);
}
}
}
return exons;
}
//--------------------------------------------------------------------------
private int getScaledWidthInPixels(int inStart, int inEnd)
{
int leftPixel = (int) (inStart * mXScalingFactor);
int rightPixel = (int) (inEnd * mXScalingFactor);
int width = rightPixel - leftPixel + 1;
// System.out.println("Exon: " + inStart + "-" + inEnd + " " + leftPixel + "-" + rightPixel + " " + mXScalingFactor + " (" + width + ")");/////////////////////////
// int width = Math.abs((int) ((inEnd - inStart + 1) * mXScalingFactor));
// Minimum width of 1
return (0 >= width ? 1 : width);
}
//--------------------------------------------------------------------------
private int getBoundedAndScaledLocation(int inValue)
{
int bound = (int) (getDisplayBoundedValue(inValue) * mXScalingFactor);
int start = (int) (mDisplayStart * mXScalingFactor);
return bound - start;
// return (int)((getDisplayBoundedValue(inValue) - mDisplayStart) * mXScalingFactor);
}
//--------------------------------------------------------------------------
private int getDisplayBoundedValue(int inValue)
{
int value = inValue;
if (value < mDisplayStart)
{
value = mDisplayStart;
}
else if (value > mDisplayEnd)
{
value = mDisplayEnd;
}
return value;
}
}