package templates; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.text.ParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import jebl.evolution.graphs.Node; import jebl.evolution.io.ImportException; import jebl.evolution.io.NexusImporter; import jebl.evolution.io.TreeImporter; import jebl.evolution.trees.RootedTree; import processing.core.PApplet; import readers.SliceHeightsReader; import structure.Coordinates; import structure.TimeLine; import utils.ThreadLocalSpreadDate; import utils.Utils; import contouring.ContourMaker; import contouring.ContourPath; import contouring.ContourWithSynder; @SuppressWarnings("serial") public class TimeSlicerToProcessing extends PApplet { private int analysisType; public final static int FIRST_ANALYSIS = 1; public final static int SECOND_ANALYSIS = 2; // how many millisecond one day holds private final int DayInMillis = 86400000; // how many days one year holds private static final int DaysInYear = 365; // Concurrency stuff private ConcurrentMap<Double, List<Coordinates>> slicesMap; private RootedTree currentTree; private RootedTree tree; private int numberOfIntervals; private double[] sliceHeights; private double minPolygonRedMapping; private double minPolygonGreenMapping; private double minPolygonBlueMapping; private double minPolygonOpacityMapping; private double maxPolygonRedMapping; private double maxPolygonGreenMapping; private double maxPolygonBlueMapping; private double maxPolygonOpacityMapping; private double minBranchRedMapping; private double minBranchGreenMapping; private double minBranchBlueMapping; private double minBranchOpacityMapping; private double maxBranchRedMapping; private double maxBranchGreenMapping; private double maxBranchBlueMapping; private double maxBranchOpacityMapping; private double branchWidth; private TreeImporter treesImporter; private TreeImporter treeImporter; private double treeRootHeight; private String precisionString; private String coordinatesName; private String longitudeName; private String latitudeName; private String rateString; private boolean useTrueNoise; private String mrsdString; private ThreadLocalSpreadDate mrsd; private double timescaler; private TimeLine timeLine; private double startTime; private double endTime; private double burnIn; private double hpd; private int gridSize; private MapBackground mapBackground; private float minX, maxX; private float minY, maxY; public TimeSlicerToProcessing() { }// END: constructor public void setAnalysisType(int analysisType) { this.analysisType = analysisType; } public void setCustomSliceHeightsPath(String path) { sliceHeights = new SliceHeightsReader(path).getSliceHeights(); } public void setTimescaler(double timescaler) { this.timescaler = timescaler; } public void setHPD(double hpd) { this.hpd = hpd; } public void setGridSize(int gridSize) { this.gridSize = gridSize; } public void setTreePath(String path) throws FileNotFoundException { treeImporter = new NexusImporter(new FileReader(path)); } public void setTreesPath(String path) throws FileNotFoundException { treesImporter = new NexusImporter(new FileReader(path)); } public void setMrsdString(String mrsd) { mrsdString = mrsd; } public void setCoordinatesName(String name) { coordinatesName = name; // this is for coordinate attribute names latitudeName = (coordinatesName + 1); longitudeName = (coordinatesName + 2); } public void setRateAttributeName(String name) { rateString = name; } public void setPrecisionAttributeName(String name) { precisionString = name; } public void setNumberOfIntervals(int number) { numberOfIntervals = number; } public void setBurnIn(double burnIn) { this.burnIn = burnIn; } public void setUseTrueNoise(boolean useTrueNoise) { this.useTrueNoise = useTrueNoise; } public void setMinPolygonRedMapping(double min) { minPolygonRedMapping = min; } public void setMinPolygonGreenMapping(double min) { minPolygonGreenMapping = min; } public void setMinPolygonBlueMapping(double min) { minPolygonBlueMapping = min; } public void setMinPolygonOpacityMapping(double min) { minPolygonOpacityMapping = min; } public void setMaxPolygonRedMapping(double max) { maxPolygonRedMapping = max; } public void setMaxPolygonGreenMapping(double max) { maxPolygonGreenMapping = max; } public void setMaxPolygonBlueMapping(double max) { maxPolygonBlueMapping = max; } public void setMaxPolygonOpacityMapping(double max) { maxPolygonOpacityMapping = max; } public void setMinBranchRedMapping(double min) { minBranchRedMapping = min; } public void setMinBranchGreenMapping(double min) { minBranchGreenMapping = min; } public void setMinBranchBlueMapping(double min) { minBranchBlueMapping = min; } public void setMinBranchOpacityMapping(double min) { minBranchOpacityMapping = min; } public void setMaxBranchRedMapping(double max) { maxBranchRedMapping = max; } public void setMaxBranchGreenMapping(double max) { maxBranchGreenMapping = max; } public void setMaxBranchBlueMapping(double max) { maxBranchBlueMapping = max; } public void setMaxBranchOpacityMapping(double max) { maxBranchOpacityMapping = max; } public void setBranchWidth(double width) { branchWidth = width; } public void setup() { minX = -180; maxX = 180; minY = -90; maxY = 90; mapBackground = new MapBackground(this); }// END:setup public void draw() { noLoop(); smooth(); mapBackground.drawMapBackground(); System.out.println("Drawing polygons..."); drawPolygons(); switch (analysisType) { case 1: System.out.println("Drawing branches..."); drawBranches(); break; case 2: break; } }// END:draw private void drawPolygons() throws OutOfMemoryError { System.out.println("Iterating through Map..."); Set<Double> hostKeys = slicesMap.keySet(); Iterator<Double> iterator = hostKeys.iterator(); int polygonsStyleId = 1; while (iterator.hasNext()) { System.out.println("Key " + polygonsStyleId + "..."); Double sliceTime = iterator.next(); drawPolygon(sliceTime); polygonsStyleId++; // slicesMap.remove(sliceTime); } }// END: drawPolygons private void drawPolygon(Double sliceTime) throws OutOfMemoryError { /** * Color and Opacity mapping * */ int red = (int) Utils.map(sliceTime, startTime, endTime, minPolygonRedMapping, maxPolygonRedMapping); int green = (int) Utils.map(sliceTime, startTime, endTime, minPolygonGreenMapping, maxPolygonGreenMapping); int blue = (int) Utils.map(sliceTime, startTime, endTime, minPolygonBlueMapping, maxPolygonBlueMapping); int alpha = (int) Utils.map(sliceTime, startTime, endTime, maxPolygonOpacityMapping, minPolygonOpacityMapping); stroke(red, green, blue, alpha); fill(red, green, blue, alpha); List<Coordinates> list = slicesMap.get(sliceTime); double[] x = new double[list.size()]; double[] y = new double[list.size()]; for (int i = 0; i < list.size(); i++) { x[i] = list.get(i).getLatitude(); y[i] = list.get(i).getLongitude(); } ContourMaker contourMaker = new ContourWithSynder(x, y, gridSize); ContourPath[] paths = contourMaker.getContourPaths(hpd); for (ContourPath path : paths) { double[] latitude = path.getAllX(); double[] longitude = path.getAllY(); List<Coordinates> coordinates = new ArrayList<Coordinates>(); for (int i = 0; i < latitude.length; i++) { coordinates .add(new Coordinates(longitude[i], latitude[i], 0.0)); } beginShape(); for (int row = 0; row < coordinates.size() - 1; row++) { double X = Utils.map(coordinates.get(row).getLongitude(), minX, maxX, 0, width); double Y = Utils.map(coordinates.get(row).getLatitude(), maxY, minY, 0, height); double XEND = Utils.map( coordinates.get(row + 1).getLongitude(), minX, maxX, 0, width); double YEND = Utils.map( (coordinates.get(row + 1).getLatitude()), maxY, minY, 0, height); vertex((float) X, (float) Y); vertex((float) XEND, (float) YEND); }// END: coordinates loop endShape(CLOSE); }// END: paths loop slicesMap.remove(sliceTime); }// END: drawPolygon() private void drawBranches() { double treeHeightMax = Utils.getTreeHeightMax(tree); strokeWeight((float) branchWidth); for (Node node : tree.getNodes()) { if (!tree.isRoot(node)) { Double longitude = (Double) node.getAttribute(longitudeName); Double latitude = (Double) node.getAttribute(latitudeName); Node parentNode = tree.getParent(node); Double parentLongitude = (Double) parentNode .getAttribute(longitudeName); Double parentLatitude = (Double) parentNode .getAttribute(latitudeName); // Equirectangular projection: double x0 = Utils.map(parentLongitude, minX, maxX, 0, width); double y0 = Utils.map(parentLatitude, maxY, minY, 0, height); double x1 = Utils.map(longitude, minX, maxX, 0, width); double y1 = Utils.map(latitude, maxY, minY, 0, height); /** * Color mapping * */ double nodeHeight = Utils.getNodeHeight(tree, node); int red = (int) Utils.map(nodeHeight, 0, treeHeightMax, minBranchRedMapping, maxBranchRedMapping); int green = (int) Utils.map(nodeHeight, 0, treeHeightMax, minBranchGreenMapping, maxBranchGreenMapping); int blue = (int) Utils.map(nodeHeight, 0, treeHeightMax, minBranchBlueMapping, maxBranchBlueMapping); int alpha = (int) Utils.map(nodeHeight, 0, treeHeightMax, maxBranchOpacityMapping, minBranchOpacityMapping); stroke(red, green, blue, alpha); line((float) x0, (float) y0, (float) x1, (float) y1); } }// END: nodes loop }// END: DrawBranches public void analyzeTrees() throws IOException, ImportException, ParseException { mrsd = new ThreadLocalSpreadDate(mrsdString); switch (analysisType) { case 1: tree = (RootedTree) treeImporter.importNextTree(); treeRootHeight = Utils.getNodeHeight(tree, tree.getRootNode()); sliceHeights = generateTreeSliceHeights(treeRootHeight, numberOfIntervals); timeLine = generateTreeTimeLine(tree); break; case 2: timeLine = generateCustomTimeLine(sliceHeights); break; } //sort them in ascending numerical order Arrays.sort(sliceHeights); System.out.println("Using as slice times: "); Utils.printArray(sliceHeights); System.out.println(); startTime = timeLine.getStartTime(); endTime = timeLine.getEndTime(); // This is for slice times slicesMap = new ConcurrentHashMap<Double, List<Coordinates>>(); // Executor for threads int NTHREDS = Runtime.getRuntime().availableProcessors(); ExecutorService executor = Executors.newFixedThreadPool(NTHREDS * 2); int treesAssumed = 10000; int treesRead = 0; System.out.println("Analyzing trees (bar assumes 10,000 trees)"); System.out.println("0 25 50 75 100"); System.out.println("|---------------------|---------------------|---------------------|---------------------|"); // System.out.println("0 25 50 75 100"); // System.out.println("|--------------|--------------|--------------|--------------|"); int stepSize = treesAssumed / 60; if (stepSize < 1) { stepSize = 1; } // This is for collecting coordinates into polygons slicesMap = new ConcurrentHashMap<Double, List<Coordinates>>(); int totalTrees = 0; while (treesImporter.hasTree()) { currentTree = (RootedTree) treesImporter.importNextTree(); if (totalTrees >= burnIn) { executor.submit(new AnalyzeTree(currentTree, // precisionString,// coordinatesName, // rateString, // sliceHeights, // timescaler,// mrsd, // slicesMap, // useTrueNoise// )); // new AnalyzeTree(currentTree, // precisionString, coordinatesName, rateString, // sliceHeights, timescaler, mrsd, slicesMap, // useTrueNoise).run(); treesRead += 1; }// END: if burn-in if (totalTrees > 0 && totalTrees % stepSize == 0) { System.out.print("*"); System.out.flush(); } totalTrees++; }// END: while has trees if ((totalTrees - burnIn) <= 0.0) { throw new RuntimeException("Burnt too many trees!"); } else { System.out.println("\nAnalyzed " + treesRead + " trees with burn-in of " + burnIn + " for the total of " + totalTrees + " trees"); } // Wait until all threads are finished executor.shutdown(); while (!executor.isTerminated()) { } }// END: AnalyzeTrees private TimeLine generateTreeTimeLine(RootedTree tree) { // This is a general time span for all of the trees double treeRootHeight = Utils.getNodeHeight(tree, tree.getRootNode()); double startTime = mrsd.getTime() - (treeRootHeight * DayInMillis * DaysInYear * timescaler); double endTime = mrsd.getTime(); TimeLine timeLine = new TimeLine(startTime, endTime, numberOfIntervals); return timeLine; }// END: generateTreeTimeLine private double[] generateTreeSliceHeights(double treeRootHeight, int numberOfIntervals) { double[] timeSlices = new double[numberOfIntervals]; for (int i = 0; i < numberOfIntervals; i++) { timeSlices[i] = treeRootHeight - (treeRootHeight / (double) numberOfIntervals) * ((double) i); } return timeSlices; }// END: generateTimeSlices private TimeLine generateCustomTimeLine(double[] timeSlices) { // This is a general time span for all of the trees int numberOfSlices = timeSlices.length; double firstSlice = timeSlices[0]; double startTime = mrsd.getTime() - (firstSlice * DayInMillis * DaysInYear * timescaler); double endTime = mrsd.getTime(); return new TimeLine(startTime, endTime, numberOfSlices); }// END: generateCustomTimeLine }// END: TimeScalerToProcessing class