package dr.app.tools; import dr.app.beast.BeastVersion; import dr.app.util.Arguments; import dr.evolution.io.Importer; import dr.evolution.io.NewickImporter; import dr.evolution.io.NexusImporter; import dr.evolution.io.TreeImporter; import dr.evolution.tree.NodeRef; import dr.evolution.tree.Tree; import dr.geo.KMLCoordinates; import dr.geo.KernelDensityEstimator2D; import dr.geo.Polygon2D; import dr.geo.contouring.*; import dr.geo.math.SphericalPolarCoordinates; import dr.inference.trace.TraceDistribution; import dr.inference.trace.TraceFactory; import dr.math.distributions.MultivariateNormalDistribution; import dr.util.HeapSort; import dr.util.Version; import org.jdom.Element; import org.jdom.output.Format; import org.jdom.output.XMLOutputter; import java.awt.geom.Point2D; import java.io.*; import java.text.SimpleDateFormat; import java.util.*; /** * @author Marc A. Suchard * @author Philippe Lemey * <p/> * example for location slices through time: * -burnin500 -trait location -sliceFileHeights sliceTimes -summary true -mrsd 2007.63 -format KML -progress true -impute true -noise true -HPD 95 WNV_relaxed_gamma_geo.trees output.kml * <p/> * example for Markov reward o backbone summary (note we cannot imput here): * -burnin50 -trait AC1R,AC2R,AC4R,AC8R,AC10R,AC11R,AC15R,AC19R,AC22R,AC23R,AC26R,AC30R,AC31R,AC32R,AC33R,AC34R,AC35R,AC37R,AC46R -sliceTimes2002,2002.25,2002.5,2002.75,2003,2003.25,2003.5,2003.75,2004,2004.25,2004.5,2004.75,2005,2005.25,2005.5,2005.75,2006 -summary true -mrsd 2007 -branchNorm true -format TAB -progress true -backbonetaxa december2006taxa.txt -branchset backbone airCommunities_1.trees TSoutput.txt * example of 1D antigenic summary * -burnin0 -trait antigenic -sliceFileTimes timeInterval -summary true -mrsd 2007 -format TAB -progress true -impute true -noise false -HPD 95 H3N2_antigenic_sub.trees antigenic.txt */ public class TimeSlicer { public static final String sep = "\t"; public static final String PRECISION_STRING = "precision"; public static final String RATE_STRING = "rate"; // TODO the rate used is supposed to be the relaxed diffusion rate, but relaxed clock models will also result in a "rate attribute", we somehow need to distinguis between them! public static final String SLICE_ELEMENT = "slice"; public static final String REGIONS_ELEMENT = "hpdRegion"; public static final String NODE_ELEMENT = "node"; public static final String ROOT_ELEMENT = "root"; public static final String TRAIT = "trait"; public static final String NAME = "name"; public static final String DENSITY_VALUE = "density"; public static final String SLICE_VALUE = "time"; public static final String STYLE = "Style"; public static final String ID = "id"; public static final String WIDTH = "0.5"; public static final String startHPDColor = "FFFFFF"; //blue=B36600 public static final String endHPDColor = "00F1D6"; //red=0000FF public static final String opacity = "6f"; public static final String BURNIN = "burnin"; public static final String SKIP = "skip"; public static final String SLICE_TIMES = "sliceTimes"; public static final String SLICE_HEIGHTS = "sliceHeights"; public static final String SLICE_COUNT = "sliceCount"; public static final String START_TIME = "startTime"; public static final String SLICE_FILE_HEIGHTS = "sliceFileHeights"; public static final String SLICE_FILE_TIMES = "sliceFileTimes"; public static final String SLICE_MODE = "sliceMode"; public static final String ROOT = "root"; public static final String TIPS = "tips"; public static final String CONTOURS = "contours"; public static final String POINTS = "points"; public static final String MRSD = "mrsd"; public static final String HELP = "help"; public static final String NOISE = "noise"; public static final String IMPUTE = "impute"; public static final String SUMMARY = "summary"; public static final String FORMAT = "format"; public static final String CONTOUR_MODE = "contourMode"; public static final String NORMALIZATION = "normalization"; public static final String HPD = "hpd"; public static final String SDR = "sdr"; public static final String PROGRESS = "progress"; public static final double treeLengthPercentage = 0.00; public static final String BRANCH_NORMALIZE = "branchnorm"; public static final String BRANCHSET = "branchset"; public static final String BACKBONETAXA = "backbonetaxa"; public static final String ICON = "http://maps.google.com/mapfiles/kml/pal4/icon49.png"; public static final int GRIDSIZE = 200; public static final double[] BANDWIDTHS = new double[]{1.0,1.0}; public static final boolean BANDWIDTHLIMIT = true; public static final String[] falseTrue = {"false", "true"}; private final static Calendar calendar = GregorianCalendar.getInstance(); private final static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); public TimeSlicer(String treeFileName, int burnin, int skipEvery, String[] traits, double[] sliceHeights, boolean impute, boolean trueNoise, double mrsd, ContourMode contourMode, SliceMode sliceMode, final boolean summarizeRoot, final boolean summarizeTips, Normalization normalize, boolean getSRD, String progress, boolean branchNormalization, BranchSet branchset, Set backboneTaxa) { this.traits = traits; traitCount = traits.length; sliceCount = 1; doSlices = false; mostRecentSamplingDate = mrsd; this.contourMode = contourMode; this.sliceMode = sliceMode; sdr = getSRD; if (progress != null) { if (progress.equalsIgnoreCase("true")) { sliceProgressReport = true; } else if (progress.equalsIgnoreCase("check")) { sliceProgressReport = true; checkSliceContours = true; } } if (sliceHeights != null) { sliceCount = sliceHeights.length; doSlices = true; this.sliceHeights = sliceHeights; if ((mostRecentSamplingDate - sliceHeights[sliceHeights.length - 1]) < 0) { ancient = true; } } values = new ArrayList<List<List<Trait>>>(sliceCount); for (int i = 0; i < sliceCount; i++) { List<List<Trait>> thisSlice = new ArrayList<List<Trait>>(traitCount); values.add(thisSlice); for (int j = 0; j < traitCount; j++) { List<Trait> thisTraitSlice = new ArrayList<Trait>(); thisSlice.add(thisTraitSlice); } } if (summarizeRoot) { rootValues = new ArrayList<List<Trait>>(traitCount); for (int k = 0; k < traitCount; k++) { List<Trait> thisTrait = new ArrayList<Trait>(); rootValues.add(thisTrait); } } if (summarizeTips) { tipValues = new ArrayList<List<List<Trait>>>(); tipNames = new ArrayList<String>(); } try { readAndAnalyzeTrees(treeFileName, burnin, skipEvery, traits, sliceHeights, impute, trueNoise, normalize, branchNormalization, branchset, backboneTaxa); } catch (IOException e) { System.err.println("Error reading file: " + treeFileName); System.exit(-1); } catch (Importer.ImportException e) { System.err.println("Error parsing trees in file: " + treeFileName); System.exit(-1); } if (values.get(0).get(0).size() == 0) { System.err.println("Trait(s) values missing from trees."); System.exit(-1); } progressStream.println(treesRead + " trees read."); progressStream.println(treesAnalyzed + " trees analyzed."); } private Element rootElement; private Element documentElement; private Element contourFolderElement; private Element pointsFolderElement; private Element nodeFolderElement; private StringBuffer tabOutput = new StringBuffer(); public void output(String outFileName, boolean summaryOnly, final boolean summarizeRoot, final boolean summarizeTips, boolean contours, boolean points, OutputFormat outputFormat, double[] hpdValues, String sdrFile) { resultsStream = System.out; if (outFileName != null) { try { resultsStream = new PrintStream(new File(outFileName)); } catch (IOException e) { System.err.println("Error opening file: " + outFileName); System.exit(-1); } } if (!summaryOnly) { outputHeader(traits); if (sliceHeights == null || sliceHeights.length == 0) { outputSlice(0, Double.NaN); } else { for (int i = 0; i < sliceHeights.length; i++) { outputSlice(i, sliceHeights[i]); } } } else { // Output summaries if (outputFormat == OutputFormat.XML) { rootElement = new Element("xml"); } else if (outputFormat == OutputFormat.KML) { Element hpdSchema = new Element("Schema"); hpdSchema.setAttribute("id", "HPD_Schema"); hpdSchema.addContent(new Element("SimpleField") .setAttribute("name", "Name") .setAttribute("type", "string") .addContent(new Element("displayName").addContent("Name"))); hpdSchema.addContent(new Element("SimpleField") .setAttribute("name", "Description") .setAttribute("type", "string") .addContent(new Element("displayName").addContent("Description"))); hpdSchema.addContent(new Element("SimpleField") .setAttribute("name", "Time") .setAttribute("type", "double") .addContent(new Element("displayName").addContent("Time"))); hpdSchema.addContent(new Element("SimpleField") .setAttribute("name", "Height") .setAttribute("type", "double") .addContent(new Element("displayName").addContent("Height"))); hpdSchema.addContent(new Element("SimpleField") .setAttribute("name", "HPD") .setAttribute("type", "double") .addContent(new Element("displayName").addContent("HPD"))); Element nodeSchema = new Element("Schema"); nodeSchema.setAttribute("id", "Point_Schema"); nodeSchema.addContent(new Element("SimpleField") .setAttribute("name", "Name") .setAttribute("type", "string") .addContent(new Element("displayName").addContent("Name"))); nodeSchema.addContent(new Element("SimpleField") .setAttribute("name", "Description") .setAttribute("type", "string") .addContent(new Element("displayName").addContent("Description"))); nodeSchema.addContent(new Element("SimpleField") .setAttribute("name", "Time") .setAttribute("type", "double") .addContent(new Element("displayName").addContent("Time"))); nodeSchema.addContent(new Element("SimpleField") .setAttribute("name", "Height") .setAttribute("type", "double") .addContent(new Element("displayName").addContent("Height"))); if (contours) { contourFolderElement = new Element("Folder"); Element contourFolderNameElement = new Element("name"); contourFolderNameElement.addContent("surface HPD regions"); //rootElement.setAttribute("xmlns","http://earth.google.com/kml/2.2"); contourFolderElement.addContent(contourFolderNameElement); } if (points) { pointsFolderElement = new Element("Folder"); Element pointsFolderNameElement = new Element("name"); pointsFolderNameElement.addContent("points"); //rootElement.setAttribute("xmlns","http://earth.google.com/kml/2.2"); pointsFolderElement.addContent(pointsFolderNameElement); } if (sliceMode == TimeSlicer.SliceMode.NODES) { nodeFolderElement = new Element("Folder"); Element nodeFolderNameElement = new Element("name"); nodeFolderNameElement.addContent("nodes"); //rootElement.setAttribute("xmlns","http://earth.google.com/kml/2.2"); nodeFolderElement.addContent(nodeFolderNameElement); } Element documentNameElement = new Element("name"); String documentName = outFileName; if (documentName == null) documentName = "default"; if (documentName.endsWith(".kml")) documentName = documentName.replace(".kml", ""); documentNameElement.addContent(documentName); documentElement = new Element("Document"); documentElement.addContent(documentNameElement); documentElement.addContent(hpdSchema); documentElement.addContent(nodeSchema); if (contourFolderElement != null) { documentElement.addContent(contourFolderElement); } if (pointsFolderElement != null) { documentElement.addContent(pointsFolderElement); } if (nodeFolderElement != null) { documentElement.addContent(nodeFolderElement); } rootElement = new Element("kml"); rootElement.addContent(documentElement); } if (sliceHeights == null) { for (double hpdValue : hpdValues) { summarizeSlice(0, Double.NaN, contours, points, outputFormat, hpdValue); } } else { if (outputFormat == OutputFormat.TAB) { if (mostRecentSamplingDate > 0) { tabOutput.append("trait\t" + "sliceTime\t" + "mean\t" + "stdev\t" + "HPDlow\t" + "HPDup"); } else { tabOutput.append("trait\t" + "sliceHeight\t" + "mean\t" + "stdev\t" + "HPDlow\t" + "HPDup"); } } for (int i = 0; i < sliceHeights.length; i++) { for (double hpdValue : hpdValues) { summarizeSlice(i, sliceHeights[i], contours, points, outputFormat, hpdValue); } } } if (summarizeRoot) { for (double hpdValue : hpdValues) { summarizeRoot(contours, points, outputFormat, hpdValue); } } if (summarizeTips) { for (double hpdValue : hpdValues) { summarizeTips(contours, points, outputFormat, hpdValue); } } if (outputFormat == OutputFormat.TAB) { resultsStream.println(tabOutput); } else { XMLOutputter xmlOutputter = new XMLOutputter(Format.getPrettyFormat().setTextMode(Format.TextMode.PRESERVE)); try { xmlOutputter.output(rootElement, resultsStream); } catch (IOException e) { System.err.println("IO Exception encountered: " + e.getMessage()); System.exit(-1); } } } // writes out dispersal rate summaries for the whole tree, there is a beast xml statistic that can do this now // if (containsLocation) { // try{ // PrintWriter dispersalRateFile = new PrintWriter(new FileWriter("dispersalRates.log"), true); // dispersalRateFile.print("state"+"\t"+"dispersalRate(native units)"+"\t"+"dispersalRate(great circle distance, km)\r"); // for(int x = 0; x < dispersalrates.size(); x++ ) { // dispersalRateFile.print(x+"\t"+dispersalrates.get(x)+"\r"); // } // // } catch (IOException e) { // System.err.println("IO Exception encountered: "+e.getMessage()); // System.exit(-1); // } // } if (sdr) { double[][] sliceTreeWeightedAverageRates = new double[sliceTreeDistanceArrays.size()][sliceCount]; double[][] sliceTreeMaxRates = new double[sliceTreeDistanceArrays.size()][sliceCount]; double[][] sliceTreeDistances = new double[sliceCount][sliceTreeDistanceArrays.size()]; double[][] sliceTreeTimes = new double[sliceCount][sliceTreeDistanceArrays.size()]; //double[][] sliceTreeMaxDistances = new double[sliceCount][sliceTreeMaxDistanceArrays.size()]; double[][] sliceTreeMaxDistances = new double[sliceTreeMaxDistanceArrays.size()][sliceCount]; double[][] sliceTreeTimesFromRoot = new double[sliceCount][sliceTreeTimeFromRootArrays.size()]; double[][] sliceTreeDiffusionCoefficients = new double[sliceTreeDiffusionCoefficientArrays.size()][sliceCount]; double[][] sliceTreeDiffusionCoefficientVariances = new double[sliceTreeDiffusionCoefficientVarianceArrays.size()][sliceCount]; //double[][] sliceTreeWeightedAverageDiffusionCoefficients = new double[sliceTreeDistanceArrays.size()][sliceCount]; for (int q = 0; q < sliceTreeDistanceArrays.size(); q++) { double[] distanceArray = (double[]) sliceTreeDistanceArrays.get(q); double[] timeArray = (double[]) sliceTreeTimeArrays.get(q); double[] maxDistanceArray = (double[]) sliceTreeMaxDistanceArrays.get(q); double[] timeFromRootArray = (double[]) sliceTreeTimeFromRootArrays.get(q); double[] diffusionCoefficientArray = (double[]) sliceTreeDiffusionCoefficientArrays.get(q); double[] diffusionCoefficientVarianceArray = (double[]) sliceTreeDiffusionCoefficientVarianceArrays.get(q); for (int r = 0; r < distanceArray.length; r++) { sliceTreeDistances[r][q] = distanceArray[r]; sliceTreeTimes[r][q] = timeArray[r]; sliceTreeMaxDistances[q][r] = maxDistanceArray[r]; sliceTreeTimesFromRoot[r][q] = timeFromRootArray[r]; sliceTreeDiffusionCoefficients[q][r] = diffusionCoefficientArray[r]; sliceTreeDiffusionCoefficientVariances[q][r] = diffusionCoefficientVarianceArray[r]; } } //print2DArray(sliceTreeDistances,"sliceTreeDistances.txt"); //print2DArray(sliceTreeTimes,"sliceTreeTimes.txt"); //print2DTransposedArray(sliceTreeDiffusionCoefficients,"sliceTreeDiffusionCoefficients.txt"); //print2DTransposedArray(sliceTreeDiffusionCoefficientVariances,"sliceTreeDiffusionCoefficientVariances.txt"); //print2DArray(sliceTreeTimesFromRoot,"sliceTreeTimesFromRoot.txt"); if (sliceCount > 1) { for (int s = 0; s < sliceTreeDistanceArrays.size(); s++) { double[] distanceArray = (double[]) sliceTreeDistanceArrays.get(s); double[] timeArray = (double[]) sliceTreeTimeArrays.get(s); double[] maxDistanceArray = (double[]) sliceTreeMaxDistanceArrays.get(s); double[] timeFromRoot = (double[]) sliceTreeTimeFromRootArrays.get(s); for (int t = 0; t < (sliceCount - 1); t++) { sliceTreeMaxRates[s][t] = (maxDistanceArray[t]) / (timeFromRoot[t]); if ((timeArray[t] - timeArray[t + 1]) > ((Double) treeLengths.get(s) * treeLengthPercentage)) { sliceTreeWeightedAverageRates[s][t] = (distanceArray[t] - distanceArray[t + 1]) / (timeArray[t] - timeArray[t + 1]); //sliceTreeWeightedAverageRates[s][t] = (sliceTreeDistances[t][s] - sliceTreeDistances[t+1][s])/(sliceTreeTimes[t][s] - sliceTreeTimes[t+1][s]); //sliceTreeWeightedAverageDiffusionCoefficients[s][t] = Math.pow((distanceArray[t] - distanceArray[t+1]),2.0)/(4.0*(timeArray[t] - timeArray[t+1])); } else { if (timeArray[t] > 0) { if (t == 0) { throw new RuntimeException("Philippe to fix: the time slices are expected in ascending height order"); } sliceTreeWeightedAverageRates[s][t] = sliceTreeWeightedAverageRates[s][t - 1]; //sliceTreeWeightedAverageDiffusionCoefficients[s][t] = sliceTreeWeightedAverageDiffusionCoefficients[s][t-1]; } else { //set it to NaN, we ignore NaNs when getting summary stats sliceTreeWeightedAverageRates[s][t] = Double.NaN; //sliceTreeWeightedAverageDiffusionCoefficients[s][t] = Double.NaN; } } } if ((timeArray[sliceCount - 1]) > ((Double) treeLengths.get(s) * treeLengthPercentage)) { sliceTreeWeightedAverageRates[s][sliceCount - 1] = (distanceArray[sliceCount - 1]) / (timeArray[sliceCount - 1]); //sliceTreeWeightedAverageDiffusionCoefficients[s][sliceCount-1] = Math.pow(distanceArray[sliceCount-1],2.0)/(4.0*timeArray[sliceCount-1]); } else { if ((timeArray[sliceCount - 1]) > 0) { sliceTreeWeightedAverageRates[s][sliceCount - 1] = sliceTreeWeightedAverageRates[s][sliceCount - 2]; //sliceTreeWeightedAverageDiffusionCoefficients[s][sliceCount-1] = sliceTreeWeightedAverageDiffusionCoefficients[s][sliceCount-2]; } else { sliceTreeWeightedAverageRates[s][sliceCount - 1] = (distanceArray[sliceCount - 1]) / (timeArray[sliceCount - 1]); //sliceTreeWeightedAverageDiffusionCoefficients[s][sliceCount-1] = Math.pow(distanceArray[sliceCount-1],2.0)/(4.0*timeArray[sliceCount-1]); } } sliceTreeMaxRates[s][sliceCount - 1] = maxDistanceArray[sliceCount - 1] / timeFromRoot[sliceCount - 1]; } } else { for (int s = 0; s < sliceTreeDistanceArrays.size(); s++) { double[] distanceArray = (double[]) sliceTreeDistanceArrays.get(s); double[] timeArray = (double[]) sliceTreeTimeArrays.get(s); double[] maxDistanceArray = (double[]) sliceTreeMaxDistanceArrays.get(s); double[] timeFromRoot = (double[]) sliceTreeTimeFromRootArrays.get(s); sliceTreeWeightedAverageRates[s][0] = distanceArray[0] / timeArray[0]; //sliceTreeWeightedAverageDiffusionCoefficients[s][0] = Math.pow(distanceArray[0],2.0)/(4.0*timeArray[0]); sliceTreeMaxRates[s][0] = maxDistanceArray[0] / timeFromRoot[0]; } } //print2DArray(sliceTreeWeightedAverageRates,"sliceTreeWeightedAverageRates.txt"); try { PrintWriter sliceDispersalRateFile = new PrintWriter(new FileWriter(sdrFile), true); sliceDispersalRateFile.print("sliceTime" + "\t"); if (mostRecentSamplingDate > 0) { sliceDispersalRateFile.print("realTime" + "\t"); } sliceDispersalRateFile.print("mean dispersalRate" + "\t" + "hpd low" + "\t" + "hpd up" + "\t" + "mean MaxDispersalRate" + "\t" + "hpd low" + "\t" + "hpd up" + "\t" + "mean MaxDispersalDistance" + "\t" + "hpd low" + "\t" + "hpd up" + "\t" + "mean cumulative DiffusionCoefficient" + "\t" + "hpd low" + "\t" + "hpd up" + "\r"); double[] meanWeightedAverageDispersalRates = meanColNoNaN(sliceTreeWeightedAverageRates); double[][] hpdWeightedAverageDispersalRates = getArrayHPDintervals(sliceTreeWeightedAverageRates); double[] meanMaxDispersalDistances = meanColNoNaN(sliceTreeMaxDistances); double[][] hpdMaxDispersalDistances = getArrayHPDintervals(sliceTreeMaxDistances); double[] meanMaxDispersalRates = meanColNoNaN(sliceTreeMaxRates); double[][] hpdMaxDispersalRates = getArrayHPDintervals(sliceTreeMaxRates); double[] meanDiffusionCoefficients = meanColNoNaN(sliceTreeDiffusionCoefficients); double[][] hpdDiffusionCoefficients = getArrayHPDintervals(sliceTreeDiffusionCoefficients); //double[] meanWeightedAverageDiffusionCoefficients = meanColNoNaN(sliceTreeWeightedAverageDiffusionCoefficients); //double[][] hpdWeightedAverageDiffusionCoefficients = getArrayHPDintervals(sliceTreeWeightedAverageDiffusionCoefficients); for (int u = 0; u < sliceCount; u++) { sliceDispersalRateFile.print(sliceHeights[u] + "\t"); if (mostRecentSamplingDate > 0) { sliceDispersalRateFile.print((mostRecentSamplingDate - sliceHeights[u]) + "\t"); } sliceDispersalRateFile.print(meanWeightedAverageDispersalRates[u] + "\t" + hpdWeightedAverageDispersalRates[u][0] + "\t" + hpdWeightedAverageDispersalRates[u][1] + "\t" + meanMaxDispersalRates[u] + "\t" + hpdMaxDispersalRates[u][0] + "\t" + hpdMaxDispersalRates[u][1] + "\t" + meanMaxDispersalDistances[u] + "\t" + hpdMaxDispersalDistances[u][0] + "\t" + hpdMaxDispersalDistances[u][1] + "\t" + meanDiffusionCoefficients[u] + "\t" + hpdDiffusionCoefficients[u][0] + "\t" + hpdDiffusionCoefficients[u][1] + "\r"); } sliceDispersalRateFile.close(); } catch (IOException e) { System.err.println("IO Exception encountered: " + e.getMessage()); System.exit(-1); } } } enum Normalization { LENGTH, HEIGHT, NONE } enum OutputFormat { TAB, KML, XML } enum BranchSet { ALL, INT, EXT, BACKBONE } enum SliceMode { BRANCHES, NODES, } public static <T extends Enum<T>> String[] enumNamesToStringArray(T[] values) { int i = 0; String[] result = new String[values.length]; for (T value : values) { result[i++] = value.name(); } return result; } private void addDimInfo(Element element, int j, int dim) { if (dim > 1) element.setAttribute("dim", Integer.toString(j + 1)); } private void summarizeRoot(boolean contours, boolean points, OutputFormat outputFormat, double hpdValue){ if (sliceProgressReport) { progressStream.print("summarizing root" + "\t"); progressStream.print("hpd " + (hpdValue * 100) + "\t"); } Element contourElement = null; Element pointsElement = null; if (contours) { contourElement = new Element("Folder"); Element name = new Element("name"); name.addContent("root_hpd" + (hpdValue * 100)); contourElement.addContent(name); } if (points) { pointsElement = new Element("Folder"); Element name = new Element("name"); name.addContent("root_points"); pointsElement.addContent(name); } for (int traitIndex = 0; traitIndex < rootValues.size(); traitIndex++) { List<Trait> thisTrait = rootValues.get(traitIndex); if (thisTrait.size() == 0) { return; } boolean isNumber = thisTrait.get(0).isNumber(); boolean isMultivariate = thisTrait.get(0).isMultivariate(); int dim = thisTrait.get(0).getDim(); boolean isBivariate = isMultivariate && dim == 2; if (isNumber) { if (isBivariate) { if (outputFormat == OutputFormat.KML) { int count = thisTrait.size(); double[][] y = new double[dim][count]; double[] h = new double[count]; for (int i = 0; i < count; i++) { Trait trait = thisTrait.get(i); double[] value = trait.getValue(); h[i] = trait.getHeight(); for (int j = 0; j < dim; j++) { y[j][i] = value[j]; } } if(sliceMode == SliceMode.NODES) { Element nodeSliceElement = generateNodeSliceElement(Double.NaN, y, -1); nodeFolderElement.addContent(nodeSliceElement); } if (contourElement != null) { String name = "root_hpd" + (hpdValue * 100); generateContours(name, contourElement, null, y, -1, Double.NaN, Double.NaN, hpdValue); } if (pointsElement != null) { String name = "root_points"; generatePoints(name, pointsElement, y, 0, 0, h) ; } } } } } if (contourFolderElement != null) { contourFolderElement.addContent(contourElement); } if (pointsFolderElement != null) { pointsFolderElement.addContent(pointsElement); } if (sliceProgressReport) { progressStream.print("\r"); } } private void summarizeTips(boolean contours, boolean points, OutputFormat outputFormat, double hpdValue){ if (sliceProgressReport) { progressStream.print("summarizing tips" + "\t"); progressStream.print("hpd " + (hpdValue * 100) + "\t"); } Element contourElement = null; Element pointsElement = null; if (contours) { contourElement = new Element("Folder"); Element name = new Element("name"); name.addContent("tips_hpd" + (hpdValue * 100)); contourElement.addContent(name); } if (points) { pointsElement = new Element("Folder"); Element name = new Element("name"); name.addContent("tips_points"); pointsElement.addContent(name); } for (int traitIndex = 0; traitIndex < tipValues.size(); traitIndex++) { List<List<Trait>> thisTrait = tipValues.get(traitIndex); for (int tipIndex = 0; tipIndex < thisTrait.size(); tipIndex++) { List<Trait> thisTip = thisTrait.get(tipIndex); if (thisTip.size() == 0) { return; } boolean isNumber = thisTip.get(0).isNumber(); boolean isMultivariate = thisTip.get(0).isMultivariate(); int dim = thisTip.get(0).getDim(); boolean isBivariate = isMultivariate && dim == 2; if (isNumber) { if (isBivariate) { if (outputFormat == OutputFormat.KML) { int count = thisTrait.size(); double[][] y = new double[dim][count]; double[] h = new double[count]; for (int i = 0; i < count; i++) { Trait trait = thisTip.get(i); h[i] = trait.getHeight(); double[] value = trait.getValue(); for (int j = 0; j < dim; j++) { y[j][i] = value[j]; } } if (contourElement != null) { String name = tipNames.get(tipIndex) + "_hpd"; generateContours(name, contourElement, null, y, -1, Double.NaN, Double.NaN, hpdValue); } if (pointsElement != null) { String name = tipNames.get(tipIndex) + "_points"; generatePoints(name, pointsElement, y, 0, 0, h) ; } } } } } } if (contourFolderElement != null) { contourFolderElement.addContent(contourElement); } if (pointsFolderElement != null) { pointsFolderElement.addContent(pointsElement); } if (sliceProgressReport) { progressStream.print("\r"); } } private void summarizeSlice(int slice, double sliceValue, boolean contours, boolean points, OutputFormat outputFormat, double hpdValue) { //if (outputFormat == OutputFormat.TAB) // throw new RuntimeException("Only XML/KML output is implemented"); Element contourElement = null; Element pointsElement = null; if (outputFormat == OutputFormat.XML) { if (contours) { contourElement = new Element(SLICE_ELEMENT); contourElement.setAttribute(SLICE_VALUE, Double.toString(sliceValue)); } } else if (outputFormat == OutputFormat.KML) { if (contours) { contourElement = new Element("Folder"); Element name = new Element("name"); name.addContent("slice" + Double.toString(sliceValue) + "_hpd" + (hpdValue * 100)); contourElement.addContent(name); } if (points) { pointsElement = new Element("Folder"); Element name = new Element("name"); name.addContent("slice" + Double.toString(sliceValue) + "_points"); pointsElement.addContent(name); } } List<List<Trait>> thisSlice = values.get(slice); int traitCount = thisSlice.size(); for (int traitIndex = 0; traitIndex < traitCount; traitIndex++) { // if (outputFormat == OutputFormat.KML) { // summarizeSliceTrait(folder, slice, thisSlice.get(traitIndex), traitIndex, sliceValue, // outputFormat, // hpdValue); // // } else { summarizeSliceTrait(contourElement, pointsElement, slice, thisSlice.get(traitIndex), traitIndex, sliceValue, outputFormat, hpdValue); // } } if (outputFormat == OutputFormat.KML) { if (contourFolderElement != null) { contourFolderElement.addContent(contourElement); } if (pointsFolderElement != null) { pointsFolderElement.addContent(pointsElement); } } else if (outputFormat == OutputFormat.XML && contourElement != null) { if (contourElement != null) { rootElement.addContent(contourElement); } if (pointsElement != null) { rootElement.addContent(pointsElement); } } } private void summarizeSliceTrait(Element contourElement, Element pointsElement, int slice, List<Trait> thisTrait, int traitIndex, double sliceValue, OutputFormat outputFormat, double hpdValue) { if (thisTrait.size() == 0) { return; } boolean isNumber = thisTrait.get(0).isNumber(); boolean isMultivariate = thisTrait.get(0).isMultivariate(); int dim = thisTrait.get(0).getDim(); boolean isBivariate = isMultivariate && dim == 2; if (sliceProgressReport) { progressStream.print("slice " + sliceValue + "\t"); progressStream.print("hpd " + (hpdValue * 100) + "\t"); if (mostRecentSamplingDate > 0) { progressStream.print("time=" + (mostRecentSamplingDate - sliceValue) + "\t"); } progressStream.print("trait=" + traits[traitIndex] + "\t"); } if (isNumber) { Element traitElement = null; if (outputFormat == OutputFormat.XML || outputFormat == OutputFormat.TAB) { traitElement = new Element(TRAIT); traitElement.setAttribute(NAME, traits[traitIndex]); } if (outputFormat == OutputFormat.KML) { if (useStyles) { Element styleElement = new Element(STYLE); constructPolygonStyleElement(styleElement, sliceValue); documentElement.addContent(styleElement); } } int count = thisTrait.size(); System.out.println(count); double[][] y = new double[dim][count]; for (int i = 0; i < count; i++) { Trait trait = thisTrait.get(i); double[] value = trait.getValue(); for (int j = 0; j < dim; j++) { y[j][i] = value[j]; } } if (outputFormat == OutputFormat.XML || outputFormat == OutputFormat.TAB) { // Compute marginal means and standard deviations for (int j = 0; j < dim; j++) { List<Double> x = new ArrayList(); for (int k = 0; k < y[dim].length; k++) { x.add(y[dim][k]); } TraceDistribution trace = new TraceDistribution(x, TraceFactory.TraceType.DOUBLE); Element statsElement = new Element("stats"); addDimInfo(statsElement, j, dim); StringBuffer sb = new StringBuffer(); sb.append(KMLCoordinates.NEWLINE); tabOutput.append(KMLCoordinates.NEWLINE); tabOutput.append(traits[traitIndex] + "\t"); if (mostRecentSamplingDate > 0) { tabOutput.append((mostRecentSamplingDate - sliceValue) + "\t"); } else { tabOutput.append(sliceValue + "\t"); } sb.append(String.format(KMLCoordinates.FORMAT, trace.getMean())).append(KMLCoordinates.SEPARATOR); tabOutput.append(String.format(KMLCoordinates.FORMAT, trace.getMean())).append("\t"); sb.append(String.format(KMLCoordinates.FORMAT, trace.getStdError())).append(KMLCoordinates.SEPARATOR); tabOutput.append(String.format(KMLCoordinates.FORMAT, trace.getStdError())).append("\t"); sb.append(String.format(KMLCoordinates.FORMAT, trace.getLowerHPD())).append(KMLCoordinates.SEPARATOR); tabOutput.append(String.format(KMLCoordinates.FORMAT, trace.getLowerHPD())).append("\t"); sb.append(String.format(KMLCoordinates.FORMAT, trace.getUpperHPD())).append(KMLCoordinates.NEWLINE); tabOutput.append(String.format(KMLCoordinates.FORMAT, trace.getUpperHPD())).append("\t"); statsElement.addContent(sb.toString()); traitElement.addContent(statsElement); } } if (isBivariate) { Element nodeSliceElement = null; if(sliceMode == SliceMode.NODES) { nodeSliceElement = generateNodeSliceElement(sliceValue, y, slice); nodeFolderElement.addContent(nodeSliceElement); if (useStyles) { Element styleElement = new Element(STYLE); constructNodeStyleElement(styleElement, sliceValue); documentElement.addContent(styleElement); } } double date = mostRecentSamplingDate - sliceValue; if (pointsElement != null) { String name = "" + date + "_points"; generatePointsElement(name, pointsElement, y, date, sliceValue); } if (contourElement != null) { String name = "" + date + "_hpd" + hpdValue; generateContours(name, contourElement, traitElement, y, slice, date, sliceValue, hpdValue); } } if (outputFormat == OutputFormat.XML) contourElement.addContent(traitElement); } // else skip if (sliceProgressReport) { progressStream.print("\r"); } } private void generatePointsElement(String name, Element pointsFolderElement, double[][] y, double date, double height) { Element pointsElement = new Element("Folder"); Element nameElement = new Element("name"); nameElement.addContent(name); pointsElement.addContent(nameElement); for (int a = 0; a < y[0].length; a++) { Element placemarkElement = new Element("Placemark"); if (sliceCount > 1) { Element timeSpan = new Element("TimeSpan"); Element begin = new Element("begin"); if (!ancient) { calendar.set(Calendar.YEAR, (int) Math.floor(date)); calendar.set(Calendar.DAY_OF_YEAR, (int) (365 * (date - Math.floor(date)))); begin.addContent(dateFormat.format(calendar.getTime())); } else { begin.addContent(Integer.toString((int)Math.round(date))); } timeSpan.addContent(begin); placemarkElement.addContent(timeSpan); } placemarkElement.addContent(generatePointData(name, height)); Element pointElement = new Element("Point"); Element coordinates = new Element("coordinates"); coordinates.addContent(y[1][a]+","+y[0][a]+",0"); pointElement.addContent(coordinates); placemarkElement.addContent(pointElement); pointsElement.addContent(placemarkElement); } pointsFolderElement.addContent(pointsElement); } private void generatePoints(String name, Element pointsFolderElement, double[][] y, double date, double height, double[] heights) { for (int a = 0; a < y[0].length; a++) { Element placemarkElement = new Element("Placemark"); if (heights == null) { placemarkElement.addContent(generatePointData(name, height)); } else { placemarkElement.addContent(generatePointData(name, heights[a])); } Element pointElement = new Element("Point"); Element coordinates = new Element("coordinates"); coordinates.addContent(y[1][a]+","+y[0][a]+",0"); pointElement.addContent(coordinates); placemarkElement.addContent(pointElement); pointsFolderElement.addContent(placemarkElement); } } private void generateContours(String name, Element sliceElement, Element traitElement, double[][] y, int slice, double date, double height, double hpdValue) { //to test how much points are within the polygons double numberOfPointsInPolygons = 0; double totalArea = 0; ContourMaker contourMaker; if (contourMode == ContourMode.JAVA) contourMaker = new KernelDensityEstimator2D(y[0], y[1], GRIDSIZE); // contourMaker = new KernelDensityEstimator2D(y[0], y[1], BANDWIDTHLIMIT); else if (contourMode == ContourMode.R) contourMaker = new ContourWithR(y[0], y[1], GRIDSIZE); else if (contourMode == ContourMode.SNYDER) contourMaker = new ContourWithSynder(y[0], y[1], GRIDSIZE); // contourMaker = new ContourWithSynder(y[0], y[1], BANDWIDTHLIMIT); else throw new RuntimeException("Unimplemented ContourModel!"); ContourPath[] paths = contourMaker.getContourPaths(hpdValue); int pathCounter = 1; for (ContourPath path : paths) { KMLCoordinates coords = new KMLCoordinates(path.getAllX(), path.getAllY()); if (traitElement != null) { Element regionElement = new Element(REGIONS_ELEMENT); regionElement.setAttribute(DENSITY_VALUE, Double.toString(hpdValue)); regionElement.addContent(coords.toXML()); traitElement.addContent(regionElement); } // only if the trait is location we will write KML if (sliceElement != null) { //because KML polygons require long,lat,alt we need to switch lat and long first coords.switchXY(); String name1 = name + "_path_"+pathCounter; Element placemarkElement = generatePlacemarkElementWithPolygon(name1, date, hpdValue, height, coords, slice, pathCounter); //testing how many points are within the polygon if (checkSliceContours) { Element testElement = new Element("test"); testElement.addContent(coords.toXML()); Polygon2D testPolygon = new Polygon2D(testElement); totalArea += testPolygon.calculateArea(); double[][] dest = new double[y.length][y[0].length]; for (int i = 0; i < y.length; i++) { for (int j = 0; j < y[0].length; j++) { dest[i][j] = y[i][j]; } } numberOfPointsInPolygons += getNumberOfPointsInPolygon(dest, testPolygon); } sliceElement.addContent(placemarkElement); } pathCounter ++; } if (checkSliceContours) { progressStream.print("numberOfContours=" + paths.length + "\tfreqOfPointsInContour=" + numberOfPointsInPolygons / y[0].length + "\ttotalArea = " + totalArea); } if (paths.length == 0 && !sliceProgressReport) { progressStream.println("Warning: slice at height " + height + ", contains no contours."); } } public static int getNumberOfPointsInPolygon(double[][] pointsArray, Polygon2D testPolygon) { int numberOfPointsInPolygon = 0; for (int x = 0; x < pointsArray[0].length; x++) { if (testPolygon.containsPoint2D(new Point2D.Double(pointsArray[0][x], pointsArray[1][x]))) { numberOfPointsInPolygon++; } } return numberOfPointsInPolygon; } private void constructPolygonStyleElement(Element styleElement, double sliceValue) { double date; if (Double.isNaN(sliceValue)){ date = Double.NaN; styleElement.setAttribute(ID, ROOT_ELEMENT + "_hpd" + "_style"); } else { date = mostRecentSamplingDate - sliceValue; styleElement.setAttribute(ID, REGIONS_ELEMENT + date + "_style"); } Element lineStyle = new Element("LineStyle"); Element width = new Element("width"); width.addContent(WIDTH); lineStyle.addContent(width); Element polyStyle = new Element("PolyStyle"); Element color = new Element("color"); double[] minMax = new double[2]; minMax[0] = sliceHeights[0]; minMax[1] = sliceHeights[(sliceHeights.length - 1)]; String colorString; if (sliceValue == Double.NaN){ colorString = startHPDColor; } else { colorString = getKMLColor(sliceValue, minMax, endHPDColor, startHPDColor); } color.addContent(opacity + colorString); Element outline = new Element("outline"); outline.addContent("0"); polyStyle.addContent(color); polyStyle.addContent(outline); styleElement.addContent(lineStyle); styleElement.addContent(polyStyle); } private void constructNodeStyleElement(Element styleElement, double sliceValue) { double date; if (Double.isNaN(sliceValue)){ date = Double.NaN; styleElement.setAttribute(ID, ROOT_ELEMENT + date + "_style"); } else { date = mostRecentSamplingDate - sliceValue; styleElement.setAttribute(ID, NODE_ELEMENT + date + "_style"); } Element iconStyle = new Element("IconStyle"); Element scale = new Element("scale"); scale.addContent("1"); iconStyle.addContent(scale); Element icon = new Element("Icon"); Element href = new Element("href"); href.addContent(ICON); icon.addContent(href); iconStyle.addContent(icon); Element color = new Element("color"); double[] minMax = new double[2]; minMax[0] = sliceHeights[0]; minMax[1] = sliceHeights[(sliceHeights.length - 1)]; String colorString; if (sliceValue == Double.NaN){ colorString = startHPDColor; } else { colorString = getKMLColor(sliceValue, minMax, endHPDColor, startHPDColor); } color.addContent(opacity + colorString); iconStyle.addContent(color); Element colorMode = new Element("colorMode"); colorMode.addContent("normal"); iconStyle.addContent(colorMode); styleElement.addContent(iconStyle); } private Element generatePlacemarkElementWithPolygon(String name, double date, double hpdValue, double sliceValue, KMLCoordinates coords, int sliceInteger, int pathCounter) { Element placemarkElement = new Element("Placemark"); Element placemarkNameElement = new Element("name"); placemarkNameElement.addContent(name); placemarkElement.addContent(placemarkNameElement); Element visibility = new Element("visibility"); if (sliceInteger == -1){ visibility.addContent("0"); } else { visibility.addContent("1"); } placemarkElement.addContent(visibility); if (!Double.isNaN(date)) { Element timeSpan = new Element("TimeSpan"); Element begin = new Element("begin"); if (!ancient) { calendar.set(Calendar.YEAR, (int) Math.floor(date)); calendar.set(Calendar.DAY_OF_YEAR, (int) (365 * (date - Math.floor(date)))); begin.addContent(dateFormat.format(calendar.getTime())); } else { begin.addContent(Integer.toString((int)Math.round(date))); } timeSpan.addContent(begin); // if (sliceInteger > 1) { // Element end = new Element("end"); // end.addContent(Double.toString(mostRecentSamplingDate- sliceHeights[(sliceInteger-1)])); // timeSpan.addContent(end); // } placemarkElement.addContent(timeSpan); } if (useStyles) { Element style = new Element("styleUrl"); if (sliceInteger == -1){ style.addContent("#" + ROOT_ELEMENT + "_hpd" + "_style"); } else { style.addContent("#" + REGIONS_ELEMENT + date + "_style"); } placemarkElement.addContent(style); } placemarkElement.addContent(generateContourData(name, date, sliceValue, hpdValue)); Element polygonElement = new Element("Polygon"); Element altitudeMode = new Element("altitudeMode"); altitudeMode.addContent("clampToGround"); polygonElement.addContent(altitudeMode); Element tessellate = new Element("tessellate"); tessellate.addContent("1"); polygonElement.addContent(tessellate); Element outerBoundaryIs = new Element("outerBoundaryIs"); Element LinearRing = new Element("LinearRing"); LinearRing.addContent(coords.toXML()); outerBoundaryIs.addContent(LinearRing); polygonElement.addContent(outerBoundaryIs); placemarkElement.addContent(polygonElement); return placemarkElement; } private Element generateNodeSliceElement(double sliceValue, double[][] nodes, int sliceInteger) { double date; Element nodeSliceElement = new Element("Folder"); Element nameNodeSliceElement = new Element("name"); String name; if (sliceInteger == -1){ date = Double.NaN; name = ROOT_ELEMENT; } else { date = mostRecentSamplingDate - sliceValue; name = "nodeSlice_"+sliceInteger+"_"+date; } nameNodeSliceElement.addContent(name); nodeSliceElement.addContent(nameNodeSliceElement); Element initialVisibility = new Element("visibility"); initialVisibility.addContent("0"); if (sliceInteger == -1){ nodeSliceElement.addContent(initialVisibility); } for (int a = 0; a < nodes[0].length; a++) { Element placemarkElement = new Element("Placemark"); // Element visibility = new Element("visibility"); // visibility.addContent("1"); // placemarkElement.addContent(visibility); if (sliceCount > 1) { Element timeSpan = new Element("TimeSpan"); Element begin = new Element("begin"); if (!ancient) { calendar.set(Calendar.YEAR, (int) Math.floor(date)); calendar.set(Calendar.DAY_OF_YEAR, (int) (365 * (date - Math.floor(date)))); begin.addContent(dateFormat.format(calendar.getTime())); } else { begin.addContent(Integer.toString((int)Math.round(date))); } timeSpan.addContent(begin); placemarkElement.addContent(timeSpan); } placemarkElement.addContent(generatePointData(name, sliceValue)); if (useStyles) { Element style = new Element("styleUrl"); if (sliceInteger == -1){ style.addContent("#" + ROOT_ELEMENT + date + "_style"); } else { style.addContent("#" + NODE_ELEMENT + date + "_style"); } placemarkElement.addContent(style); } Element pointElement = new Element("Point"); // Element altitudeMode = new Element("altitudeMode"); // altitudeMode.addContent("clampToGround"); // polygonElement.addContent(altitudeMode); // Element tessellate = new Element("tessellate"); // tessellate.addContent("1"); // polygonElement.addContent(tessellate); Element coordinates = new Element("coordinates"); coordinates.addContent(nodes[1][a]+","+nodes[0][a]+",0"); pointElement.addContent(coordinates); placemarkElement.addContent(pointElement); nodeSliceElement.addContent(placemarkElement); } return nodeSliceElement; } private Element generateContourData(String name, double date, double height, double hpd) { Element data = new Element("ExtendedData"); Element schemaData = new Element("SchemaData"); schemaData.setAttribute("schemaUrl", "#HPD_Schema"); schemaData.addContent(new Element("SimpleData").setAttribute("name", "Name").addContent(name)); // schemaData.addContent(new Element("SimpleData").setAttribute("name", "Description")); schemaData.addContent(new Element("SimpleData").setAttribute("name", "Time").addContent(Double.toString(date))); schemaData.addContent(new Element("SimpleData").setAttribute("name", "Height").addContent(Double.toString(height))); if (hpd > 0) { schemaData.addContent(new Element("SimpleData").setAttribute("name", "HPD").addContent(Double.toString(hpd))); } data.addContent(schemaData); return data; } private Element generatePointData(String name, double height) { Element data = new Element("ExtendedData"); Element schemaData = new Element("SchemaData"); schemaData.setAttribute("schemaUrl", "#Point_Schema"); double date = mostRecentSamplingDate - height; schemaData.addContent(new Element("SimpleData").setAttribute("name", "Name").addContent(name)); schemaData.addContent(new Element("SimpleData").setAttribute("name", "Time").addContent(Double.toString(date))); schemaData.addContent(new Element("SimpleData").setAttribute("name", "Height").addContent(Double.toString(height))); data.addContent(schemaData); return data; } private void outputHeader(String[] traits) { StringBuffer sb = new StringBuffer("slice"); for (int i = 0; i < traits.length; i++) { // Load first value to check dimensionality Trait trait = values.get(0).get(i).get(0); if (trait.isMultivariate()) { int dim = trait.getDim(); for (int j = 1; j <= dim; j++) sb.append(sep).append(traits[i]).append(j); } else sb.append(sep).append(traits[i]); } sb.append("\n"); resultsStream.print(sb); } // private List<Tree> importTrees(String treeFileName, int burnin) throws IOException, Importer.ImportException { // // int totalTrees = 10000; // // progressStream.println("Reading trees (bar assumes 10,000 trees)..."); // progressStream.println("0 25 50 75 100"); // progressStream.println("|--------------|--------------|--------------|--------------|"); // // int stepSize = totalTrees / 60; // if (stepSize < 1) stepSize = 1; // // List<Tree> treeList = new ArrayList<Tree>(); // BufferedReader reader1 = new BufferedReader(new FileReader(treeFileName)); // // String line1 = reader1.readLine(); // TreeImporter importer1; // if (line1.toUpperCase().startsWith("#NEXUS")) { // importer1 = new NexusImporter(new FileReader(treeFileName)); // } else { // importer1 = new NewickImporter(new FileReader(treeFileName)); // } // totalTrees = 0; // while (importer1.hasTree()) { // Tree treeTime = importer1.importNextTree(); // // if (totalTrees > burnin) // treeList.add(treeTime); // // if (totalTrees > 0 && totalTrees % stepSize == 0) { // progressStream.print("*"); // progressStream.flush(); // } // totalTrees++; // } // return treeList; // } private void readAndAnalyzeTrees(String treeFileName, int burnin, int skipEvery, String[] traits, double[] slices, boolean impute, boolean trueNoise, Normalization normalize, boolean divideByBranchLength, BranchSet branchset, Set backboneTaxa) throws IOException, Importer.ImportException { int totalTrees = 10000; int totalStars = 0; progressStream.println("Reading and analyzing trees (bar assumes 10,000 trees)..."); progressStream.println("0 25 50 75 100"); progressStream.println("|--------------|--------------|--------------|--------------|"); int stepSize = totalTrees / 60; if (stepSize < 1) stepSize = 1; BufferedReader reader1 = new BufferedReader(new FileReader(treeFileName)); String line1 = reader1.readLine(); TreeImporter importer1; if (line1.toUpperCase().startsWith("#NEXUS")) { importer1 = new NexusImporter(new FileReader(treeFileName)); } else { importer1 = new NewickImporter(new FileReader(treeFileName)); } totalTrees = 0; while (importer1.hasTree()) { Tree treeTime = importer1.importNextTree(); if (totalTrees % skipEvery == 0) { treesRead++; if (totalTrees >= burnin) { analyzeTree(treeTime, traits, slices, impute, trueNoise, normalize, divideByBranchLength, branchset, backboneTaxa); } } if (totalTrees > 0 && totalTrees % stepSize == 0) { progressStream.print("*"); totalStars++; if (totalStars % 61 == 0) progressStream.print("\n"); progressStream.flush(); } totalTrees++; } progressStream.print("\n"); } class Trait { Trait(Object obj) { this.obj = obj; if (obj instanceof Object[]) { isMultivariate = true; array = (Object[]) obj; } this.height = 0.0; } Trait(Object obj, double height) { this.obj = obj; if (obj instanceof Object[]) { isMultivariate = true; array = (Object[]) obj; } this.height = height; } public boolean isMultivariate() { return isMultivariate; } public boolean isNumber() { if (!isMultivariate) return (obj instanceof Double); return (array[0] instanceof Double); } public int getDim() { if (isMultivariate) { return array.length; } return 1; } public double[] getValue() { int dim = getDim(); double[] result = new double[dim]; if (!isMultivariate) { result[0] = (Double) obj; } else { for (int i = 0; i < dim; i++) result[i] = (Double) array[i]; } return result; } public double getHeight() { return height; } public void multiplyBy(double factor) { if (!isMultivariate) { obj = ((Double) obj * factor); } else { for (int i = 0; i < array.length; i++) { array[i] = ((Double) array[i] * factor); } } } private Object obj; private Object[] array; private boolean isMultivariate = false; private double height; public String toString() { if (!isMultivariate) return obj.toString(); StringBuffer sb = new StringBuffer(array[0].toString()); for (int i = 1; i < array.length; i++) sb.append(sep).append(array[i]); return sb.toString(); } } private List<List<List<Trait>>> values; private List<List<Trait>> rootValues; private List<List<List<Trait>>> tipValues; private List<String> tipNames; private void outputSlice(int slice, double sliceValue) { List<List<Trait>> thisSlice = values.get(slice); int traitCount = thisSlice.size(); int valueCount = thisSlice.get(0).size(); StringBuffer sb = new StringBuffer(); for (int v = 0; v < valueCount; v++) { if (Double.isNaN(sliceValue)) sb.append("All"); else sb.append(sliceValue); for (int t = 0; t < traitCount; t++) { sb.append(sep); sb.append(thisSlice.get(t).get(v)); } sb.append("\n"); } resultsStream.print(sb); } private static boolean onBackbone(Tree tree, NodeRef node, Set targetSet) { if (tree.isExternal(node)) return false; Set leafSet = Tree.Utils.getDescendantLeaves(tree, node); int size = leafSet.size(); leafSet.retainAll(targetSet); if (leafSet.size() > 0) { // if all leaves below are in target then check just above. if (leafSet.size() == size) { Set superLeafSet = Tree.Utils.getDescendantLeaves(tree, tree.getParent(node)); superLeafSet.removeAll(targetSet); // the branch is on ancestral path if the super tree has some non-targets in it return (superLeafSet.size() > 0); } else return true; } else return false; } private void analyzeTree(Tree treeTime, String[] traits, double[] slices, boolean impute, boolean trueNoise, Normalization normalize, boolean divideByBranchlength, BranchSet branchset, Set backboneTaxa) { double[][] precision = null; if (impute) { Object o = treeTime.getAttribute(PRECISION_STRING); double treeNormalization = 1; // None if (normalize == Normalization.LENGTH) { treeNormalization = Tree.Utils.getTreeLength(treeTime, treeTime.getRoot()); } else if (normalize == Normalization.HEIGHT) { treeNormalization = treeTime.getNodeHeight(treeTime.getRoot()); } if (o != null) { Object[] array = (Object[]) o; int dim = (int) Math.sqrt(1 + 8 * array.length) / 2; precision = new double[dim][dim]; int c = 0; for (int i = 0; i < dim; i++) { for (int j = i; j < dim; j++) { precision[j][i] = precision[i][j] = ((Double) array[c++]) * treeNormalization; } } } System.out.println(treeNormalization+"\t"+precision[0][0]+"\t"+precision[0][1]+"\t"+precision[1][0]+"\t"+precision[1][1]); } if (tipValues != null && tipValues.size() == 0) { // this is the first tree so initialize the tip value lists for (int i = 0; i < treeTime.getExternalNodeCount(); i++) { List<List<Trait>> thisTip = new ArrayList<List<Trait>>(traitCount); tipValues.add(thisTip); for (int j = 0; j < traitCount; j++) { List<Trait> thisTipTrait = new ArrayList<Trait>(); thisTip.add(thisTipTrait); } tipNames.add(treeTime.getNodeTaxon(treeTime.getExternalNode(i)).getId()); } } // employed to get dispersal rates across the whole tree // double treeNativeDistance = 0; // double treeKilometerGreatCircleDistance = 0; double[] treeSliceTime = new double[sliceCount]; double[] treeSliceDistance = new double[sliceCount]; double[] treeSliceMaxDistance = new double[sliceCount]; double[] treeTimeFromRoot = new double[sliceCount]; double[] maxDistanceFromRoot = new double[sliceCount]; //this one will used for weighted average (WA) //double[] treeSliceDiffusionCoefficientWA = new double[sliceCount]; //this one is used for simple average double[] treeSliceDiffusionCoefficientA = new double[sliceCount]; // this is for the variance double[] treeSliceDiffusionCoefficientV = new double[sliceCount]; double[][] treeSliceDiffusionCoefficients = new double[sliceCount][treeTime.getNodeCount() - 1]; double[] treeSliceBranchCount = new double[sliceCount]; treeLengths.add(Tree.Utils.getTreeLength(treeTime, treeTime.getRoot())); for (int x = 0; x < treeTime.getNodeCount(); x++) { NodeRef node = treeTime.getNode(x); if (!(treeTime.isRoot(node))) { double nodeHeight = treeTime.getNodeHeight(node); double parentHeight = treeTime.getNodeHeight(treeTime.getParent(node)); double oneOverBranchLength = 1.0; if (divideByBranchlength) { oneOverBranchLength = 1.0 / treeTime.getBranchLength(node); } boolean proceed = false; if (branchset == BranchSet.ALL) { proceed = true; } else if (branchset == BranchSet.EXT && treeTime.isExternal(node)) { proceed = true; } else if (branchset == BranchSet.INT && !treeTime.isExternal(node)) { proceed = true; } else if (branchset == BranchSet.BACKBONE && onBackbone(treeTime, node, backboneTaxa)) { proceed = true; } // employed to get dispersal rates across the whole tree // if (containsLocation){ // // Trait nodeLocationTrait = new Trait (treeTime.getNodeAttribute(node, LOCATIONTRAIT)); // Trait parentNodeLocationTrait = new Trait (treeTime.getNodeAttribute(treeTime.getParent(node), LOCATIONTRAIT)); // treeNativeDistance += getNativeDistance(nodeLocationTrait.getValue(),parentNodeLocationTrait.getValue()); // treeKilometerGreatCircleDistance += getKilometerGreatCircleDistance(nodeLocationTrait.getValue(),parentNodeLocationTrait.getValue()); // // } for (int i = 0; i < sliceCount; i++) { //System.out.println(slices[i]); if (sdr) { if (!doSlices || (slices[i] < nodeHeight) ) { if (proceed) { treeSliceTime[i] += (parentHeight - nodeHeight); //TreeModel model = new TreeModel(treeTime, true); //treeSliceDistance[i] += getKilometerGreatCircleDistance(model.getMultivariateNodeTrait(node, LOCATIONTRAIT),model.getMultivariateNodeTrait(model.getParent(node), LOCATIONTRAIT)); Trait nodeLocationTrait = new Trait(treeTime.getNodeAttribute(node, traits[0])); Trait parentNodeLocationTrait = new Trait(treeTime.getNodeAttribute(treeTime.getParent(node), traits[0])); treeSliceDistance[i] += getGeographicalDistance(nodeLocationTrait.getValue(), parentNodeLocationTrait.getValue()); //treeSliceDiffusionCoefficientWA[i] += (Math.pow((getGeographicalDistance(nodeLocationTrait.getValue(),parentNodeLocationTrait.getValue())),2.0)/(4.0*(parentHeight-nodeHeight)))*(parentHeight-nodeHeight); double diffusionCoefficient = (Math.pow((getGeographicalDistance(nodeLocationTrait.getValue(), parentNodeLocationTrait.getValue())), 2.0) / (4.0 * (parentHeight - nodeHeight))); treeSliceDiffusionCoefficientA[i] += diffusionCoefficient; treeSliceDiffusionCoefficients[i][x] = diffusionCoefficient; treeSliceBranchCount[i]++; } } } double height = Double.MAX_VALUE; if (i < sliceCount - 1) { height = slices[i + 1]; } if (!doSlices || ((sliceMode == SliceMode.BRANCHES) && (slices[i] >= nodeHeight && slices[i] < parentHeight)) || ((sliceMode == SliceMode.NODES) && (slices[i] < nodeHeight && height >= nodeHeight)) ) { if (proceed) { List<List<Trait>> thisSlice = values.get(i); for (int j = 0; j < traitCount; j++) { List<Trait> thisTraitSlice = thisSlice.get(j); Object tmpTrait = treeTime.getNodeAttribute(node, traits[j]); if (tmpTrait == null) { System.err.println("Trait '" + traits[j] + "' not found on branch."); System.exit(-1); } Trait trait = new Trait(tmpTrait); //System.out.println("trees "+treesAnalyzed+"\tslice "+slices[i]+"\t"+trait.toString()); if (divideByBranchlength) { trait.multiplyBy(oneOverBranchLength); } if (impute && (sliceMode == SliceMode.BRANCHES)) { Double rateAttribute = (Double) treeTime.getNodeAttribute(node, RATE_STRING); double rate = 1.0; if (rateAttribute != null) { rate = rateAttribute; if (outputRateWarning) { progressStream.println("Warning: using a rate attribute during imputation!"); outputRateWarning = false; } } if (trueNoise && precision == null) { progressStream.println("Error: not precision available for imputation with correct noise!"); System.exit(-1); } // if (slices[i] > nodeHeight) { trait = imputeValue(trait, new Trait(treeTime.getNodeAttribute(treeTime.getParent(node), traits[j])), slices[i], nodeHeight, parentHeight, precision, rate, trueNoise); // System.out.println(slices[i]+"\t"+nodeHeight+"\t"+parentHeight+"\t"+precision[0][0]+"\t"+precision[0][1]+"\t"+precision[1][0]+"\t"+precision[1][1]+"\t"+rate+"\t"+trait); //// // } // QUESTION to PL: MAS does not see how slices[i] is ever less than nodeHeight // } else if (impute && (sliceMode == SliceMode.NODES)) { // progressStream.println("no imputation for slice mode = nodes"); } thisTraitSlice.add(trait); //System.out.println("trees "+treesAnalyzed+"\tslice "+slices[i]+"\t"+trait.toString()); //if trait is location if (sdr) { treeSliceTime[i] += (parentHeight - slices[i]); Trait parentTrait = new Trait(treeTime.getNodeAttribute(treeTime.getParent(node), traits[j])); treeSliceDistance[i] += getGeographicalDistance(trait.getValue(), parentTrait.getValue()); double diffusionCoefficient = (Math.pow((getGeographicalDistance(trait.getValue(), parentTrait.getValue())), 2.0) / (4.0 * (parentHeight - slices[i]))); treeSliceDiffusionCoefficientA[i] += diffusionCoefficient; treeSliceDiffusionCoefficients[i][x] = diffusionCoefficient; treeSliceBranchCount[i]++; double tempDistanceFromRoot = getDistanceFromRoot(treeTime, traits[j], trait.getValue()); if (maxDistanceFromRoot[i] < tempDistanceFromRoot) { maxDistanceFromRoot[i] = tempDistanceFromRoot; treeSliceMaxDistance[i] = getPathDistance(treeTime, node, traits[j], trait.getValue()); //putting this below here ensures that treeTimeFromRoot is never < 0 treeTimeFromRoot[i] = treeTime.getNodeHeight(treeTime.getRoot()) - slices[i]; } } } } } } if (tipValues != null && treeTime.isExternal(node)) { List<List<Trait>> thisTip = tipValues.get(x); for (int j = 0; j < traitCount; j++) { Object tmpTrait = treeTime.getNodeAttribute(node, traits[j]); if (tmpTrait == null) { System.err.println("Trait '" + traits[j] + "' not found for tip."); System.exit(-1); } thisTip.get(j).add(new Trait(tmpTrait, treeTime.getNodeHeight(node))); } } } else { if (sliceMode == SliceMode.NODES) { double nodeHeight = treeTime.getNodeHeight(node); for (int i = 0; i < sliceCount; i++) { double height = Double.MAX_VALUE; if (i < sliceCount - 1) { height = slices[i + 1]; } if ((slices[i] < nodeHeight && height >= nodeHeight)){ List<List<Trait>> thisSlice = values.get(i); for (int j = 0; j < traitCount; j++) { List<Trait> thisTraitSlice = thisSlice.get(j); Object tmpTrait = treeTime.getNodeAttribute(node, traits[j]); if (tmpTrait == null) { System.err.println("Trait '" + traits[j] + "' not found on node."); System.exit(-1); } Trait trait = new Trait(tmpTrait); thisTraitSlice.add(trait); } } } } if (rootValues != null) { for (int j = 0; j < traitCount; j++) { List<Trait> thisRootTrait = rootValues.get(j); Object tmpTrait = treeTime.getNodeAttribute(node, traits[j]); if (tmpTrait == null) { System.err.println("Trait '" + traits[j] + "' not found on root node."); System.exit(-1); } Trait trait = new Trait(tmpTrait, treeTime.getNodeHeight(node)); thisRootTrait.add(trait); } } } } //System.out.println(Tree.Utils.getTreeLength(treeTime, treeTime.getRoot())+"\t"+test); if (sdr) { sliceTreeDistanceArrays.add(treeSliceDistance); sliceTreeTimeArrays.add(treeSliceTime); sliceTreeMaxDistanceArrays.add(treeSliceMaxDistance); sliceTreeTimeFromRootArrays.add(treeTimeFromRoot); for (int i = 0; i < treeSliceDiffusionCoefficientA.length; i++) { //treeSliceDiffusionCoefficientWA[i] = treeSliceDiffusionCoefficientWA[i]/treeSliceTime[i]; treeSliceDiffusionCoefficientA[i] = treeSliceDiffusionCoefficientA[i] / treeSliceBranchCount[i]; for (int j = 0; j < treeSliceDiffusionCoefficients[0].length; j++) { treeSliceDiffusionCoefficientV[i] += Math.pow((treeSliceDiffusionCoefficients[i][j] - treeSliceDiffusionCoefficientA[i]),2); } treeSliceDiffusionCoefficientV[i] = treeSliceDiffusionCoefficientV[i] / treeSliceBranchCount[i]; //System.out.println(treeSliceTime[i]+"\t"+treeLengths.get(i)); } sliceTreeDiffusionCoefficientArrays.add(treeSliceDiffusionCoefficientA); sliceTreeDiffusionCoefficientVarianceArrays.add(treeSliceDiffusionCoefficientV); } // employed to get dispersal rates across the whole tree // if (containsLocation) { // double treelength = Tree.Utils.getTreeLength(treeTime, treeTime.getRoot()); // double dispersalNativeRate = treeNativeDistance/treelength; // double dispersalKilometerRate = treeKilometerGreatCircleDistance/treelength; //System.out.println(dispersalNativeRate+"\t"+dispersalKilometerRate); // dispersalrates.add(dispersalNativeRate+"\t"+dispersalKilometerRate); // } treesAnalyzed++; } // employed to get dispersal rates across the whole tree // private static double getNativeDistance(double[] location1, double[] location2) { // return Math.sqrt(Math.pow((location2[0]-location1[0]),2.0)+Math.pow((location2[1]-location1[1]),2.0)); // } // private static double getGeographicalDistance(double[] location1, double[] location2) { if (location1.length == 1) { // assume we only have latitude so put them on the prime meridian return getKilometerGreatCircleDistance(new double[] { location1[0], 0.0}, new double[] { location2[0], 0.0}); } else if (location1.length == 2) { return getKilometerGreatCircleDistance(location1, location2); } throw new RuntimeException("Distances can only be calculated for longitude and latitude (or just latitude)"); } private static double getKilometerGreatCircleDistance(double[] location1, double[] location2) { SphericalPolarCoordinates coord1 = new SphericalPolarCoordinates(location1[0], location1[1]); SphericalPolarCoordinates coord2 = new SphericalPolarCoordinates(location2[0], location2[1]); return (coord1.distance(coord2)); } private double getPathDistance(Tree tree, NodeRef node, String locationTrait, double[] sliceTrait) { double pathDistance = 0; NodeRef parentNode = tree.getParent(node); Trait parentTrait = new Trait(tree.getNodeAttribute(parentNode, locationTrait)); pathDistance += getGeographicalDistance(sliceTrait, parentTrait.getValue()); while (parentNode != tree.getRoot()) { node = tree.getParent(node); parentNode = tree.getParent(parentNode); Trait nodeTrait = new Trait(tree.getNodeAttribute(node, locationTrait)); parentTrait = new Trait(tree.getNodeAttribute(parentNode, locationTrait)); pathDistance += getGeographicalDistance(nodeTrait.getValue(), parentTrait.getValue()); } return pathDistance; } private double getDistanceFromRoot(Tree tree, String locationTrait, double[] sliceTrait) { NodeRef rootNode = tree.getRoot(); Trait rootTrait = new Trait(tree.getNodeAttribute(rootNode, locationTrait)); return getGeographicalDistance(sliceTrait, rootTrait.getValue()); } private int traitCount; private int sliceCount; private String[] traits; private double[] sliceHeights; private boolean sliceProgressReport; private boolean checkSliceContours; private boolean doSlices; private int treesRead = 0; private int treesAnalyzed = 0; private double mostRecentSamplingDate; private ContourMode contourMode; private SliceMode sliceMode; private boolean ancient = false; private boolean useStyles = true; // employed to get dispersal rates across the whole tree // private static boolean containsLocation = false; // private static ArrayList dispersalrates = new ArrayList(); // private void run(List<Tree> trees, String[] traits, double[] slices, boolean impute, boolean trueNoise) { // // for (Tree treeTime : trees) { // analyzeTree(treeTime, traits, slices, impute, trueNoise); // } // } private ArrayList sliceTreeDistanceArrays = new ArrayList(); private ArrayList sliceTreeTimeArrays = new ArrayList(); private ArrayList sliceTreeMaxDistanceArrays = new ArrayList(); private ArrayList sliceTreeTimeFromRootArrays = new ArrayList(); private ArrayList sliceTreeDiffusionCoefficientArrays = new ArrayList(); private ArrayList sliceTreeDiffusionCoefficientVarianceArrays = new ArrayList(); private boolean sdr; private ArrayList treeLengths = new ArrayList(); private boolean outputRateWarning = true; private Trait imputeValue(Trait nodeTrait, Trait parentTrait, double time, double nodeHeight, double parentHeight, double[][] precision, double rate, boolean trueNoise) { if (!nodeTrait.isNumber()) { System.err.println("Can only impute numbers!"); System.exit(-1); } int dim = nodeTrait.getDim(); double[] nodeValue = nodeTrait.getValue(); double[] parentValue = parentTrait.getValue(); final double scaledTimeChild = (time - nodeHeight) * rate; final double scaledTimeParent = (parentHeight - time) * rate; final double scaledWeightTotal = 1.0 / scaledTimeChild + 1.0 / scaledTimeParent; if (scaledTimeChild == 0) return nodeTrait; if (scaledTimeParent == 0) return parentTrait; // Find mean value, weighted average double[] mean = new double[dim]; double[][] scaledPrecision = new double[dim][dim]; for (int i = 0; i < dim; i++) { mean[i] = (nodeValue[i] / scaledTimeChild + parentValue[i] / scaledTimeParent) / scaledWeightTotal; if (trueNoise) { for (int j = i; j < dim; j++) scaledPrecision[j][i] = scaledPrecision[i][j] = precision[i][j] * scaledWeightTotal; } } // System.out.print(time+"\t"+nodeHeight+"\t"+parentHeight+"\t"+scaledTimeChild+"\t"+scaledTimeParent+"\t"+scaledWeightTotal+"\t"+mean[0]+"\t"+mean[1]+"\t"+scaledPrecision[0][0]+"\t"+scaledPrecision[0][1]+"\t"+scaledPrecision[1][0]+"\t"+scaledPrecision[1][1]); if (trueNoise) { mean = MultivariateNormalDistribution.nextMultivariateNormalPrecision(mean, scaledPrecision); } // System.out.println("\t"+mean[0]+"\t"+mean[1]+"\r"); Object[] result = new Object[dim]; for (int i = 0; i < dim; i++) result[i] = mean[i]; return new Trait(result); } // Messages to stderr, output to stdout private static PrintStream progressStream = System.err; private PrintStream resultsStream; private final static Version version = new BeastVersion(); private static final String commandName = "timeslicer"; public static void printUsage(Arguments arguments) { arguments.printUsage(commandName, "<input-file-name> [<output-file-name>]"); progressStream.println(); progressStream.println(" Example: " + commandName + " test.trees out.kml"); progressStream.println(); } public static void centreLine(String line, int pageWidth) { int n = pageWidth - line.length(); int n1 = n / 2; for (int i = 0; i < n1; i++) { progressStream.print(" "); } progressStream.println(line); } public static void printTitle() { progressStream.println(); centreLine("TimeSlicer " + version.getVersionString() + ", " + version.getDateString(), 60); centreLine("MCMC Output analysis", 60); centreLine("by", 60); centreLine("Marc A. Suchard, Philippe Lemey,", 60); centreLine("Alexei J. Drummond and Andrew Rambaut", 60); progressStream.println(); centreLine("Department of Biomathematics", 60); centreLine("University of California, Los Angeles", 60); centreLine("msuchard@ucla.edu", 60); progressStream.println(); centreLine("Rega Institute for Medical Research", 60); centreLine("Katholieke Universiteit Leuven", 60); centreLine("philippe.lemey@gmail.com", 60); progressStream.println(); centreLine("Department of Computer Science", 60); centreLine("University of Auckland", 60); centreLine("alexei@cs.auckland.ac.nz", 60); progressStream.println(); centreLine("Institute of Evolutionary Biology", 60); centreLine("University of Edinburgh", 60); centreLine("a.rambaut@ed.ac.uk", 60); progressStream.println(); progressStream.println(); } public static double[] parseVariableLengthDoubleArray(String inString) throws Arguments.ArgumentException { List<Double> returnList = new ArrayList<Double>(); StringTokenizer st = new StringTokenizer(inString, ","); while (st.hasMoreTokens()) { try { returnList.add(Double.parseDouble(st.nextToken())); } catch (NumberFormatException e) { throw new Arguments.ArgumentException(); } } if (returnList.size() > 0) { double[] doubleArray = new double[returnList.size()]; for (int i = 0; i < doubleArray.length; i++) doubleArray[i] = returnList.get(i); return doubleArray; } return null; } private static String[] parseVariableLengthStringArray(String inString) { List<String> returnList = new ArrayList<String>(); StringTokenizer st = new StringTokenizer(inString, ","); while (st.hasMoreTokens()) { returnList.add(st.nextToken()); } if (returnList.size() > 0) { String[] stringArray = new String[returnList.size()]; stringArray = returnList.toArray(stringArray); return stringArray; } return null; } private static double[] parseFileWithArray(String file) { List<Double> returnList = new ArrayList<Double>(); try { BufferedReader readerTimes = new BufferedReader(new FileReader(file)); String line = readerTimes.readLine(); while (line != null && !line.equals("")) { returnList.add(Double.valueOf(line)); line = readerTimes.readLine(); } } catch (IOException e) { System.err.println("Error reading " + file); System.exit(1); } if (returnList.size() > 0) { double[] doubleArray = new double[returnList.size()]; for (int i = 0; i < doubleArray.length; i++) doubleArray[i] = returnList.get(i); //System.out.println(doubleArray.length); return doubleArray; } return null; } 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]; } } return blue + green + red; } private static double[] getHPDInterval(double proportion, double[] array, int[] indices) { double returnArray[] = new double[2]; double minRange = Double.MAX_VALUE; int hpdIndex = 0; int diff = (int) Math.round(proportion * (double) array.length); for (int i = 0; i <= (array.length - diff); i++) { double minValue = array[indices[i]]; double maxValue = array[indices[i + diff - 1]]; double range = Math.abs(maxValue - minValue); if (range < minRange) { minRange = range; hpdIndex = i; } } returnArray[0] = array[indices[hpdIndex]]; returnArray[1] = array[indices[hpdIndex + diff - 1]]; return returnArray; } private static void print2DArray(double[][] array, String name) { try { PrintWriter outFile = new PrintWriter(new FileWriter(name), true); for (double[] anArray : array) { for (int j = 0; j < array[0].length; j++) { outFile.print(anArray[j] + "\t"); } outFile.println(""); } outFile.close(); } catch (IOException io) { System.err.print("Error writing to file: " + name); } } private static void print2DTransposedArray(double[][] array, String name) { try { PrintWriter outFile = new PrintWriter(new FileWriter(name), true); for (int i = 0; i < array[0].length; i++) { for (double[] anArray : array) { outFile.print(anArray[i] + "\t"); } outFile.println(""); } outFile.close(); } catch (IOException io) { System.err.print("Error writing to file: " + name); } } private static double[] meanColNoNaN(double[][] x) { double[] returnArray = new double[x[0].length]; for (int i = 0; i < x[0].length; i++) { double m = 0.0; int lenNoZero = 0; for (int j = 0; j < x.length; j++) { if (!(((Double) x[j][i]).isNaN())) { m += x[j][i]; lenNoZero += 1; } } returnArray[i] = m / (double) lenNoZero; } return returnArray; } private static double[][] getArrayHPDintervals(double[][] array) { double[][] returnArray = new double[array[0].length][2]; for (int col = 0; col < array[0].length; col++) { int counter = 0; for (int row = 0; row < array.length; row++) { if (!(((Double) array[row][col]).isNaN())) { counter += 1; } } if (counter > 0) { double[] columnNoNaNArray = new double[counter]; int index = 0; for (int row = 0; row < array.length; row++) { if (!(((Double) array[row][col]).isNaN())) { columnNoNaNArray[index] = array[row][col]; index += 1; } } int[] indices = new int[counter]; HeapSort.sort(columnNoNaNArray, indices); double hpdBinInterval[] = getHPDInterval(0.95, columnNoNaNArray, indices); returnArray[col][0] = hpdBinInterval[0]; returnArray[col][1] = hpdBinInterval[1]; } else { returnArray[col][0] = Double.NaN; returnArray[col][1] = Double.NaN; } } return returnArray; } private static Set getTargetSet(String x) { Set targetSet = null; targetSet = new HashSet(); try { BufferedReader reader = new BufferedReader(new FileReader(x)); try { String line = reader.readLine().trim(); while (line != null && !line.equals("")) { targetSet.add(line); line = reader.readLine(); if (line != null) line = line.trim(); } } catch (IOException io) { System.err.println("Error reading " + x); } } catch (FileNotFoundException a) { System.err.println("Error finding " + x); } return targetSet; } public static void main(String[] args) throws IOException { String inputFileName = null; String outputFileName = null; String[] traitNames = null; double[] sliceHeights = null; OutputFormat outputFormat = OutputFormat.KML; boolean impute = false; boolean trueNoise = true; boolean summaryOnly = true; ContourMode contourMode = ContourMode.SNYDER; Normalization normalize = Normalization.LENGTH; int burnin = -1; int skipEvery = 1; double mrsd = 0; boolean summarizeRoot = false; boolean summarizeTips = false; boolean contours = true; boolean points = false; double[] hpdValues = { 0.80 }; String outputFileSDR = null; boolean getSDR = false; String progress = null; boolean branchNormalization = false; BranchSet set = BranchSet.ALL; Set backboneTaxa = null; SliceMode sliceMode = SliceMode.BRANCHES; // if (args.length == 0) { // // TODO Make flash GUI // } printTitle(); Arguments arguments = new Arguments( new Arguments.Option[]{ new Arguments.IntegerOption(BURNIN, "the number of states to be considered as 'burn-in' [default = 0]"), new Arguments.IntegerOption(SKIP, "skip every i'th tree [default = 0]"), new Arguments.StringOption(TRAIT, "trait_name", "specifies an attribute-list to use to create a density map [default = location.rate]"), new Arguments.StringOption(SLICE_TIMES, "slice_times", "specifies a slice time-list [default=none]"), new Arguments.StringOption(SLICE_HEIGHTS, "slice_heights", "specifies a slice height-list [default=none]"), new Arguments.StringOption(SLICE_FILE_HEIGHTS, "heights_file", "specifies a file with a slice heights-list, is overwritten by command-line specification of slice heights [default=none]"), new Arguments.StringOption(SLICE_FILE_TIMES, "Times_file", "specifies a file with a slice Times-list, is overwritten by command-line specification of slice times [default=none]"), new Arguments.IntegerOption(SLICE_COUNT, "the number of time slices to use [default=0]"), new Arguments.StringOption(SLICE_MODE, "Slice_mode", "specifies how to perform the slicing [default=branches]"), new Arguments.StringOption(ROOT, falseTrue, false, "include a summary for the root [default=off]"), new Arguments.StringOption(TIPS, falseTrue, false, "include a summary for the tips [default=off]"), new Arguments.StringOption(CONTOURS, falseTrue, true, "include contours in summary [default=true]"), new Arguments.StringOption(POINTS, falseTrue, false, "include all points for the summary [default=off]"), new Arguments.RealOption(START_TIME, "the time of the earliest slice [default=0]"), new Arguments.RealOption(MRSD, "specifies the most recent sampling data in fractional years to rescale time [default=0]"), new Arguments.Option(HELP, "option to print this message"), new Arguments.StringOption(NOISE, falseTrue, false, "add true noise [default = true])"), new Arguments.StringOption(IMPUTE, falseTrue, false, "impute trait at time-slice [default = false]"), new Arguments.StringOption(SUMMARY, falseTrue, false, "compute summary statistics [default = true]"), new Arguments.StringOption(FORMAT, enumNamesToStringArray(OutputFormat.values()), false, "summary output format [default = KML]"), new Arguments.StringOption(HPD, "hpd", "mass (1 - 99%) to include in HPD regions (or list) [default = 80]"), new Arguments.StringOption(CONTOUR_MODE, enumNamesToStringArray(ContourMode.values()), false, "contouring model [default = snyder]"), new Arguments.StringOption(NORMALIZATION, enumNamesToStringArray(Normalization.values()), false, "tree normalization [default = length"), new Arguments.StringOption(SDR, "sliceDispersalRate", "specifies output file name for dispersal rates for each slice (from previous sliceTime[or root of the trees] up to current sliceTime"), new Arguments.StringOption(PROGRESS, "progress report", "reports slice progress and checks the bivariate contour HPD regions by calculating what fraction of points the polygons for a given slice contain [default = false]"), new Arguments.StringOption(BRANCH_NORMALIZE, falseTrue, false, "devide a branch trait by branch length (can be useful for 'rewards' [default = false]"), new Arguments.StringOption(BRANCHSET, TimeSlicer.enumNamesToStringArray(BranchSet.values()), false, "branch set [default = all]"), new Arguments.StringOption(BACKBONETAXA, "Backbone taxa file", "specifies a file with taxa that define the backbone"), }); try { arguments.parseArguments(args); } catch (Arguments.ArgumentException ae) { progressStream.println(ae); printUsage(arguments); System.exit(1); } if (arguments.hasOption(HELP)) { printUsage(arguments); System.exit(0); } try { // Make sense of arguments String sliceHeightsFileString = arguments.getStringOption(SLICE_FILE_HEIGHTS); if (sliceHeightsFileString != null) { sliceHeights = parseFileWithArray(sliceHeightsFileString); } if (arguments.hasOption(MRSD)) { mrsd = arguments.getRealOption(MRSD); } String sliceTimesFileString = arguments.getStringOption(SLICE_FILE_TIMES); if (sliceTimesFileString != null) { //System.out.println(sliceTimesFileString); double[] sliceTimes = parseFileWithArray(sliceTimesFileString); sliceHeights = new double[sliceTimes.length]; for (int i = 0; i < sliceTimes.length; i++) { if (mrsd == 0) { sliceHeights[i] = sliceTimes[i]; } else { //System.out.println((mrsd - sliceTimes[i])); sliceHeights[i] = mrsd - sliceTimes[i]; } } } String sliceModeString = arguments.getStringOption(SLICE_MODE); if (sliceModeString != null) { try { sliceMode = SliceMode.valueOf(sliceModeString.toUpperCase()); } catch (IllegalArgumentException iae) { System.err.println("Unrecognized slice mode: " + sliceModeString); } } if (arguments.hasOption(BURNIN)) { burnin = arguments.getIntegerOption(BURNIN); System.err.println("Ignoring a burnin of " + burnin + " trees."); } if (arguments.hasOption(SKIP)) { skipEvery = arguments.getIntegerOption(SKIP); System.err.println("Skipping every " + skipEvery + " trees."); } if (skipEvery < 1) { skipEvery = 1; } String hpdString = arguments.getStringOption(HPD); if (hpdString != null) { hpdValues = parseVariableLengthDoubleArray(hpdString); if (hpdValues.length > 0) { for (int i = 0; i < hpdValues.length; i++) { if (hpdValues[i] < 1 || hpdValues[i] > 99) { progressStream.println("HPD Region mass falls outside of 1 - 99% range."); System.exit(-1); } hpdValues[i] = hpdValues[i] / 100.0; } } else { hpdValues = new double[] { 80.0 }; } } String traitString = arguments.getStringOption(TRAIT); if (traitString != null) { traitNames = parseVariableLengthStringArray(traitString); //employed to get dispersal rates across the whole tree // for (int y = 0; y < traitNames.length; y++) { // if (traitNames[y].equals(LOCATIONTRAIT)){ // containsLocation = true; // } // } } if (traitNames == null) { traitNames = new String[1]; traitNames[0] = "location.rate"; } String sliceTimeString = arguments.getStringOption(SLICE_TIMES); if (sliceTimeString != null) { double[] sliceTimes = parseVariableLengthDoubleArray(sliceTimeString); sliceHeights = new double[sliceTimes.length]; for (int i = 0; i < sliceTimes.length; i++) { if (mrsd == 0) { sliceHeights[i] = sliceTimes[i]; } else { sliceHeights[i] = mrsd - sliceTimes[i]; } } } String sliceHeightString = arguments.getStringOption(SLICE_HEIGHTS); if (sliceHeightString != null) { if (sliceTimeString != null) { progressStream.println("Either sliceTimes, sliceHeights, timesFile or sliceCount" + "nt."); System.exit(-1); } sliceHeights = parseVariableLengthDoubleArray(sliceHeightString); } if (arguments.hasOption(SLICE_COUNT)) { int sliceCount = arguments.getIntegerOption(SLICE_COUNT); double startTime = arguments.getRealOption(START_TIME); double delta; if (mrsd != 0) { delta = (mrsd - startTime) / (sliceCount - 1); } else { delta = startTime / (sliceCount - 1); } sliceHeights = new double[sliceCount]; // double height = mrsd - startTime; // for (int i = 0; i < sliceCount; i++) { // sliceHeights[i] = height; // height -= delta; // } double height = 0; for (int i = 0; i < sliceCount; i++) { sliceHeights[i] = height; height += delta; } } String optionString = arguments.getStringOption(ROOT); if (optionString != null && optionString.compareToIgnoreCase("true") == 0) { summarizeRoot = true; } optionString = arguments.getStringOption(TIPS); if (optionString != null && optionString.compareToIgnoreCase("true") == 0) { summarizeTips = true; } optionString = arguments.getStringOption(CONTOURS); if (optionString != null && optionString.compareToIgnoreCase("false") == 0) { contours = false; } optionString = arguments.getStringOption(POINTS); if (optionString != null && optionString.compareToIgnoreCase("true") == 0) { points = true; } //sorting sliceHeights if (sliceHeights != null) { Arrays.sort(sliceHeights); } String imputeString = arguments.getStringOption(IMPUTE); if (imputeString != null && imputeString.compareToIgnoreCase("true") == 0) impute = true; String branchNormString = arguments.getStringOption(BRANCH_NORMALIZE); if (branchNormString != null && branchNormString.compareToIgnoreCase("true") == 0) branchNormalization = true; String noiseString = arguments.getStringOption(NOISE); if (noiseString != null && noiseString.compareToIgnoreCase("false") == 0) trueNoise = false; String summaryString = arguments.getStringOption(SUMMARY); if (summaryString != null && summaryString.compareToIgnoreCase("true") == 0) summaryOnly = true; String modeString = arguments.getStringOption(CONTOUR_MODE); if (modeString != null) { contourMode = ContourMode.valueOf(modeString.toUpperCase()); if (contourMode == ContourMode.R && !ContourWithR.processWithR) contourMode = ContourMode.SNYDER; } String normalizeString = arguments.getStringOption(NORMALIZATION); if (normalizeString != null) { normalize = Normalization.valueOf(normalizeString.toUpperCase()); } String summaryFormat = arguments.getStringOption(FORMAT); if (summaryFormat != null) { outputFormat = OutputFormat.valueOf(summaryFormat.toUpperCase()); } String sdrString = arguments.getStringOption(SDR); if (sdrString != null) { outputFileSDR = sdrString; getSDR = true; } String progressString = arguments.getStringOption(PROGRESS); if (progressString != null) { progress = progressString; } String branch = arguments.getStringOption(BRANCHSET); if (branch != null) { set = BranchSet.valueOf(branch.toUpperCase()); System.out.println("Using the branch set: " + set.name()); } if (set == set.BACKBONE) { if (arguments.hasOption(BACKBONETAXA)) { backboneTaxa = getTargetSet(arguments.getStringOption(BACKBONETAXA)); } else { System.err.println("you want to summarize the backbone, but have no taxa to define it??"); } } } catch (Arguments.ArgumentException e) { progressStream.println(e); printUsage(arguments); System.exit(-1); } final String[] args2 = arguments.getLeftoverArguments(); switch (args2.length) { case 0: printUsage(arguments); System.exit(1); case 2: outputFileName = args2[1]; // fall to case 1: inputFileName = args2[0]; break; default: { System.err.println("Unknown option: " + args2[2]); System.err.println(); printUsage(arguments); System.exit(1); } } TimeSlicer timeSlicer = new TimeSlicer(inputFileName, burnin, skipEvery, traitNames, sliceHeights, impute, trueNoise, mrsd, contourMode, sliceMode,summarizeRoot, summarizeTips, normalize, getSDR, progress, branchNormalization, set, backboneTaxa); timeSlicer.output(outputFileName, summaryOnly, summarizeRoot, summarizeTips, contours, points, outputFormat, hpdValues, outputFileSDR); System.exit(0); } }