/*
* ContinuousKML.java
*
* Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard
*
* This file is part of BEAST.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership and licensing.
*
* BEAST 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
* of the License, or (at your option) any later version.
*
* BEAST 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 BEAST; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
package dr.app.phylogeography.tools;
import dr.stats.DiscreteStatistics;
import jebl.evolution.trees.RootedTree;
import jebl.evolution.graphs.Node;
/**
* @author Philippe Lemey
* @author Andrew Rambaut
* @author Marc A. Suchard
*/
public class ContinuousKML {
//input related variables
RootedTree treeToExport;
String traitName;
String latitudeName;
String longitudeName;
String documentName;
boolean tipsSampled = true; //in case the tips are sampled within a polygon, we would like to see their contours
// variables shared by rectangle, triangle and surfacetree
double branchWidthConstant = 3.0; // the width of branches will be posterior*branchWidthMultiplier+branchWidthConstant
double branchWidthMultiplier = 5.0;
// variables shared by rectangle and triangle
double plotHeight = 2000000;
double fraction = 0.05; // additional fraction of the total plotHeight for the root branch
// rectangle tree variables
boolean iniVisi_RT = false; // initial visibility of rectangle tree
boolean usePosterior_RT = false; // use posterior probabilities for branch width
double branchWidth_RT = 5.0; // branch width if posterior probabilities are not used
boolean useRates_RT = true; // use rates to color branch
boolean useLogRates_RT = false; // use rates to color branch
boolean useHeights_RT = false; // use heights (time) to color branches
String startBranchColor_RT = "ffffff"; //blue=B36600
String endBranchColor_RT = "0000FF"; //red=0000FF
String branchColor_RT = "ffffff"; // branch color if color range based on rates is not used
double opacity_RT = 1.0;
double branchChop_RT = 1.0;
// triangle tree variables
boolean iniVisi_TT = false; // initial visibility of rectangle tree
boolean usePosterior_TT = false; // use posterior probabilities for branch width
double branchWidth_TT = 5.0; // branch width if posterior probabilities are not used
boolean useRates_TT = true; // use rates to color branch
boolean useLogRates_TT = false; // use rates to color branch
String startBranchColor_TT = "0000FF"; //blue=B36600
String endBranchColor_TT = "ffffff"; //red=0000FF
String branchColor_TT = "ffffff"; // branch color if color range based on rates is not used
double opacity_TT = 1.0;
// surface tree variables
double divider = 10; // this is to chop up the branches of the surface tree in 'divider' segments
boolean iniVisi_ST = true; // initial visibility of rectangle tree
boolean usePosterior_ST = false; // use posterior probabilities for branch width
double branchWidth_ST = 6.0; // branch width if posterior probabilities are not used
boolean useHeights_ST = true; // use heights (time) to color branches
String startBranchColor_ST = "FF00FF"; //"00FF00"=green,"FF00FF"=magenta startpoint is the youngest node
String endBranchColor_ST = "FBC042"; //"00F1D6"=yellow,blue="B36600",carribean="FBC042"
String branchColor_ST = "ffffff"; // branch color if rates are not used
double opacity_ST = 1.0;
boolean arcBranches = true; // branches are arcs with heights proportional to the time length of the branches
double altitudeFactor = 100; // this is the factor with which to multiply the time of the branch to get the altitude for that branch in the surface Tree
// tree slice variables
String startBranchColor_TS = "00FF00"; //green, startpoint is the youngest node
String endBranchColor_TS = "00F1D6"; //yellow
// taxa variables
boolean iniVisi_taxa = false;
// spade variables
boolean iniVisi_spades = false;
boolean useHeights_spades = true; // use heights (time) to color branches
String startSpadeColor = "00FF00"; //green, startpoint is the youngest node
String endSpadeColor = "00F1D6"; //yellow
String spadeColor = "ffffff"; // branch color if heights are not used
double opacity_spades = 1.0;
//specifically for tips
boolean iniVisi_tipSpades = false;
boolean useHeights_tipSpades = true; // use heights (time) to color branches
String startTipSpadeColor = "00F1D6"; //green, startpoint is the youngest node
String endTipSpadeColor = "00FF00"; //yellow
String tipSpadeColor = "ffffff"; // branch color if heights are not used
double opacity_tipSpades = 1.0;
// general location HPD variables
String locationHPDpercentage = "80%";
// contour variables
boolean iniVisi_contours = false;
boolean useHeights_contours = true; // use heights (time) to color branches
String startContourColor = "ffffff"; //green, startpoint is the youngest node
String endContourColor = "0000FF"; //yellow
String contourColor = "ffffff"; // branch color if heights are not used
double opacity_contours = 0.6;
//specifically for tips
boolean iniVisi_tipContours = false;
boolean useHeights_tipContours = true; // use heights (time) to color branches
String startTipContourColor = "00F1D6"; //green, startpoint is the youngest node
String endTipContourColor = "00FF00"; //yellow
String tipContourColor = "ffffff"; // branch color if heights are not used
double opacity_tipContours = 0.6;
// ground spade variables
boolean iniVisi_groundSpades = false;
boolean useHeights_groundSpades = true; // use heights (time) to color branches
String startgroundSpadeColor = "00FF00"; //green, startpoint is the youngest node
String endgroundSpadeColor = "00F1D6"; //yellow
String groundSpadeColor = "ffffff"; // branch color if heights are not used
double opacity_groundSpades = 0.6;
//specifically for tips
boolean iniVisi_groundTipSpades = false;
boolean useHeights_groundTipSpades = true; // use heights (time) to color branches
String startgroundTipSpadeColor = "00FF00"; //green, startpoint is the youngest node
String endgroundTipSpadeColor = "00F1D6"; //yellow
String groundTipSpadeColor = "ffffff"; // branch color if heights are not used
double opacity_groundTipSpades = 0.6;
// ground contour variables
boolean contoursAndNotSpades = true;
boolean iniVisi_groundContours = true;
boolean useHeights_groundContours = true; // use heights (time) to color branches
String startgroundContourColor = "00FF00"; //green, startpoint is the youngest node
String endgroundContourColor = "00F1D6"; //yellow
String groundContourColor = "ffffff"; // branch color if rates are not used
double opacity_groundContours = 0.5;
//specifically for tips
boolean iniVisi_groundTipContours = true;
boolean useHeights_groundTipContours = true; // use heights (time) to color branches
String startgroundTipContourColor = "00FF00"; //green, startpoint is the youngest node
String endgroundTipContourColor = "00F1D6"; //yellow
String groundTipContourColor = "ffffff"; // branch color if rates are not used
double opacity_groundTipContours = 1.0;
// diamond variables
boolean iniVisi_diamonds = false;
//projections variables
boolean iniVisi_projections = false;
// additional variables
double[] rateMinMaxMedian; // used to calibrate the color range for the branches
double[] heightMinAndMax; // used to calibrate the color range for branches or node hpd polygons
double mostRecentDate; // required to convert heights to calendar dates
boolean ancient = false;
//everything is written to separate buffers, and than collected in structured KML document by compileBuffer
StringBuffer rectangleTreeBuffer = new StringBuffer();
StringBuffer triangleTreeBuffer = new StringBuffer();
StringBuffer spadeBuffer = new StringBuffer();
StringBuffer spadeTipsBuffer = new StringBuffer();
StringBuffer groundSpadeBuffer = new StringBuffer();
StringBuffer groundSpadeTipsBuffer = new StringBuffer();
StringBuffer contourBuffer = new StringBuffer();
StringBuffer contourTipsBuffer = new StringBuffer();
StringBuffer groundContourBuffer = new StringBuffer();
StringBuffer groundContourTipsBuffer = new StringBuffer();
StringBuffer diamondBuffer = new StringBuffer();
StringBuffer diamondTipsBuffer = new StringBuffer();
StringBuffer taxaBuffer = new StringBuffer();
StringBuffer surfaceTreeBuffer = new StringBuffer();
StringBuffer projectionsBuffer = new StringBuffer();
StringBuffer styleBuffer = new StringBuffer();
StringBuffer treeSliceBuffer = new StringBuffer();
StringBuffer contourSliceBuffer = new StringBuffer();
double totalHPDarea;
public ContinuousKML(){
}
public ContinuousKML(RootedTree tree, String name, double height, double date, String coordinateName){
treeToExport = tree;
plotHeight = height;
documentName = name;
mostRecentDate = date;
latitudeName = coordinateName+"1";
longitudeName = coordinateName+"2";
traitName = coordinateName;
Node rootNode = treeToExport.getRootNode();
if ((Object)rootNode.getAttribute(traitName+"_"+locationHPDpercentage+"HPD_modality") != null) {
contoursAndNotSpades = true;
}
if (mostRecentDate - treeToExport.getHeight(treeToExport.getRootNode()) < 0) {
ancient = true;
}
}
public void writeTreeToKML() {
double scaleFactor = plotHeight / treeToExport.getHeight(treeToExport.getRootNode());
heightMinAndMax = getHeightMinAndMax(treeToExport);
if (useRates_RT || useRates_TT) {
rateMinMaxMedian = getRateMinMaxMedian(treeToExport, useLogRates_TT);
}
// convert initial visibility booleans to int (0 = false, 1 = true)
int visibility_RT;
if (iniVisi_RT) { visibility_RT = 1; } else { visibility_RT = 0; }
int visibility_TT;
if (iniVisi_TT) { visibility_TT = 1; } else { visibility_TT = 0; }
int visibility_ST;
if (iniVisi_ST) { visibility_ST = 1; } else { visibility_ST = 0; }
int visibility_taxa;
if (iniVisi_taxa) { visibility_taxa = 1; } else { visibility_taxa = 0; }
//
int nodeNumber = 0;
for (Node node : treeToExport.getNodes()) {
nodeNumber++;
Double longitude = (Double)node.getAttribute(longitudeName);
Double latitude = (Double)node.getAttribute(latitudeName);
double altitude = (treeToExport.getHeight(node)*scaleFactor);
if (!treeToExport.isRoot(node)) {
Node parentNode = treeToExport.getParent(node);
Double parentLongitude = (Double)parentNode.getAttribute(longitudeName);
if (parentLongitude == null) {
throw new RuntimeException("Longitude attribute, " + latitudeName + ", not found in tree");
}
Double parentLatitude = (Double)parentNode.getAttribute(latitudeName);
if (parentLatitude == null) {
throw new RuntimeException("Latitude attribute, " + latitudeName + ", not found in tree");
}
double parentAltitude = (treeToExport.getHeight(parentNode)*scaleFactor);
if (plotHeight > 0) {
rectangleTreeBuffer.append("\t\t<Placemark>\r");
rectangleTreeBuffer.append("\t\t\t<visibility>"+visibility_RT+"</visibility>\r");
rectangleTreeBuffer.append("\t\t\t<name>rectangleTreeBranch"+ nodeNumber +"</name>\r");
rectangleTreeBuffer.append("\t\t\t<styleUrl>#rectangleTreeBranch"+ nodeNumber +"_style</styleUrl>\r");
rectangleTreeBuffer.append("\t\t\t<LineString>\r");
rectangleTreeBuffer.append("\t\t\t\t<tessellate>1</tessellate>\r");
rectangleTreeBuffer.append("\t\t\t\t<altitudeMode>relativeToGround</altitudeMode>\r");
rectangleTreeBuffer.append("\t\t\t\t<coordinates>\r");
rectangleTreeBuffer.append("\t\t\t\t\t"+longitude+","+latitude+","+altitude+"\r");
rectangleTreeBuffer.append("\t\t\t\t\t"+longitude+","+latitude+","+parentAltitude+"\r");
if (branchChop_RT > 0) {
double longInterval = parentLongitude - longitude;
double latInterval = parentLatitude - latitude;
double steps;
double longStepSize;
double latStepSize;
if (Math.abs(longInterval) > Math.abs(latInterval)) {
steps = (Math.abs(longInterval)/branchChop_RT) - 1;
if (longInterval > 0) {
longStepSize = branchChop_RT;
} else {
longStepSize = -branchChop_RT;
}
latStepSize = latInterval/(steps + 1);
} else {
steps = (Math.abs(latInterval)/branchChop_RT) - 1;
if (latInterval > 0) {
latStepSize = branchChop_RT;
} else {
latStepSize = -branchChop_RT;
}
longStepSize = longInterval/(steps + 1);
}
for (int x = 0; x < steps; x++) {
rectangleTreeBuffer.append("\t\t\t\t\t"+(longitude + (x+1)*longStepSize)+","+(latitude + (x+1)*latStepSize)+","+parentAltitude+"\r");
}
}
rectangleTreeBuffer.append("\t\t\t\t\t"+parentLongitude+","+parentLatitude+","+parentAltitude+"\r");
rectangleTreeBuffer.append("\t\t\t\t</coordinates>\r");
rectangleTreeBuffer.append("\t\t\t</LineString>\r");
rectangleTreeBuffer.append("\t\t</Placemark>\r");
triangleTreeBuffer.append("\t\t<Placemark>\r");
triangleTreeBuffer.append("\t\t\t<visibility>"+visibility_TT+"</visibility>\r");
triangleTreeBuffer.append("\t\t\t<name>triangleTreeBranch"+ nodeNumber +"</name>\r");
triangleTreeBuffer.append("\t\t\t<styleUrl>#triangleTreeBranch"+ nodeNumber +"_style</styleUrl>\r");
triangleTreeBuffer.append("\t\t\t<LineString>\r");
triangleTreeBuffer.append("\t\t\t\t<altitudeMode>relativeToGround</altitudeMode>\r");
triangleTreeBuffer.append("\t\t\t\t<coordinates>\r");
triangleTreeBuffer.append("\t\t\t\t\t"+longitude+","+latitude+","+altitude+"\r");
triangleTreeBuffer.append("\t\t\t\t\t"+parentLongitude+","+parentLatitude+","+parentAltitude+"\r");
triangleTreeBuffer.append("\t\t\t\t</coordinates>\r");
triangleTreeBuffer.append("\t\t\t</LineString>\r");
triangleTreeBuffer.append("\t\t</Placemark>\r");
}
//variables required for chopping up the branches of the surface Tree
double maxAltitude = (treeToExport.getHeight(parentNode) - treeToExport.getHeight(node))*altitudeFactor;
double latitudeDifference = parentLatitude - latitude;
double longitudeDifference = parentLongitude - longitude;
surfaceTreeBuffer.append("\t<Folder>\r");
surfaceTreeBuffer.append("\t\t\t<name>surfaceTreeBranch"+ nodeNumber +"</name>\r");
for (int a = 0; a < divider; a ++) {
surfaceTreeBuffer.append("\t\t<Placemark>\r");
surfaceTreeBuffer.append("\t\t\t<visibility>"+visibility_ST+"</visibility>\r");
surfaceTreeBuffer.append("\t\t\t<name>surfaceTreeBranch"+ nodeNumber +"_part"+(a+1)+"</name>\r");
surfaceTreeBuffer.append("\t\t\t<TimeSpan>\r");
//convert height of the branch segment to a real date (based on th date for the most recent sample)
double date = mostRecentDate - (treeToExport.getHeight(node) + (a + 1) *
((treeToExport.getHeight(parentNode) - (treeToExport.getHeight(node)))/divider));
String[] yearMonthDay = convertToYearMonthDay(date);
if (ancient) {
surfaceTreeBuffer.append("\t\t\t\t<begin>"+Math.round(date)+"</begin>\r");
} else {
surfaceTreeBuffer.append("\t\t\t\t<begin>"+yearMonthDay[0]+"-"+yearMonthDay[1]+"-"+yearMonthDay[2]+"</begin>\r");
}
surfaceTreeBuffer.append("\t\t\t</TimeSpan>\r");
surfaceTreeBuffer.append("\t\t\t<styleUrl>#surfaceTreeBranch"+ nodeNumber +"_part"+(a+1)+"_style</styleUrl>\r");
// surfaceTreeBuffer.append("\t\t\t<styleUrl>#surfaceTreeBranch"+nodeNumber+"_style</styleUrl>\r");
surfaceTreeBuffer.append("\t\t\t<LineString>\r");
if (arcBranches) {
surfaceTreeBuffer.append("\t\t\t\t<altitudeMode>absolute</altitudeMode>\r");
surfaceTreeBuffer.append("\t\t\t\t<tessellate>1</tessellate>\r");
} else {
surfaceTreeBuffer.append("\t\t\t\t<altitudeMode>clampToGround</altitudeMode>\r");
}
surfaceTreeBuffer.append("\t\t\t\t<coordinates>\r");
surfaceTreeBuffer.append("\t\t\t\t\t"+(longitude+a*(longitudeDifference/divider))+","+
(latitude+a*(latitudeDifference/divider))+","+
(maxAltitude*Math.sin(Math.acos(1 - a*(1.0/(divider/2.0)))))+"\r");
surfaceTreeBuffer.append("\t\t\t\t\t"+(longitude+(a+1)*(longitudeDifference/divider))+","+
(latitude+(a+1)*(latitudeDifference/divider))+","+
(maxAltitude*Math.sin(Math.acos(1 - (a+1)*(1.0/(divider/2.0)))))+"\r");
surfaceTreeBuffer.append("\t\t\t\t</coordinates>\r");
surfaceTreeBuffer.append("\t\t\t</LineString>\r");
surfaceTreeBuffer.append("\t\t</Placemark>\r");
styleBuffer.append("\t<Style id=\"surfaceTreeBranch"+ nodeNumber +"_part"+(a+1)+"_style\">\r");
styleBuffer.append("\t\t<LineStyle>\r");
if (usePosterior_ST) {
if (treeToExport.isExternal(node)) {
styleBuffer.append("\t\t\t<width>"+(branchWidthConstant+branchWidthMultiplier)+"</width>\r");
} else {
Double posterior = (Double)node.getAttribute("posterior");
styleBuffer.append("\t\t\t<width>"+(branchWidthConstant+posterior*branchWidthMultiplier)+"</width>\r");
}
} else {
styleBuffer.append("\t\t\t<width>"+branchWidth_ST+"</width>\r");
}
if (useHeights_ST){
styleBuffer.append("\t\t\t<color>"+"FF"+getKMLColor((treeToExport.getHeight(node) + (a + 1) *
((treeToExport.getHeight(parentNode) - (treeToExport.getHeight(node)))/divider)),
heightMinAndMax, startBranchColor_ST, endBranchColor_ST)+"</color>\r");
} else {
styleBuffer.append("\t\t\t<color>"+"FF"+branchColor_ST+"</color>\r");
}
styleBuffer.append("\t\t</LineStyle>\r");
styleBuffer.append("\t</Style>\r");
}
surfaceTreeBuffer.append("\t</Folder>\r");
//this writes placemarks on the surface for the taxa, and writes out projections from the tips to the surface
if (treeToExport.isExternal(node)) {
taxaBuffer.append("\t\t\t<Placemark>\r");
taxaBuffer.append("\t\t\t<visibility>"+visibility_taxa+"</visibility>\r");
taxaBuffer.append("\t\t\t\t<name>"+treeToExport.getTaxon(node).getName()+"</name>\r");
taxaBuffer.append("\t\t\t\t<Point>\r");
taxaBuffer.append("\t\t\t\t\t<altitudeMode>relativeToGround</altitudeMode>\r");
taxaBuffer.append("\t\t\t\t\t<coordinates>"+longitude+","+latitude+","+"0</coordinates>\r");
taxaBuffer.append("\t\t\t\t</Point>\r");
taxaBuffer.append("\t\t\t</Placemark>\r");
if (treeToExport.getHeight(node) > 0) {
projectionsBuffer.append("\t\t\t<LineString>\r");
projectionsBuffer.append("\t\t\t\t<altitudeMode>relativeToGround</altitudeMode>\r");
projectionsBuffer.append("\t\t\t\t<coordinates>\r");
projectionsBuffer.append("\t\t\t\t\t"+longitude+","+latitude+","+altitude+"\r");
projectionsBuffer.append("\t\t\t\t\t"+longitude+","+latitude+",0\r");
projectionsBuffer.append("\t\t\t\t</coordinates>\r");
projectionsBuffer.append("\t\t\t</LineString>\r");
}
styleBuffer.append("\t<Style id=\"rectangleTreeBranch"+ nodeNumber +"_style\">\r");
styleBuffer.append("\t\t<LineStyle>\r");
if (usePosterior_RT) {
styleBuffer.append("\t\t\t<width>"+(branchWidthConstant+branchWidthMultiplier)+"</width>\r");
} else {
styleBuffer.append("\t\t\t<width>"+branchWidth_RT+"</width>\r");
}
if (useRates_RT || useLogRates_RT) {
double rate = 0;
if (useRates_RT) {
rate = (Double)node.getAttribute("rate");
} else if (useLogRates_RT) {
rate = Math.log((Double)node.getAttribute("rate"));
}
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_RT)+
getKMLColor(rate, rateMinMaxMedian, startBranchColor_RT,
endBranchColor_RT)+"</color>\r");
} else if (useHeights_RT) {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_RT)+
getKMLColor(treeToExport.getHeight(node), heightMinAndMax, startBranchColor_RT,
endBranchColor_RT)+"</color>\r");
} else {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_RT)+branchColor_RT+"</color>\r");
}
styleBuffer.append("\t\t</LineStyle>\r");
styleBuffer.append("\t</Style>\r");
styleBuffer.append("\t<Style id=\"triangleTreeBranch"+ nodeNumber +"_style\">\r");
styleBuffer.append("\t\t<LineStyle>\r");
if (usePosterior_TT) {
styleBuffer.append("\t\t\t<width>"+(branchWidthConstant+branchWidthMultiplier)+"</width>\r");
} else {
styleBuffer.append("\t\t\t<width>"+branchWidth_TT+"</width>\r");
}
if (useRates_TT || useLogRates_TT) {
double rate = 0;
if (useRates_TT) {
rate = (Double)node.getAttribute("rate");
} else if (useLogRates_TT) {
rate = Math.log((Double)node.getAttribute("rate"));
}
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_TT)+
getKMLColor(rate, rateMinMaxMedian, startBranchColor_TT,
endBranchColor_TT)+"</color>\r");
} else {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_TT)+branchColor_TT+"</color>\r");
}
styleBuffer.append("\t\t</LineStyle>\r");
styleBuffer.append("\t</Style>\r");
// line style for surface external branches, based on posterior
styleBuffer.append("\t<Style id=\"surfaceTreeBranch"+ nodeNumber +"_style\">\r");
styleBuffer.append("\t\t<LineStyle>\r");
if (usePosterior_ST) {
styleBuffer.append("\t\t\t<width>"+(branchWidthConstant+branchWidthMultiplier)+"</width>\r");
} else {
styleBuffer.append("\t\t\t<width>"+branchWidth_ST+"</width>\r");
}
if (useHeights_ST){
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_ST)+getKMLColor(treeToExport.getHeight(node),
heightMinAndMax, startBranchColor_ST, endBranchColor_ST)+"</color>\r");
} else {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_ST)+branchColor_ST+"</color>\r");
}
styleBuffer.append("\t\t</LineStyle>\r");
styleBuffer.append("\t</Style>\r");
if (tipsSampled) {
if (contoursAndNotSpades) {
appendContour(contourTipsBuffer, treeToExport, node, nodeNumber, plotHeight, mostRecentDate, traitName, latitudeName, longitudeName, false, iniVisi_contours, locationHPDpercentage, ancient);
appendContour(groundContourTipsBuffer, treeToExport, node, nodeNumber, plotHeight, mostRecentDate, traitName, latitudeName, longitudeName, true, iniVisi_groundContours, locationHPDpercentage, ancient);
} else {
appendSpade(spadeTipsBuffer, treeToExport, node, nodeNumber, plotHeight, mostRecentDate, latitudeName, longitudeName, false, iniVisi_spades, locationHPDpercentage, ancient);
appendSpade(groundSpadeTipsBuffer, treeToExport, node, nodeNumber, plotHeight, mostRecentDate, latitudeName, longitudeName, true, iniVisi_groundSpades, locationHPDpercentage, ancient);
appendDiamond(diamondTipsBuffer, treeToExport, node, nodeNumber, plotHeight, latitudeName, longitudeName, locationHPDpercentage, iniVisi_diamonds);
}
// spade/contour style for tips
//contour
if (contoursAndNotSpades) {
styleBuffer.append("\t<Style id=\"contour"+ nodeNumber +"_style\">\r");
styleBuffer.append("\t\t<LineStyle>\r");
styleBuffer.append("\t\t\t<width>1.5</width>\r");
if (useHeights_tipContours) {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_tipContours)+getKMLColor(treeToExport.getHeight(node),
heightMinAndMax, startTipContourColor, endTipContourColor)+"</color>\r");
} else {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_tipContours)+tipContourColor+"</color>\r");
}
styleBuffer.append("\t\t</LineStyle>\r");
styleBuffer.append("\t\t<PolyStyle>\r");
if (useHeights_tipContours) {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_tipContours)+getKMLColor(treeToExport.getHeight(node),
heightMinAndMax, startTipContourColor, endTipContourColor)+"</color>\r");
} else {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_tipContours)+tipContourColor+"</color>\r");
}
styleBuffer.append("\t\t</PolyStyle>\r");
styleBuffer.append("\t</Style>\r");
//spade
} else {
styleBuffer.append("\t<Style id=\"spade"+ nodeNumber +"_style\">\r");
styleBuffer.append("\t\t<LineStyle>\r");
styleBuffer.append("\t\t\t<width>1.5</width>\r");
if (useHeights_tipSpades) {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_tipSpades)+getKMLColor(treeToExport.getHeight(node),
heightMinAndMax, startTipSpadeColor, endTipSpadeColor)+"</color>\r");
} else {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_tipSpades)+tipSpadeColor+"</color>\r");
}
styleBuffer.append("\t\t</LineStyle>\r");
styleBuffer.append("\t\t<PolyStyle>\r");
if (useHeights_tipSpades) {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_tipSpades)+getKMLColor(treeToExport.getHeight(node),
heightMinAndMax, startTipSpadeColor, endTipSpadeColor)+"</color>\r");
} else {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_tipSpades)+tipSpadeColor+"</color>\r");
}
styleBuffer.append("\t\t</PolyStyle>\r");
styleBuffer.append("\t</Style>\r");
}
// ground spade/contour style for internal nodes
//contour
if (contoursAndNotSpades) {
styleBuffer.append("\t<Style id=\"groundContour"+ nodeNumber +"_style\">\r");
styleBuffer.append("\t\t<LineStyle>\r");
styleBuffer.append("\t\t\t<width>0.5</width>\r");
styleBuffer.append("\t\t</LineStyle>\r");
styleBuffer.append("\t\t<PolyStyle>\r");
if (useHeights_groundTipSpades) {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_groundTipContours)+getKMLColor(treeToExport.getHeight(node),
heightMinAndMax, startgroundTipContourColor, endgroundTipContourColor)+"</color>\r");
} else {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_groundTipContours)+groundTipContourColor+"</color>\r");
}
styleBuffer.append("\t\t\t<outline>0</outline>\r");
styleBuffer.append("\t\t</PolyStyle>\r");
styleBuffer.append("\t</Style>\r");
//spade
} else {
styleBuffer.append("\t<Style id=\"groundSpade"+ nodeNumber +"_style\">\r");
styleBuffer.append("\t\t<LineStyle>\r");
styleBuffer.append("\t\t\t<width>0.5</width>\r");
styleBuffer.append("\t\t</LineStyle>\r");
styleBuffer.append("\t\t<PolyStyle>\r");
if (useHeights_groundTipSpades) {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_groundTipSpades)+getKMLColor(treeToExport.getHeight(node),
heightMinAndMax, startgroundTipSpadeColor, endgroundTipSpadeColor)+"</color>\r");
} else {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_groundTipSpades)+groundTipSpadeColor+"</color>\r");
}
styleBuffer.append("\t\t\t<outline>0</outline>\r");
styleBuffer.append("\t\t</PolyStyle>\r");
styleBuffer.append("\t</Style>\r");
}
totalHPDarea += getNodeArea(node, traitName, latitudeName, longitudeName, locationHPDpercentage);
}
} else {
if (contoursAndNotSpades) {
appendContour(contourBuffer, treeToExport, node, nodeNumber, plotHeight, mostRecentDate, traitName, latitudeName, longitudeName, false, iniVisi_contours, locationHPDpercentage, ancient);
appendContour(groundContourBuffer, treeToExport, node, nodeNumber, plotHeight, mostRecentDate, traitName, latitudeName, longitudeName, true, iniVisi_groundContours, locationHPDpercentage, ancient);
} else {
appendSpade(spadeBuffer, treeToExport, node, nodeNumber, plotHeight, mostRecentDate, latitudeName, longitudeName, false, iniVisi_spades, locationHPDpercentage, ancient);
appendSpade(groundSpadeBuffer, treeToExport, node, nodeNumber, plotHeight, mostRecentDate, latitudeName, longitudeName, true, iniVisi_groundSpades, locationHPDpercentage, ancient);
appendDiamond(diamondBuffer, treeToExport, node, nodeNumber, plotHeight, latitudeName, longitudeName, locationHPDpercentage, iniVisi_diamonds);
}
Double posterior = (Double)node.getAttribute("posterior");
// line style for internal rectangleTree branches, based on posterior
styleBuffer.append("\t<Style id=\"rectangleTreeBranch"+ nodeNumber +"_style\">\r");
styleBuffer.append("\t\t<LineStyle>\r");
if (usePosterior_RT) {
styleBuffer.append("\t\t\t<width>"+(branchWidthConstant+posterior*branchWidthMultiplier)+"</width>\r");
} else {
styleBuffer.append("\t\t\t<width>"+branchWidth_RT+"</width>\r");
}
if (useRates_RT || useLogRates_RT) {
double rate = 0;
if (useRates_RT) {
rate = (Double)node.getAttribute("rate");
} else if (useLogRates_RT) {
rate = Math.log((Double)node.getAttribute("rate"));
}
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_RT)+getKMLColor(rate,rateMinMaxMedian,
startBranchColor_RT, endBranchColor_RT)+"</color>\r");
} else if (useHeights_RT) {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_RT)+
getKMLColor(treeToExport.getHeight(node), heightMinAndMax, startBranchColor_RT,
endBranchColor_RT)+"</color>\r");
} else {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_RT)+branchColor_RT+"</color>\r");
}
styleBuffer.append("\t\t</LineStyle>\r");
styleBuffer.append("\t</Style>\r");
// line style for internal nodes, based on posterior
styleBuffer.append("\t<Style id=\"triangleTreeBranch"+ nodeNumber +"_style\">\r");
styleBuffer.append("\t\t<LineStyle>\r");
if (usePosterior_TT) {
styleBuffer.append("\t\t\t<width>"+(branchWidthConstant+posterior*branchWidthMultiplier)+"</width>\r");
} else {
styleBuffer.append("\t\t\t<width>"+branchWidth_TT+"</width>\r");
}
if (useRates_TT || useLogRates_TT) {
double rate = 0;
if (useRates_TT) {
rate = (Double)node.getAttribute("rate");
} else if (useLogRates_TT) {
rate = Math.log((Double)node.getAttribute("rate"));
}
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_TT)+getKMLColor(rate,rateMinMaxMedian,
startBranchColor_TT, endBranchColor_TT)+"</color>\r");
} else {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_TT)+branchColor_TT+"</color>\r");
}
styleBuffer.append("\t\t</LineStyle>\r");
styleBuffer.append("\t</Style>\r");
// line style for surface internal branches, based on posterior
styleBuffer.append("\t<Style id=\"surfaceTreeBranch"+ nodeNumber +"_style\">\r");
styleBuffer.append("\t\t<LineStyle>\r");
if (usePosterior_ST) {
styleBuffer.append("\t\t\t<width>"+(branchWidthConstant+posterior*branchWidthMultiplier)+"</width>\r");
} else {
styleBuffer.append("\t\t\t<width>"+branchWidth_ST+"</width>\r");
}
if (useHeights_ST){
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_ST)+getKMLColor(treeToExport.getHeight(node),
heightMinAndMax, startBranchColor_ST, endBranchColor_ST)+"</color>\r");
} else {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_ST)+branchColor_ST+"</color>\r");
}
styleBuffer.append("\t\t</LineStyle>\r");
styleBuffer.append("\t</Style>\r");
// spade/contour style for internal nodes
//contour
if (contoursAndNotSpades) {
styleBuffer.append("\t<Style id=\"contour"+ nodeNumber +"_style\">\r");
styleBuffer.append("\t\t<LineStyle>\r");
styleBuffer.append("\t\t\t<width>1.5</width>\r");
if (useHeights_contours) {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_contours)+getKMLColor(treeToExport.getHeight(node),
heightMinAndMax, startContourColor, endContourColor)+"</color>\r");
} else {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_contours)+contourColor+"</color>\r");
}
styleBuffer.append("\t\t</LineStyle>\r");
styleBuffer.append("\t\t<PolyStyle>\r");
if (useHeights_contours) {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_contours)+getKMLColor(treeToExport.getHeight(node),
heightMinAndMax, startContourColor, endContourColor)+"</color>\r");
} else {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_contours)+contourColor+"</color>\r");
}
styleBuffer.append("\t\t</PolyStyle>\r");
styleBuffer.append("\t</Style>\r");
//spade
} else {
styleBuffer.append("\t<Style id=\"spade"+ nodeNumber +"_style\">\r");
styleBuffer.append("\t\t<LineStyle>\r");
styleBuffer.append("\t\t\t<width>1.5</width>\r");
if (useHeights_spades) {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_spades)+getKMLColor(treeToExport.getHeight(node),
heightMinAndMax, startSpadeColor, endSpadeColor)+"</color>\r");
} else {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_spades)+spadeColor+"</color>\r");
}
styleBuffer.append("\t\t</LineStyle>\r");
styleBuffer.append("\t\t<PolyStyle>\r");
if (useHeights_spades) {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_spades)+getKMLColor(treeToExport.getHeight(node),
heightMinAndMax, startSpadeColor, endSpadeColor)+"</color>\r");
} else {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_spades)+spadeColor+"</color>\r");
}
styleBuffer.append("\t\t</PolyStyle>\r");
styleBuffer.append("\t</Style>\r");
}
// ground spade/contour style for internal nodes
//contour
if (contoursAndNotSpades) {
styleBuffer.append("\t<Style id=\"groundContour"+ nodeNumber +"_style\">\r");
styleBuffer.append("\t\t<LineStyle>\r");
styleBuffer.append("\t\t\t<width>0.5</width>\r");
styleBuffer.append("\t\t</LineStyle>\r");
styleBuffer.append("\t\t<PolyStyle>\r");
if (useHeights_groundSpades) {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_groundContours)+getKMLColor(treeToExport.getHeight(node),
heightMinAndMax, startgroundContourColor, endgroundContourColor)+"</color>\r");
} else {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_groundContours)+groundContourColor+"</color>\r");
}
styleBuffer.append("\t\t\t<outline>0</outline>\r");
styleBuffer.append("\t\t</PolyStyle>\r");
styleBuffer.append("\t</Style>\r");
//spade
} else {
styleBuffer.append("\t<Style id=\"groundSpade"+ nodeNumber +"_style\">\r");
styleBuffer.append("\t\t<LineStyle>\r");
styleBuffer.append("\t\t\t<width>0.5</width>\r");
styleBuffer.append("\t\t</LineStyle>\r");
styleBuffer.append("\t\t<PolyStyle>\r");
if (useHeights_groundSpades) {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_groundSpades)+getKMLColor(treeToExport.getHeight(node),
heightMinAndMax, startgroundSpadeColor, endgroundSpadeColor)+"</color>\r");
} else {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_groundSpades)+groundSpadeColor+"</color>\r");
}
styleBuffer.append("\t\t\t<outline>0</outline>\r");
styleBuffer.append("\t\t</PolyStyle>\r");
styleBuffer.append("\t</Style>\r");
}
totalHPDarea += getNodeArea(node, traitName, latitudeName, longitudeName, locationHPDpercentage);
}
// write a ancestral branch to the root with length that is a precentage of the total height
} else {
rectangleTreeBuffer.append("\t\t<Placemark>\r");
triangleTreeBuffer.append("\t\t<Placemark>\r");
// by putting a Span on this root branch, this will be the oldest time. So, everything else will appear later in the time animation. (if not, the root spade would already be there)
double date = mostRecentDate - (treeToExport.getHeight(node) + (fraction*treeToExport.getHeight(node)));
String[] yearMonthDay = convertToYearMonthDay(date);
rectangleTreeBuffer.append("\t\t\t<TimeSpan>\r");
triangleTreeBuffer.append("\t\t\t<TimeSpan>\r");
if (ancient) {
rectangleTreeBuffer.append("\t\t\t\t<begin>"+Math.round(date)+"</begin>\r");
triangleTreeBuffer.append("\t\t\t\t<begin>"+Math.round(date)+"</begin>\r");
} else {
rectangleTreeBuffer.append("\t\t\t\t<begin>"+yearMonthDay[0]+"-"+yearMonthDay[1]+"-"+yearMonthDay[2]+"</begin>\r");
triangleTreeBuffer.append("\t\t\t\t<begin>"+yearMonthDay[0]+"-"+yearMonthDay[1]+"-"+yearMonthDay[2]+"</begin>\r");
}
rectangleTreeBuffer.append("\t\t\t</TimeSpan>\r");
triangleTreeBuffer.append("\t\t\t</TimeSpan>\r");
rectangleTreeBuffer.append("\t\t\t<visibility>"+visibility_RT+"</visibility>\r");
triangleTreeBuffer.append("\t\t\t<visibility>"+visibility_TT+"</visibility>\r");
rectangleTreeBuffer.append("\t\t\t<name>rectangleTreeBranch"+ nodeNumber +"</name>\r");
triangleTreeBuffer.append("\t\t\t<name>triangleTreeBranch"+ nodeNumber +"</name>\r");
rectangleTreeBuffer.append("\t\t\t<styleUrl>#rectangleTreeBranch"+ nodeNumber +"_style</styleUrl>\r");
triangleTreeBuffer.append("\t\t\t<styleUrl>#triangleTreeBranch"+ nodeNumber +"_style</styleUrl>\r");
rectangleTreeBuffer.append("\t\t\t<LineString>\r");
triangleTreeBuffer.append("\t\t\t<LineString>\r");
rectangleTreeBuffer.append("\t\t\t\t<altitudeMode>relativeToGround</altitudeMode>\r");
triangleTreeBuffer.append("\t\t\t\t<altitudeMode>relativeToGround</altitudeMode>\r");
rectangleTreeBuffer.append("\t\t\t\t<coordinates>\r");
triangleTreeBuffer.append("\t\t\t\t<coordinates>\r");
rectangleTreeBuffer.append("\t\t\t\t\t"+longitude+","+latitude+","+altitude+"\r");
triangleTreeBuffer.append("\t\t\t\t\t"+longitude+","+latitude+","+altitude+"\r");
rectangleTreeBuffer.append("\t\t\t\t\t"+longitude+","+latitude+","+(altitude+(fraction*altitude))+"\r");
triangleTreeBuffer.append("\t\t\t\t\t"+longitude+","+latitude+","+(altitude+(fraction*altitude))+"\r");
rectangleTreeBuffer.append("\t\t\t\t</coordinates>\r");
triangleTreeBuffer.append("\t\t\t\t</coordinates>\r");
rectangleTreeBuffer.append("\t\t\t</LineString>\r");
triangleTreeBuffer.append("\t\t\t</LineString>\r");
rectangleTreeBuffer.append("\t\t</Placemark>\r");
triangleTreeBuffer.append("\t\t</Placemark>\r");
if (contoursAndNotSpades) {
appendContour(contourBuffer, treeToExport, node, nodeNumber, plotHeight, mostRecentDate, traitName, latitudeName, longitudeName, false, iniVisi_contours, locationHPDpercentage, ancient);
appendContour(groundContourBuffer, treeToExport, node, nodeNumber, plotHeight, mostRecentDate, traitName, latitudeName, longitudeName, true, iniVisi_groundContours, locationHPDpercentage, ancient);
} else {
appendSpade(spadeBuffer, treeToExport, node, nodeNumber, plotHeight, mostRecentDate, latitudeName, longitudeName, false, iniVisi_spades, locationHPDpercentage, ancient);
appendSpade(groundSpadeBuffer, treeToExport, node, nodeNumber, plotHeight, mostRecentDate, latitudeName, longitudeName, true, iniVisi_groundSpades, locationHPDpercentage, ancient);
appendDiamond(diamondBuffer, treeToExport, node, nodeNumber, plotHeight, latitudeName, longitudeName, locationHPDpercentage, iniVisi_diamonds);
}
// style for the rectangle root branch
styleBuffer.append("\t<Style id=\"rectangleTreeBranch"+ nodeNumber +"_style\">\r");
styleBuffer.append("\t\t<LineStyle>\r");
if (usePosterior_RT) {
styleBuffer.append("\t\t\t<width>"+(branchWidthConstant+branchWidthMultiplier)+"</width>\r");
} else {
styleBuffer.append("\t\t\t<width>"+branchWidth_RT+"</width>\r");
}
if (useRates_RT){
// the root branch gets the color for the median rate
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_RT)+getKMLColor(rateMinMaxMedian[2],
rateMinMaxMedian, startBranchColor_RT, endBranchColor_RT)+"</color>\r");
} else {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_RT)+branchColor_RT+"</color>\r");
}
styleBuffer.append("\t\t</LineStyle>\r");
styleBuffer.append("\t</Style>\r");
// style for the triangle root branch
styleBuffer.append("\t<Style id=\"triangleTreeBranch"+ nodeNumber +"_style\">\r");
styleBuffer.append("\t\t<LineStyle>\r");
if (usePosterior_RT) {
styleBuffer.append("\t\t\t<width>"+(branchWidthConstant+branchWidthMultiplier)+"</width>\r");
} else {
styleBuffer.append("\t\t\t<width>"+branchWidth_TT+"</width>\r");
}
if (useRates_TT){
// the root branch gets the color for the median rate
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_TT)+getKMLColor(rateMinMaxMedian[2],
rateMinMaxMedian, startBranchColor_TT, endBranchColor_TT)+"</color>\r");
} else {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_TT)+branchColor_TT+"</color>\r");
}
styleBuffer.append("\t\t</LineStyle>\r");
styleBuffer.append("\t</Style>\r");
// style for the root spade/contour based on height
if (contoursAndNotSpades) {
styleBuffer.append("\t<Style id=\"contour"+ nodeNumber +"_style\">\r");
styleBuffer.append("\t\t<LineStyle>\r");
styleBuffer.append("\t\t\t<width>1.5</width>\r");
if (useHeights_contours) {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_contours)+getKMLColor(treeToExport.getHeight(node),
heightMinAndMax, startContourColor, endContourColor)+"</color>\r");
} else {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_contours)+contourColor+"</color>\r");
}
styleBuffer.append("\t\t</LineStyle>\r");
styleBuffer.append("\t\t<PolyStyle>\r");
if (useHeights_contours) {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_contours)+getKMLColor(treeToExport.getHeight(node),
heightMinAndMax, startContourColor, endContourColor)+"</color>\r");
} else {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_contours)+contourColor+"</color>\r");
}
styleBuffer.append("\t\t</PolyStyle>\r");
styleBuffer.append("\t</Style>\r");
} else {
styleBuffer.append("\t<Style id=\"spade"+ nodeNumber +"_style\">\r");
styleBuffer.append("\t\t<LineStyle>\r");
styleBuffer.append("\t\t\t<width>1.5</width>\r");
if (useHeights_spades) {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_spades)+getKMLColor(treeToExport.getHeight(node),
heightMinAndMax, startSpadeColor, endSpadeColor)+"</color>\r");
} else {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_spades)+spadeColor+"</color>\r");
}
styleBuffer.append("\t\t</LineStyle>\r");
styleBuffer.append("\t\t<PolyStyle>\r");
if (useHeights_spades) {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_spades)+getKMLColor(treeToExport.getHeight(node),
heightMinAndMax, startSpadeColor, endSpadeColor)+"</color>\r");
} else {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_spades)+spadeColor+"</color>\r");
}
styleBuffer.append("\t\t</PolyStyle>\r");
styleBuffer.append("\t</Style>\r");
}
// ground spade/contour style for root
if (contoursAndNotSpades) {
styleBuffer.append("\t<Style id=\"groundContour"+ nodeNumber +"_style\">\r");
styleBuffer.append("\t\t<LineStyle>\r");
styleBuffer.append("\t\t\t<width>0.5</width>\r");
styleBuffer.append("\t\t</LineStyle>\r");
styleBuffer.append("\t\t<PolyStyle>\r");
if (useHeights_groundContours) {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_groundContours)+getKMLColor(treeToExport.getHeight(node),
heightMinAndMax, startgroundContourColor, endgroundContourColor)+"</color>\r");
} else {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_groundContours)+groundContourColor+"</color>\r");
}
styleBuffer.append("\t\t\t<outline>0</outline>");
styleBuffer.append("\t\t</PolyStyle>\r");
styleBuffer.append("\t</Style>\r");
} else {
styleBuffer.append("\t<Style id=\"groundSpade"+ nodeNumber +"_style\">\r");
styleBuffer.append("\t\t<LineStyle>\r");
styleBuffer.append("\t\t\t<width>0.5</width>\r");
styleBuffer.append("\t\t</LineStyle>\r");
styleBuffer.append("\t\t<PolyStyle>\r");
if (useHeights_groundSpades) {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_groundSpades)+getKMLColor(treeToExport.getHeight(node),
heightMinAndMax, startgroundSpadeColor, endgroundSpadeColor)+"</color>\r");
} else {
styleBuffer.append("\t\t\t<color>"+getOpacity(opacity_groundSpades)+groundSpadeColor+"</color>\r");
}
styleBuffer.append("\t\t\t<outline>0</outline>");
styleBuffer.append("\t\t</PolyStyle>\r");
styleBuffer.append("\t</Style>\r");
}
double rootHPDArea = getNodeArea(node, traitName, latitudeName, longitudeName, locationHPDpercentage);
totalHPDarea += rootHPDArea;
System.out.println("root HPD area is "+rootHPDArea+" degrees");
}
}
System.out.println("total HPD area is "+totalHPDarea+" degrees");
}
public void writeTreeToKML(double time, double treeSliceBranchWidth, boolean showBranchAtMidPoint) {
heightMinAndMax = getHeightMinAndMax(treeToExport);
treeSliceBuffer.append("\t<Folder>\r");
treeSliceBuffer.append("\t\t\t<name>tree"+ time +"</name>\r");
contourSliceBuffer.append("\t<Folder>\r");
contourSliceBuffer.append("\t\t\t<name>contours"+ time +"</name>\r");
int nodeNumber = 0;
for (Node node : treeToExport.getNodes()) {
Double longitude = (Double)node.getAttribute(longitudeName);
Double latitude = (Double)node.getAttribute(latitudeName);
double nodeHeight = treeToExport.getHeight(node);
if (!treeToExport.isRoot(node)) {
Node parentNode = treeToExport.getParent(node);
Double parentLongitude = (Double)parentNode.getAttribute(longitudeName);
Double parentLatitude = (Double)parentNode.getAttribute(latitudeName);
double parentHeight = treeToExport.getHeight(parentNode);
if ((parentHeight > time) && (nodeHeight <= time)) {
//extrapolate lat/long
if (!showBranchAtMidPoint) {
latitude = parentLatitude + (latitude-parentLatitude)*((parentHeight-time)/(parentHeight-nodeHeight));
longitude = parentLongitude + (longitude-parentLongitude)*((parentHeight-time)/(parentHeight-nodeHeight));
}
}
if (((parentHeight > time) && !(showBranchAtMidPoint)) || (showBranchAtMidPoint && (time < ((nodeHeight+((parentHeight-nodeHeight)/2.0)))))) {
treeSliceBuffer.append("\t\t<Placemark>\r");
treeSliceBuffer.append("\t\t\t<name>treeSliceBranch"+ nodeNumber + "_" + time + "</name>\r");
treeSliceBuffer.append("\t\t\t<styleUrl>#treeSliceBranch"+ nodeNumber + "_" + time + "_style</styleUrl>\r");
treeSliceBuffer.append("\t\t\t<LineString>\r");
treeSliceBuffer.append("\t\t\t\t<altitudeMode>clampToGround</altitudeMode>\r");
treeSliceBuffer.append("\t\t\t\t<coordinates>\r");
treeSliceBuffer.append("\t\t\t\t\t"+longitude+","+latitude+",0\r");
treeSliceBuffer.append("\t\t\t\t\t"+parentLongitude+","+parentLatitude+",0\r");
treeSliceBuffer.append("\t\t\t\t</coordinates>\r");
treeSliceBuffer.append("\t\t\t</LineString>\r");
treeSliceBuffer.append("\t\t</Placemark>\r");
styleBuffer.append("\t<Style id=\"treeSliceBranch"+ nodeNumber + "_" + time + "_style\">\r");
styleBuffer.append("\t\t<LineStyle>\r");
styleBuffer.append("\t\t\t<width>"+treeSliceBranchWidth+"</width>\r");
styleBuffer.append("\t\t\t<color>"+"FF"+ ContinuousKML.getKMLColor((nodeHeight+((parentHeight-nodeHeight)/2.0)),
heightMinAndMax, startBranchColor_TS, endBranchColor_TS)+"</color>\r");
styleBuffer.append("\t\t</LineStyle>\r");
styleBuffer.append("\t</Style>\r");
appendContour(contourSliceBuffer, treeToExport, node, nodeNumber, plotHeight, mostRecentDate, traitName, latitudeName, longitudeName, true, iniVisi_groundContours, locationHPDpercentage, ancient);
}
}
nodeNumber ++;
}
treeSliceBuffer.append("\t</Folder>\r");
contourSliceBuffer.append("\t</Folder>\r");
}
public void compileBuffer(StringBuffer buffer, boolean makeTreeSlices) {
buffer.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r");
buffer.append("<kml xmlns=\"http://earth.google.com/kml/2.2\">\r");
buffer.append("<Document>\r");
buffer.append("\t<name>"+documentName+"</name>\r");
buffer.append(styleBuffer);
if (!makeTreeSlices) {
buffer.append("\t<Style id=\"diamondStyle\">\r");
buffer.append("\t\t<LineStyle>\r");
buffer.append("\t\t\t<width>0.5</width>\r");
buffer.append("\t\t</LineStyle>\r");
buffer.append("\t\t<PolyStyle>\r");
buffer.append("\t\t\t<color>7d00ffff</color>\r");
buffer.append("\t\t\t<fill>0</fill>\r");
buffer.append("\t\t</PolyStyle>\r");
buffer.append("\t</Style>\r");
buffer.append("\t<Folder>\r");
buffer.append("\t<name>triangle tree</name>\r");
buffer.append("\t<description>tree out of surface with node heights proportional to time</description>\r");
buffer.append(triangleTreeBuffer);
buffer.append("\t</Folder>\r");
buffer.append("\t<Folder>\r");
buffer.append("\t<name>rectangle tree</name>\r");
buffer.append("\t<description>tree out of surface with branch lengths (and node heights) proportional to time</description>\r");
buffer.append(rectangleTreeBuffer);
buffer.append("\t</Folder>\r");
buffer.append("\t<Folder>\r");
buffer.append("\t<name>surface tree</name>\r");
buffer.append("\t<description>tree on the surface interconnecting inferred and sampled locations</description>\r");
buffer.append(surfaceTreeBuffer);
buffer.append("\t</Folder>\r");
int visibility_projections;
if (iniVisi_projections) { visibility_projections = 1; } else { visibility_projections = 0; }
buffer.append("\t<Placemark>\r");
buffer.append("\t\t<name>projections</name>\r");
buffer.append("\t\t<description>projections from tips to surface</description>\r");
buffer.append("\t\t<visibility>"+visibility_projections+"</visibility>\r");
buffer.append("\t\t<MultiGeometry>\r");
buffer.append(projectionsBuffer);
buffer.append("\t\t</MultiGeometry>\r");
buffer.append("\t</Placemark>\r");
buffer.append("\t<Folder>\r");
buffer.append("\t\t<name>Taxon labels</name>\r");
buffer.append("\t\t<description>Taxon Labels</description>\r");
buffer.append(taxaBuffer);
buffer.append("\t</Folder>\r");
//writes a folder with credible intervals in the form of polygons
buffer.append("\t<Folder>\r");
buffer.append("\t<name>polygon HPDs</name>\r");
buffer.append("\t<description>various polygons to represent credible intervals</description>\r");
if (contoursAndNotSpades) {
buffer.append("\t\t<Folder>\r");
buffer.append("\t\t\t<name>contours HPDs</name>\r");
buffer.append("\t\t\t<description>contour HPDs for internal nodes</description>\r");
buffer.append(contourBuffer);
buffer.append("\t\t</Folder>\r");
buffer.append("\t\t<Folder>\r");
buffer.append("\t\t\t<name>surface contour HPDs</name>\r");
buffer.append("\t\t\t<description>contour HPDs for internal nodes projected on the surface</description>\r");
buffer.append(groundContourBuffer);
buffer.append("\t\t</Folder>\r");
if (tipsSampled){
buffer.append("\t\t<Folder>\r");
buffer.append("\t\t\t<name>contours tip HPDs</name>\r");
buffer.append("\t\t\t<description>contour HPDs for tips</description>\r");
buffer.append(contourTipsBuffer);
buffer.append("\t\t</Folder>\r");
buffer.append("\t\t<Folder>\r");
buffer.append("\t\t\t<name>surface contour tip HPD</name>\r");
buffer.append("\t\t\t<description>contour HPDs for tips projected on the surface</description>\r");
buffer.append(groundContourTipsBuffer);
buffer.append("\t\t</Folder>\r");
}
} else {
buffer.append("\t\t<Folder>\r");
buffer.append("\t\t\t<name>spade HPDs</name>\r");
buffer.append("\t\t\t<description>longitude and latitude HPDs for internal nodes</description>\r");
buffer.append(spadeBuffer);
buffer.append("\t\t</Folder>\r");
buffer.append("\t\t<Folder>\r");
buffer.append("\t\t\t<name>surface spade HPDs</name>\r");
buffer.append("\t\t\t<description>longitude and latitude HPDs for internal nodes projected on the surface</description>\r");
buffer.append(groundSpadeBuffer);
buffer.append("\t\t</Folder>\r");
buffer.append("\t\t<Folder>\r");
buffer.append("\t\t\t<name>diamond HPDs</name>\r");
buffer.append("\t\t\t<description>longitude and latitude and altitude HPDs for internal nodes</description>\r");
buffer.append(diamondBuffer);
buffer.append("\t\t</Folder>\r");
if (tipsSampled){
buffer.append("\t\t<Folder>\r");
buffer.append("\t\t\t<name>spade tip HPDs</name>\r");
buffer.append("\t\t\t<description>longitude and latitude HPDs for tips</description>\r");
buffer.append(spadeTipsBuffer);
buffer.append("\t\t</Folder>\r");
buffer.append("\t\t<Folder>\r");
buffer.append("\t\t\t<name>surface spade tip HPDs</name>\r");
buffer.append("\t\t\t<description>longitude and latitude HPDs for tips projected on the surface</description>\r");
buffer.append(groundSpadeTipsBuffer);
buffer.append("\t\t</Folder>\r");
buffer.append("\t\t<Folder>\r");
buffer.append("\t\t\t<name>diamond tip HPDs</name>\r");
buffer.append("\t\t\t<description>longitude and latitude and altitude HPDs for tips</description>\r");
buffer.append(diamondTipsBuffer);
buffer.append("\t\t</Folder>\r");
}
}
buffer.append("\t\t</Folder>\r");
} else {
buffer.append(treeSliceBuffer);
buffer.append("\t<Folder>\r");
buffer.append("\t\t<name>Taxon labels</name>\r");
buffer.append("\t\t<description>Taxon Labels</description>\r");
buffer.append(taxaBuffer);
buffer.append("\t</Folder>\r");
buffer.append(contourSliceBuffer);
}
buffer.append("</Document>\r");
buffer.append("</kml>");
}
private static void appendSpade(StringBuffer buffer, RootedTree tree, Node node, int nodeNumber, double plotHeight, double mostRecentDate, String latitudeName, String longitudeName, boolean groundSpade, boolean initialVisibility, String spadeHPD, boolean ancient) {
int visibility;
if (initialVisibility) {visibility = 1; } else { visibility = 0; }
double scaleFactor = plotHeight/tree.getHeight(tree.getRootNode());
Double longitude = (Double)node.getAttribute(longitudeName);
Double latitude = (Double)node.getAttribute(latitudeName);
double altitude = (tree.getHeight(node)*scaleFactor);
String altitudeMode;
if (groundSpade) {
altitude = 0;
altitudeMode = "clampToGround";
} else {
altitudeMode = "relativeToGround";
}
Object[] longitudeHPDs = (Object[])node.getAttribute(longitudeName+"_"+spadeHPD+"HPD");
Object[] latitudeHPDs = (Object[])node.getAttribute(latitudeName+"_"+spadeHPD+"HPD");
buffer.append("\t\t<Placemark>\r");
buffer.append("\t\t\t<visibility>"+visibility+"</visibility>\r");
buffer.append("\t\t<TimeSpan>\r");
double date = mostRecentDate - tree.getHeight(node);
String[] yearMonthDay = convertToYearMonthDay(date);
if (ancient) {
buffer.append("\t\t\t<begin>"+Math.round(date)+"</begin>\r");
} else {
buffer.append("\t\t\t<begin>"+yearMonthDay[0]+"-"+yearMonthDay[1]+"-"+yearMonthDay[2]+"</begin>\r");
}
buffer.append("\t\t</TimeSpan>\r");
if (groundSpade) {
buffer.append("\t\t<styleUrl>#groundSpade"+nodeNumber+"_style</styleUrl>\r");
} else {
buffer.append("\t\t<styleUrl>#spade"+nodeNumber+"_style</styleUrl>\r");
}
buffer.append("\t\t<Polygon>\r");
buffer.append("\t\t\t<altitudeMode>"+altitudeMode+"</altitudeMode>\r");
if (groundSpade) {
buffer.append("\t\t\t<tessellate>1</tessellate>\r");
}
buffer.append("\t\t\t<outerBoundaryIs>\r");
buffer.append("\t\t\t\t<LinearRing>\r");
buffer.append("\t\t\t\t\t<coordinates>\r");
buffer.append("\t\t\t\t\t"+longitude+","+latitudeHPDs[1]+","+altitude+"\r");
buffer.append("\t\t\t\t\t"+longitudeHPDs[1]+","+latitude+","+altitude+"\r");
buffer.append("\t\t\t\t\t"+longitude+","+latitudeHPDs[0]+","+altitude+"\r");
buffer.append("\t\t\t\t\t"+longitudeHPDs[0]+","+latitude+","+altitude+"\r");
buffer.append("\t\t\t\t\t"+longitude+","+latitudeHPDs[1]+","+altitude+"\r");
buffer.append("\t\t\t\t\t</coordinates>\r");
buffer.append("\t\t\t\t</LinearRing>\r");
buffer.append("\t\t\t</outerBoundaryIs>\r");
buffer.append("\t\t</Polygon>\r");
buffer.append("\t\t</Placemark>\r");
}
private static void appendContour(StringBuffer buffer, RootedTree tree, Node node, int nodeNumber, double plotHeight, double mostRecentDate, String latLongName, String latitudeName, String longitudeName, boolean groundContour, boolean initialVisibility, String contourHPD, boolean ancient) {
int visibility;
if (initialVisibility) {visibility = 1; } else { visibility = 0; }
double scaleFactor = plotHeight/tree.getHeight(tree.getRootNode());
double altitude = (tree.getHeight(node)*scaleFactor);
String altitudeMode;
if (groundContour) {
altitude = 0;
altitudeMode = "clampToGround";
} else {
altitudeMode = "relativeToGround";
}
Object testAttribute = node.getAttribute(latLongName+"_"+contourHPD+"HPD_modality");
if (testAttribute != null) {
int modality = ((Integer)node.getAttribute(latLongName+"_"+contourHPD+"HPD_modality")).intValue();
for (int x = 0; x < modality; x++) {
Object[] longitudeHPDs = (Object[])node.getAttribute(longitudeName+"_"+contourHPD+"HPD_"+(x + 1));
Object[] latitudeHPDs = (Object[])node.getAttribute(latitudeName+"_"+contourHPD+"HPD_"+(x + 1));
buffer.append("\t\t<Placemark>\r");
buffer.append("\t\t\t<visibility>"+visibility+"</visibility>\r");
buffer.append("\t\t<TimeSpan>\r");
double date = mostRecentDate - tree.getHeight(node);
String[] yearMonthDay = convertToYearMonthDay(date);
if (ancient) {
buffer.append("\t\t\t<begin>"+Math.round(date)+"</begin>\r");
} else {
buffer.append("\t\t\t<begin>"+yearMonthDay[0]+"-"+yearMonthDay[1]+"-"+yearMonthDay[2]+"</begin>\r");
}
buffer.append("\t\t</TimeSpan>\r");
if (groundContour) {
buffer.append("\t\t<styleUrl>#groundContour"+nodeNumber+"_style</styleUrl>\r");
} else {
buffer.append("\t\t<styleUrl>#contour"+nodeNumber+"_style</styleUrl>\r");
}
buffer.append("\t\t<Polygon>\r");
buffer.append("\t\t\t<altitudeMode>"+altitudeMode+"</altitudeMode>\r");
if (groundContour) {
buffer.append("\t\t\t<tessellate>1</tessellate>\r");
}
buffer.append("\t\t\t<outerBoundaryIs>\r");
buffer.append("\t\t\t\t<LinearRing>\r");
buffer.append("\t\t\t\t\t<coordinates>\r");
for (int y = 0; y < longitudeHPDs.length; y++) {
buffer.append("\t\t\t\t\t"+longitudeHPDs[y]+","+latitudeHPDs[y]+","+altitude+"\r");
}
buffer.append("\t\t\t\t\t</coordinates>\r");
buffer.append("\t\t\t\t</LinearRing>\r");
buffer.append("\t\t\t</outerBoundaryIs>\r");
buffer.append("\t\t</Polygon>\r");
buffer.append("\t\t</Placemark>\r");
}
} else {
System.err.print("found a node without location HPD contour!!\n");
}
}
private static void appendDiamond(StringBuffer buffer, RootedTree tree, Node node, int nodeNumber, double plotHeight, String latitudeName, String longitudeName, String diamondHPD, boolean initialVisibility) {
int visibility;
if (initialVisibility) { visibility = 1; } else { visibility = 0; }
double scaleFactor = plotHeight/tree.getHeight(tree.getRootNode());
Double longitude = (Double)node.getAttribute(longitudeName);
Double latitude = (Double)node.getAttribute(latitudeName);
double altitude = (tree.getHeight(node)*scaleFactor);
Object[] longitudeHPDs = (Object[])node.getAttribute(longitudeName+"_"+diamondHPD+"HPD");
Object[] latitudeHPDs = (Object[])node.getAttribute(latitudeName+"_"+diamondHPD+"HPD");
Object[] heightHPDs = (Object[])node.getAttribute("height_95%_HPD");
double longitudeHPDlower = (Double)longitudeHPDs[0];
double longitudeHPDupper = (Double)longitudeHPDs[1];
double latitudeHPDlower = (Double)latitudeHPDs[0];
double latitudeHPDupper = (Double)latitudeHPDs[1];
double heightHPDlower = (Double)heightHPDs[0];
double altitudeHPDlower = heightHPDlower*scaleFactor;
double heightHPDupper = (Double)heightHPDs[1];
double altitudeHPDupper = heightHPDupper* scaleFactor;
buffer.append("\t\t<Folder>\r");
buffer.append("\t\t\t<name>node"+nodeNumber+"_diamond</name>\r");
buffer.append("\t\t\t<Placemark>\r");
buffer.append("\t\t\t<visibility>"+visibility+"</visibility>\r");
buffer.append("\t\t\t<styleUrl>#diamondStyle</styleUrl>\r");
buffer.append("\t\t\t<Polygon>\r");
buffer.append("\t\t\t\t<altitudeMode>relativeToGround</altitudeMode>\r");
buffer.append("\t\t\t\t<outerBoundaryIs>\r");
buffer.append("\t\t\t\t\t<LinearRing>\r");
buffer.append("\t\t\t\t\t\t<coordinates>\r");
buffer.append("\t\t\t\t\t\t"+longitude+","+latitude+","+altitudeHPDlower+"\r");
buffer.append("\t\t\t\t\t\t"+longitudeHPDlower+","+latitudeHPDupper+","+altitude+"\r");
buffer.append("\t\t\t\t\t\t"+longitudeHPDlower+","+latitudeHPDlower+","+altitude+"\r");
buffer.append("\t\t\t\t\t\t"+longitude+","+latitude+","+altitudeHPDlower+"\r");
buffer.append("\t\t\t\t\t\t</coordinates>\r");
buffer.append("\t\t\t\t\t</LinearRing>\r");
buffer.append("\t\t\t\t</outerBoundaryIs>\r");
buffer.append("\t\t\t</Polygon>\r");
buffer.append("\t\t\t</Placemark>\r");
buffer.append("\t\t\t<Placemark>\r");
buffer.append("\t\t\t<visibility>0</visibility>\r");
buffer.append("\t\t\t<styleUrl>#diamondStyle</styleUrl>\r");
buffer.append("\t\t\t<Polygon>\r");
buffer.append("\t\t\t\t<altitudeMode>relativeToGround</altitudeMode>\r");
buffer.append("\t\t\t\t<outerBoundaryIs>\r");
buffer.append("\t\t\t\t\t<LinearRing>\r");
buffer.append("\t\t\t\t\t\t<coordinates>\r");
buffer.append("\t\t\t\t\t\t"+longitudeHPDlower+","+latitudeHPDupper+","+altitude+"\r");
buffer.append("\t\t\t\t\t\t"+longitudeHPDlower+","+latitudeHPDlower+","+altitude+"\r");
buffer.append("\t\t\t\t\t\t"+longitude+","+latitude+","+altitudeHPDupper+"\r");
buffer.append("\t\t\t\t\t\t"+longitudeHPDlower+","+latitudeHPDupper+","+altitude+"\r");
buffer.append("\t\t\t\t\t\t</coordinates>\r");
buffer.append("\t\t\t\t\t</LinearRing>\r");
buffer.append("\t\t\t\t</outerBoundaryIs>\r");
buffer.append("\t\t\t</Polygon>\r");
buffer.append("\t\t\t</Placemark>\r");
buffer.append("\t\t\t<Placemark>\r");
buffer.append("\t\t\t<visibility>0</visibility>\r");
buffer.append("\t\t\t<styleUrl>#diamondStyle</styleUrl>\r");
buffer.append("\t\t\t<Polygon>\r");
buffer.append("\t\t\t\t<altitudeMode>relativeToGround</altitudeMode>\r");
buffer.append("\t\t\t\t<outerBoundaryIs>\r");
buffer.append("\t\t\t\t\t<LinearRing>\r");
buffer.append("\t\t\t\t\t\t<coordinates>\r");
buffer.append("\t\t\t\t\t\t"+longitudeHPDlower+","+latitudeHPDupper+","+altitude+"\r");
buffer.append("\t\t\t\t\t\t"+longitude+","+latitude+","+altitudeHPDupper+"\r");
buffer.append("\t\t\t\t\t\t"+longitudeHPDupper+","+latitudeHPDupper+","+altitude+"\r");
buffer.append("\t\t\t\t\t\t"+longitudeHPDlower+","+latitudeHPDupper+","+altitude+"\r");
buffer.append("\t\t\t\t\t\t</coordinates>\r");
buffer.append("\t\t\t\t\t</LinearRing>\r");
buffer.append("\t\t\t\t</outerBoundaryIs>\r");
buffer.append("\t\t\t</Polygon>\r");
buffer.append("\t\t\t</Placemark>\r");
buffer.append("\t\t\t<Placemark>\r");
buffer.append("\t\t\t<visibility>0</visibility>\r");
buffer.append("\t\t\t<styleUrl>#diamondStyle</styleUrl>\r");
buffer.append("\t\t\t<Polygon>\r");
buffer.append("\t\t\t\t<altitudeMode>relativeToGround</altitudeMode>\r");
buffer.append("\t\t\t\t<outerBoundaryIs>\r");
buffer.append("\t\t\t\t\t<LinearRing>\r");
buffer.append("\t\t\t\t\t\t<coordinates>\r");
buffer.append("\t\t\t\t\t\t"+longitudeHPDlower+","+latitudeHPDupper+","+altitude+"\r");
buffer.append("\t\t\t\t\t\t"+longitudeHPDupper+","+latitudeHPDupper+","+altitude+"\r");
buffer.append("\t\t\t\t\t\t"+longitude+","+latitude+","+altitudeHPDlower+"\r");
buffer.append("\t\t\t\t\t\t"+longitudeHPDlower+","+latitudeHPDupper+","+altitude+"\r");
buffer.append("\t\t\t\t\t\t</coordinates>\r");
buffer.append("\t\t\t\t\t</LinearRing>\r");
buffer.append("\t\t\t\t</outerBoundaryIs>\r");
buffer.append("\t\t\t</Polygon>\r");
buffer.append("\t\t\t</Placemark>\r");
buffer.append("\t\t\t<Placemark>\r");
buffer.append("\t\t\t<visibility>0</visibility>\r");
buffer.append("\t\t\t<styleUrl>#diamondStyle</styleUrl>\r");
buffer.append("\t\t\t<Polygon>\r");
buffer.append("\t\t\t\t<altitudeMode>relativeToGround</altitudeMode>\r");
buffer.append("\t\t\t\t<outerBoundaryIs>\r");
buffer.append("\t\t\t\t\t<LinearRing>\r");
buffer.append("\t\t\t\t\t\t<coordinates>\r");
buffer.append("\t\t\t\t\t\t"+longitudeHPDupper+","+latitudeHPDupper+","+altitude+"\r");
buffer.append("\t\t\t\t\t\t"+longitudeHPDupper+","+latitudeHPDlower+","+altitude+"\r");
buffer.append("\t\t\t\t\t\t"+longitude+","+latitude+","+altitudeHPDupper+"\r");
buffer.append("\t\t\t\t\t\t"+longitudeHPDupper+","+latitudeHPDupper+","+altitude+"\r");
buffer.append("\t\t\t\t\t\t</coordinates>\r");
buffer.append("\t\t\t\t\t</LinearRing>\r");
buffer.append("\t\t\t\t</outerBoundaryIs>\r");
buffer.append("\t\t\t</Polygon>\r");
buffer.append("\t\t\t</Placemark>\r");
buffer.append("\t\t\t<Placemark>\r");
buffer.append("\t\t\t<visibility>0</visibility>\r");
buffer.append("\t\t\t<styleUrl>#diamondStyle</styleUrl>\r");
buffer.append("\t\t\t<Polygon>\r");
buffer.append("\t\t\t\t<altitudeMode>relativeToGround</altitudeMode>\r");
buffer.append("\t\t\t\t<outerBoundaryIs>\r");
buffer.append("\t\t\t\t\t<LinearRing>\r");
buffer.append("\t\t\t\t\t\t<coordinates>\r");
buffer.append("\t\t\t\t\t\t"+longitudeHPDupper+","+latitudeHPDupper+","+altitude+"\r");
buffer.append("\t\t\t\t\t\t"+longitudeHPDupper+","+latitudeHPDlower+","+altitude+"\r");
buffer.append("\t\t\t\t\t\t"+longitude+","+latitude+","+altitudeHPDlower+"\r");
buffer.append("\t\t\t\t\t\t"+longitudeHPDupper+","+latitudeHPDupper+","+altitude+"\r");
buffer.append("\t\t\t\t\t\t</coordinates>\r");
buffer.append("\t\t\t\t\t</LinearRing>\r");
buffer.append("\t\t\t\t</outerBoundaryIs>\r");
buffer.append("\t\t\t</Polygon>\r");
buffer.append("\t\t\t</Placemark>\r");
buffer.append("\t\t\t<Placemark>\r");
buffer.append("\t\t\t<visibility>0</visibility>\r");
buffer.append("\t\t\t<styleUrl>#diamondStyle</styleUrl>\r");
buffer.append("\t\t\t<Polygon>\r");
buffer.append("\t\t\t\t<altitudeMode>relativeToGround</altitudeMode>\r");
buffer.append("\t\t\t\t<outerBoundaryIs>\r");
buffer.append("\t\t\t\t\t<LinearRing>\r");
buffer.append("\t\t\t\t\t\t<coordinates>\r");
buffer.append("\t\t\t\t\t\t"+longitudeHPDlower+","+latitudeHPDlower+","+altitude+"\r");
buffer.append("\t\t\t\t\t\t"+longitudeHPDupper+","+latitudeHPDlower+","+altitude+"\r");
buffer.append("\t\t\t\t\t\t"+longitude+","+latitude+","+altitudeHPDupper+"\r");
buffer.append("\t\t\t\t\t\t"+longitudeHPDlower+","+latitudeHPDlower+","+altitude+"\r");
buffer.append("\t\t\t\t\t\t</coordinates>\r");
buffer.append("\t\t\t\t\t</LinearRing>\r");
buffer.append("\t\t\t\t</outerBoundaryIs>\r");
buffer.append("\t\t\t</Polygon>\r");
buffer.append("\t\t\t</Placemark>\r");
buffer.append("\t\t\t<Placemark>\r");
buffer.append("\t\t\t<visibility>0</visibility>\r");
buffer.append("\t\t\t<styleUrl>#diamondStyle</styleUrl>\r");
buffer.append("\t\t\t<Polygon>\r");
buffer.append("\t\t\t\t<altitudeMode>relativeToGround</altitudeMode>\r");
buffer.append("\t\t\t\t<outerBoundaryIs>\r");
buffer.append("\t\t\t\t\t<LinearRing>\r");
buffer.append("\t\t\t\t\t\t<coordinates>\r");
buffer.append("\t\t\t\t\t"+longitudeHPDlower+","+latitudeHPDlower+","+altitude+"\r");
buffer.append("\t\t\t\t\t"+longitudeHPDupper+","+latitudeHPDlower+","+altitude+"\r");
buffer.append("\t\t\t\t\t"+longitude+","+latitude+","+altitudeHPDlower+"\r");
buffer.append("\t\t\t\t\t"+longitudeHPDlower+","+latitudeHPDlower+","+altitude+"\r");
buffer.append("\t\t\t\t\t\t</coordinates>\r");
buffer.append("\t\t\t\t\t</LinearRing>\r");
buffer.append("\t\t\t\t</outerBoundaryIs>\r");
buffer.append("\t\t\t</Polygon>\r");
buffer.append("\t\t\t</Placemark>\r");
buffer.append("\t\t</Folder>\r");
}
// get the coordinates of the root (to locate a placemark and its icon)
private String getRootPointLocation(RootedTree tree, double plotHeight, String latitudeName, String longitudeName) {
Node root = tree.getRootNode();
return root.getAttribute(longitudeName)+","+root.getAttribute(latitudeName)+","+plotHeight;
}
private double[] getRateMinMaxMedian(RootedTree tree, boolean log) {
double[] minMaxMedian = new double[3];
double[] rates = new double[(tree.getNodes().size() - 1)];
int counter = 0;
int i = 0;
for (Node node : tree.getNodes()) {
if (!tree.isRoot(node)) {
if (log) {
rates[counter] = Math.log((Double)node.getAttribute("rate"));
} else {
rates[counter] = (Double)node.getAttribute("rate");
}
counter ++;
}
i++;
}
double median = DiscreteStatistics.quantile(0.5, rates);
double max = 0.0;
double min = Double.MAX_VALUE;
for (int j = 0; j < rates.length; j++) {
if (rates[j] > max) {
max = rates[j];
}
if (rates[j] < min) {
min = rates[j];
}
}
minMaxMedian[0] = min;
minMaxMedian[1] = max;
minMaxMedian[2] = median;
return minMaxMedian;
}
private double[] getHeightMinAndMax(RootedTree tree) {
double[] minAndMax = new double[2];
double[] heights = new double[tree.getNodes().size()];
int i = 0;
for (Node node : tree.getNodes()) {
heights[i] = tree.getHeight(node);
i++;
}
double max = 0.0;
double min = Double.MAX_VALUE;
for (int j = 0; j < heights.length; j++) {
if (heights[j] > max) {
max = heights[j];
}
if (heights[j] < min) {
min = heights[j];
}
}
minAndMax[0] = min;
minAndMax[1] = max;
return minAndMax;
}
private static String[] convertToYearMonthDay(double fractionalDate) {
String[] yearMonthDay = new String[3];
int year = (int) fractionalDate;
String yearString;
if (year < 10) {
yearString = "000"+year;
} else if (year < 100) {
yearString = "00"+year;
} else if (year < 1000) {
yearString = "0"+year;
} else {
yearString = ""+year;
}
yearMonthDay[0] = yearString;
double fractionalMonth = fractionalDate - year;
int month = (int) (12.0 * fractionalMonth);
String monthString;
if (month < 10) {
monthString = "0"+month;
} else {
monthString = ""+month;
}
yearMonthDay[1] = monthString;
int day = (int) Math.round(30*(12*fractionalMonth - month));
String dayString;
if (day < 10) {
dayString = "0"+day;
} else {
dayString = ""+day;
}
yearMonthDay[2] = dayString;
return yearMonthDay;
}
public static String getKMLColor(double value, double[] minMaxMedian, String startColor, String endColor) {
startColor = startColor.toLowerCase();
String startBlue = startColor.substring(0,2);
String startGreen = startColor.substring(2,4);
String startRed = startColor.substring(4,6);
endColor = endColor.toLowerCase();
String endBlue = endColor.substring(0,2);
String endGreen = endColor.substring(2,4);
String endRed = endColor.substring(4,6);
double proportion = (value - minMaxMedian[0])/(minMaxMedian[1] - minMaxMedian[0]);
// generate an array with hexadecimal code for each RGB entry number
String[] colorTable = new String[256];
int colorTableCounter = 0;
for (int a = 0; a < 10; a++) {
for (int b = 0; b < 10; b++) {
colorTable[colorTableCounter] = a + "" + b;
colorTableCounter ++;
}
for(int c = (int)('a'); c<6+(int)('a'); c++) {
colorTable[colorTableCounter] = a + "" + (char)c;
colorTableCounter ++;
}
}
for(int d = (int)('a'); d<6+(int)('a'); d++) {
for (int e = 0; e < 10; e++) {
colorTable[colorTableCounter] = (char) d + "" + e;
colorTableCounter ++;
}
for(int f = (int)('a'); f<6+(int)('a'); f++) {
colorTable[colorTableCounter] = (char) d + "" + (char) f;
colorTableCounter ++;
}
}
int startBlueInt = 0;
int startGreenInt = 0;
int startRedInt = 0;
int endBlueInt = 0;
int endGreenInt = 0;
int endRedInt = 0;
for (int i = 0; i < colorTable.length; i ++) {
if (colorTable[i].equals(startBlue)) {startBlueInt = i; }
if (colorTable[i].equals(startGreen)) {startGreenInt = i; }
if (colorTable[i].equals(startRed)) {startRedInt = i; }
if (colorTable[i].equals(endBlue)) {endBlueInt = i; }
if (colorTable[i].equals(endGreen)) {endGreenInt = i; }
if (colorTable[i].equals(endRed)) {endRedInt = i; }
}
int blueInt = startBlueInt + (int) Math.round((endBlueInt-startBlueInt)*proportion);
int greenInt = startGreenInt + (int) Math.round((endGreenInt-startGreenInt)*proportion);
int redInt = startRedInt + (int) Math.round((endRedInt-startRedInt)*proportion);
String blue = null;
String green = null;
String red = null;
for (int j = 0; j < colorTable.length; j ++) {
if (j == blueInt) {blue = colorTable[j]; }
if (j == greenInt) {green = colorTable[j]; }
if (j == redInt) {red = colorTable[j]; }
}
String color = blue+green+red;
return color;
}
private static String getOpacity(double opacity) {
String[] opacityTable = new String[256];
int colorTableCounter = 0;
for (int a = 0; a < 10; a++) {
for (int b = 0; b < 10; b++) {
opacityTable[colorTableCounter] = a + "" + b;
colorTableCounter ++;
}
for(int c = (int)('a'); c<6+(int)('a'); c++) {
opacityTable[colorTableCounter] = a + "" + (char)c;
colorTableCounter ++;
}
}
for(int d = (int)('a'); d<6+(int)('a'); d++) {
for (int e = 0; e < 10; e++) {
opacityTable[colorTableCounter] = (char) d + "" + e;
colorTableCounter ++;
}
for(int f = (int)('a'); f<6+(int)('a'); f++) {
opacityTable[colorTableCounter] = (char) d + "" + (char) f;
colorTableCounter ++;
}
}
int opacityInt = (int) Math.round(opacityTable.length*opacity);
String opacityString = "FF";
for (int j = 0; j < opacityTable.length; j ++) {
if (j == opacityInt) {opacityString = opacityTable[j]; }
}
return opacityString;
}
private static double getNodeArea(Node node, String latLongName, String latitudeName, String longitudeName, String contourHPD) {
double area = 0;
Object testAttribute = node.getAttribute(latLongName+"_"+contourHPD+"HPD_modality");
if (testAttribute != null) {
int modality = ((Integer)node.getAttribute(latLongName+"_"+contourHPD+"HPD_modality")).intValue();
for (int x = 0; x < modality; x++) {
Object[] longitudeHPDs = (Object[])node.getAttribute(longitudeName+"_"+contourHPD+"HPD_"+(x + 1));
Object[] latitudeHPDs = (Object[])node.getAttribute(latitudeName+"_"+contourHPD+"HPD_"+(x + 1));
//we can implement Strang's formula like this because the polygon is closed
for (int y = 0; y < longitudeHPDs.length - 1; y++) {
area += ( Double.valueOf((longitudeHPDs[y]).toString()).doubleValue() *
Double.valueOf((latitudeHPDs[y+1]).toString()).doubleValue() ) -
( Double.valueOf((longitudeHPDs[y+1]).toString()).doubleValue() *
Double.valueOf((latitudeHPDs[y]).toString()).doubleValue() );
}
}
}
return (area/2);
}
}