com.hfg.bio.phylogeny.PhyloNode 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.bio.phylogeny;
import java.util.*;
import java.util.List;
import java.util.regex.Pattern;
import java.awt.*;
import com.hfg.util.collection.CollectionUtil;
import com.hfg.util.StringUtil;
import com.hfg.network.Edge;
//------------------------------------------------------------------------------
/**
Object representation of a node in a Newick format phylogenetic tree.
Does not work with negative distance values.
@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 PhyloNode
{
private List> mEdges;
private String mId;
private String mLabel;
private Color mColor;
private Integer mLineIndex;
private Integer mVericalLineStartIndex;
private Integer mVericalLineEndIndex;
private Double mAngle;
private static SubnodeCountComparator sSubnodeCountComparator = new PhyloNode().new SubnodeCountComparator();
private static final Pattern sWhitespacePattern = Pattern.compile("\\s+");
// cached values
private Float mDistanceFromRoot;
//--------------------------------------------------------------------------
public PhyloNode setId(int inValue)
{
return setId(inValue + "");
}
//--------------------------------------------------------------------------
public PhyloNode setId(String inValue)
{
mId = inValue;
return this;
}
//--------------------------------------------------------------------------
public String getId()
{
return mId;
}
//--------------------------------------------------------------------------
public PhyloNode setLabel(String inValue)
{
mLabel = inValue;
return this;
}
//--------------------------------------------------------------------------
public String getLabel()
{
return mLabel;
}
//--------------------------------------------------------------------------
public PhyloNode setColor(Color inValue)
{
mColor = inValue;
return this;
}
//--------------------------------------------------------------------------
public Color getColor()
{
return mColor;
}
//--------------------------------------------------------------------------
public List> getEdges()
{
return mEdges;
}
//--------------------------------------------------------------------------
public void addEdge(PhyloNode in2ndNode, Float inDistance)
{
if (null == mEdges)
{
mEdges = new ArrayList<>(3);
}
Edge edge = new Edge<>(this, in2ndNode, inDistance);
mEdges.add(edge);
in2ndNode.addEdge(edge);
}
//--------------------------------------------------------------------------
public PhyloNode setAngle(Double inAngleInRadians)
{
mAngle = inAngleInRadians;
return this;
}
//--------------------------------------------------------------------------
public Double getAngle()
{
return mAngle;
}
//--------------------------------------------------------------------------
protected void addEdge(Edge inEdge)
{
if (null == mEdges)
{
mEdges = new ArrayList<>(3);
}
mEdges.add(inEdge);
}
//--------------------------------------------------------------------------
protected boolean removeEdge(Edge inEdge)
{
boolean result = false;
if (mEdges != null)
{
result = mEdges.remove(inEdge);
// Remove the edge from the other node as well.
if (this == inEdge.getFrom())
{
inEdge.setFrom(null);
if (inEdge.getTo() != null) inEdge.getTo().removeEdge(inEdge);
}
else if (this == inEdge.getTo())
{
inEdge.setTo(null);
if (inEdge.getFrom() != null) inEdge.getFrom().removeEdge(inEdge);
}
}
return result;
}
//--------------------------------------------------------------------------
protected boolean removeEdgeFrom(PhyloNode inNode)
{
boolean result = false;
if (mEdges != null)
{
for (Edge edge : mEdges)
{
if (edge.getFrom() == inNode)
{
mEdges.remove(edge);
edge.setTo(null);
// Remove the edge from the other node as well.
edge.getFrom().removeEdge(edge);
result = true;
break;
}
}
}
return result;
}
//--------------------------------------------------------------------------
public boolean isLeaf()
{
return null == mEdges || mEdges.size() < 2;
}
//--------------------------------------------------------------------------
public void makeRoot()
{
// Fan out through the tree setting the directionality to flow from the root.
for (Edge edge : mEdges)
{
if (edge.getFrom() != this) edge.switchDirection();
edge.getTo().setRootDirection(edge);
}
}
//--------------------------------------------------------------------------
protected void setRootDirection(Edge inEdge)
{
for (Edge edge : mEdges)
{
if (edge == inEdge) continue;
if (edge.getFrom() != this) edge.switchDirection();
edge.getTo().setRootDirection(edge);
}
}
/*
//--------------------------------------------------------------------------
public Float getMaxDistance()
{
float maxChildDistance = 0;
if (CollectionUtil.hasValues(mChildNodes))
{
for (PhyloNode child : mChildNodes)
{
float childDistance = child.getMaxDistance();
if (childDistance > maxChildDistance)
{
maxChildDistance = childDistance;
}
}
}
return maxChildDistance + (getDistance() != null ? getDistance() : 0);
}
//--------------------------------------------------------------------------
public Float getDistance()
{
return mDistance;
}
*/
//--------------------------------------------------------------------------
public PhyloNode getParentNode()
{
PhyloNode parent = null;
Edge parentEdge = getParentEdge();
if (parentEdge != null)
{
parent = parentEdge.getFrom();
}
return parent;
}
//--------------------------------------------------------------------------
public List getChildNodes()
{
List childNodes = null;
if (CollectionUtil.hasValues(mEdges))
{
for (Edge edge : mEdges)
{
if (edge.getFrom() == this)
{
if (null == childNodes)
{
childNodes = new ArrayList<>(4);
}
childNodes.add(edge.getTo());
}
}
}
return childNodes;
}
//--------------------------------------------------------------------------
/**
Returns the root-facing edge.
@return the root-facing Edge for the node
*/
public Edge getParentEdge()
{
Edge parentEdge = null;
if (CollectionUtil.hasValues(mEdges))
{
for (Edge edge : mEdges)
{
if (edge.getTo() == this)
{
parentEdge = edge;
break;
}
}
}
return parentEdge;
}
//--------------------------------------------------------------------------
/**
Returns the leaf-facing edges.
@return the leaf-facing Edge for the node
*/
public List> getLeafFacingEdges()
{
List> leafFacingEdges = new ArrayList<>(3);
if (! isLeaf())
{
for (Edge edge : mEdges)
{
if (edge.getFrom() == this)
{
leafFacingEdges.add(edge);
}
}
}
return leafFacingEdges;
}
//--------------------------------------------------------------------------
public void orderEdgesByLeafCount()
{
if (! isLeaf())
{
List> leafFacingEdges = getLeafFacingEdges();
if (CollectionUtil.hasValues(leafFacingEdges))
{
Collections.sort(leafFacingEdges, sSubnodeCountComparator);
}
List> newEdgeList = new ArrayList<>(mEdges.size());
for (Edge edge : mEdges)
{
if (edge.getFrom() != this)
{
newEdgeList.add(edge);
}
}
newEdgeList.addAll(leafFacingEdges);
mEdges = newEdgeList;
}
}
//--------------------------------------------------------------------------
public Float getDistanceFromRoot()
{
if (null == mDistanceFromRoot)
{
float distance = 0;
Edge parentEdge = getParentEdge();
if (parentEdge != null)
{
do
{
if (parentEdge.getDistance() != null) distance += parentEdge.getDistance();
}
while (parentEdge.getFrom() != null
&& (parentEdge = parentEdge.getFrom().getParentEdge()) != null);
}
mDistanceFromRoot = distance;
}
return mDistanceFromRoot;
}
//--------------------------------------------------------------------------
public Float getMaxDistanceToLeaf()
{
float maxDistance = 0;
if (! isLeaf())
{
for (Edge edge : mEdges)
{
if (edge.getFrom() == this)
{
float childDistance = 0;
if (edge.getDistance() != null) childDistance += edge.getDistance();
PhyloNode child = edge.getTo();
if (! child.isLeaf()) childDistance += child.getMaxDistanceToLeaf();
if (childDistance > maxDistance)
{
maxDistance = childDistance;
}
}
}
}
return maxDistance;
}
//--------------------------------------------------------------------------
public int getSubnodeCount()
{
int subnodeCount = 0;
if (! isLeaf())
{
for (Edge edge : mEdges)
{
if (edge.getFrom() == this)
{
subnodeCount += 1 + edge.getTo().getSubnodeCount();
}
}
}
return subnodeCount;
}
//--------------------------------------------------------------------------
/**
Calculates the distance between two nodes in the tree.
@param inNode2 the node to calculate the distance to
@return the distance between this node and the specified node
*/
// Three cases:
// 1. node2 is above node1
// 2. node2 is below node1
// 3. node2 and node1 have a common ancestor
public Float distanceTo(PhyloNode inNode2)
{
float result = 0;
float distance1 = 0;
float distance2 = 0;
Map map1 = new HashMap<>();
boolean done = false;
Edge parentEdge = getParentEdge();
if (parentEdge != null)
{
do
{
map1.put(parentEdge.getTo(), distance1);
if (parentEdge.getTo() == inNode2)
{
result = distance1;
done = true;
break;
}
if (parentEdge.getDistance() != null) distance1 += parentEdge.getDistance();
}
while (parentEdge.getFrom() != null
&& (parentEdge = parentEdge.getFrom().getParentEdge()) != null);
}
if (!done)
{
parentEdge = inNode2.getParentEdge();
if (parentEdge != null)
{
do
{
if (parentEdge.getDistance() != null) distance2 += parentEdge.getDistance();
Float distance = map1.get(parentEdge.getFrom());
if (distance != null)
{
result = distance + distance2;
done = true;
break;
}
}
while (parentEdge.getFrom() != null
&& (parentEdge = parentEdge.getFrom().getParentEdge()) != null);
}
if (!done)
{
result = distance1 + distance2;
}
}
return result;
}
//--------------------------------------------------------------------------
@Override
public String toString()
{
StringBuilder buffer = new StringBuilder();
if (! isLeaf())
{
buffer.append("(");
for (Edge edge : mEdges)
{
if (edge.getFrom() == this)
{
PhyloNode child = edge.getTo();
if (buffer.length() > 1) buffer.append(",");
buffer.append(child.toString());
}
}
buffer.append(")");
}
if (StringUtil.isSet(getLabel()))
{
// Convert spaces to underscores.
String label = StringUtil.replaceAllRegexp(mLabel, sWhitespacePattern, "_");
// Convert colons to underscores.
label = StringUtil.replaceAll(label, ":", "_");
buffer.append(label);
}
Edge parentEdge = getParentEdge();
if (parentEdge != null
&& parentEdge.getDistance() != null)
{
buffer.append(":" + parentEdge.getDistance());
}
return buffer.toString();
}
//--------------------------------------------------------------------------
protected void setLineIndex(int inValue)
{
mLineIndex = inValue;
}
//--------------------------------------------------------------------------
protected Integer getLineIndex()
{
return mLineIndex;
}
//--------------------------------------------------------------------------
protected void setVerticalLineStartIndex(int inValue)
{
mVericalLineStartIndex = inValue;
}
//--------------------------------------------------------------------------
protected Integer getVericalLineStartIndex()
{
return mVericalLineStartIndex;
}
//--------------------------------------------------------------------------
protected void setVerticalLineEndIndex(int inValue)
{
mVericalLineEndIndex = inValue;
}
//--------------------------------------------------------------------------
protected Integer getVericalLineEndIndex()
{
return mVericalLineEndIndex;
}
private class SubnodeCountComparator implements Comparator>
{
public int compare(Edge edge1, Edge edge2)
{
int node1SubnodeCount = edge1.getTo().getSubnodeCount();
int node2SubnodeCount = edge2.getTo().getSubnodeCount();
if (node1SubnodeCount > node2SubnodeCount) return -1;
if (node1SubnodeCount < node2SubnodeCount) return 1;
if (edge1.getTo().getLabel() != null)
{
return edge1.getTo().getLabel().compareTo(edge2.getTo().getLabel());
}
return 0;
}
}
}