/*
* Copyright (C) 2011 apurv
*
* This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
*/
package nescent.phylogeoref.processor;
import java.awt.Color;
import static java.lang.System.out;
import java.math.BigDecimal;
import java.util.Vector;
import nescent.phylogeoref.processor.utility.ComputeUtility;
import nescent.phylogeoref.validator.PhylogenyValidator;
import org.forester.phylogeny.Phylogeny;
import org.forester.phylogeny.PhylogenyMethods;
import org.forester.phylogeny.PhylogenyNode;
import org.forester.phylogeny.data.BranchColor;
import org.forester.phylogeny.data.BranchData;
import org.forester.phylogeny.data.Distribution;
import org.forester.phylogeny.data.NodeData;
import org.forester.phylogeny.iterators.PhylogenyNodeIterator;
/**
* Processes this phylogeny and assigns color and lat/long values to internal nodes.(Weighted)
* Also Validates the phylogeny.<br>
* @author apurv
*/
public class WeightedPhylogenyProcessor implements PhylogenyProcessor{
//To be used in the formula HTU height = a +(n-1)*b
private final static long a = 198000;
private final static long b = 66000;
//Do not change this value, same value has also been used in KmlUtility.
private final static double UNDEFINED = -1.0d;
private PhylogenyValidator validator;
private boolean weightedAvg;
protected WeightedPhylogenyProcessor(){
this.weightedAvg = true;
validator = new PhylogenyValidator(this.weightedAvg);
}
/**
* Phylogenifies the phylogeny
* @param phylogeny
*/
@Override
public void phylogenify(Phylogeny phylogeny){
validator.validatePhylogeny(phylogeny);
assignCoordinatesToInternalNodes(phylogeny);
assignColorToInternalNodes(phylogeny);
}
/**
* Assigns coordinates to internal nodes of the phylogeny.
*
* Most of this code is a copy of Kathryn's code from last year.(Calc3DTree.java)
* @param phylogeny
*/
private void assignCoordinatesToInternalNodes(Phylogeny phylogeny){
int heightOfTree = PhylogenyMethods.calculateMaxDepth(phylogeny);
for( PhylogenyNodeIterator it = phylogeny.iteratorPostorder(); it.hasNext();) {
PhylogenyNode node = it.next();
NodeData data = node.getNodeData();
if ( node.isInternal() ) {
data.setDistribution(new Distribution(node.getNodeName()));
Distribution dist = data.getDistribution();
double meanLat = findMeanChildLatitude(node);
double meanLong = findMeanChildLongitude(node);
dist.setLatitude(new BigDecimal(meanLat));
dist.setLongitude(new BigDecimal(meanLong));
int nodeDepth = PhylogenyMethods.calculateDepth(node);
int n = heightOfTree - nodeDepth; //the height/level of node.
long altitude = a + (n-1)*b;
BigDecimal alt = new BigDecimal(altitude);
dist.setAltitude(alt);
}
}
}
/**
* Finds the mean latitude of the children of this node.
* Should be called only for internal nodes.
* @param node
* @return
*/
private double findMeanChildLatitude(PhylogenyNode node){
double meanLat = 0.0;
int numValidChildren = 0;
double sigmaLat = 0.0;
double sigmaWt = 0.0;
int numChildren = node.getNumberOfDescendants();
for (int i=0; i < numChildren ; i++){
PhylogenyNode childNode = node.getChildNode(i);
NodeData childData = childNode.getNodeData();
Distribution childDist = childData.getDistribution();
BigDecimal childLat = BigDecimal.ZERO;
if(childDist != null){
childLat = childDist.getLatitude();
if(childLat.doubleValue() != UNDEFINED){
numValidChildren++;
double time = childNode.getDistanceToParent();
double weight = 1.0/time;
sigmaLat += childLat.doubleValue()*weight;
sigmaWt += weight;
}
}
}
if(numValidChildren == 0){
meanLat = UNDEFINED;
}else{
meanLat = sigmaLat/sigmaWt;
}
return meanLat;
}
/**
* Finds the mean longitude of the children of this node.
* Should be called only for internal nodes.
* @param node
* @return
*/
private double findMeanChildLongitude(PhylogenyNode node){
double meanLon = 0.0;
int numValidChildren = 0;
int numChildren = node.getNumberOfDescendants();
Vector<Double> lonVector = new Vector<Double>();
Vector<Double> timeVector = new Vector<Double>();
for (int i=0; i < numChildren ; i++){
PhylogenyNode childNode = node.getChildNode(i);
NodeData childData = childNode.getNodeData();
Distribution childDist = childData.getDistribution();
BigDecimal childLon = BigDecimal.ZERO;
if(childDist != null){
childLon = childDist.getLongitude();
if(childLon.doubleValue() != UNDEFINED){
numValidChildren++;
lonVector.add(childLon.doubleValue());
timeVector.add(childNode.getDistanceToParent());
}
}
}
if(numValidChildren > 1){
meanLon = ComputeUtility.findMeanPosition(lonVector);
}else if( numValidChildren == 1 ){
meanLon = node.getChildNode1().getNodeData().getDistribution().getLongitude().doubleValue();
}else{
meanLon = UNDEFINED;
}
return meanLon;
}
/**
* Assigns color to internal edges of the phylogeny.
* Color of parent branch is taken to be the arithmetic mean of colors of child branches.
* @param phylogeny
*/
private void assignColorToInternalNodes(Phylogeny phylogeny){
for( PhylogenyNodeIterator it = phylogeny.iteratorPostorder(); it.hasNext();) {
PhylogenyNode node = it.next();
int numChildren = 0;
int rSum = 0;
int gSum = 0;
int bSum = 0;
if ( node.isInternal() ) {
double sigmaWt = 0.0;
for (int i=0; i < node.getNumberOfDescendants(); i++){
PhylogenyNode childNode = node.getChildNode(i);
BranchColor childBranchColor = childNode.getBranchData().getBranchColor();
if(childBranchColor !=null){
Color childColor = childBranchColor.getValue();
double time = childNode.getDistanceToParent();
double weight = 1.0/time;
sigmaWt+= weight;
rSum+= weight*childColor.getRed();
gSum+= weight*childColor.getGreen();
bSum+= weight*childColor.getBlue();
numChildren++;
}
}
int meanR = 0;
int meanG = 0;
int meanB = 0;
if(numChildren > 1){
meanR = (int) (rSum/sigmaWt);
meanG = (int) (gSum/sigmaWt);
meanB = (int) (bSum/sigmaWt);
Color meanColor = new Color(meanR,meanG,meanB);
BranchData branchData = node.getBranchData();
branchData.setBranchColor(new BranchColor(meanColor));
}else if( numChildren == 1 ){
PhylogenyNode childNode = node.getChildNode1();
BranchColor childBranchColor = childNode.getBranchData().getBranchColor();
if(childBranchColor !=null){
Color childColor = childBranchColor.getValue();
BranchData branchData = node.getBranchData();
branchData.setBranchColor(new BranchColor(childColor));
}
}
}
}
}
}