package templates; import generator.KMLGenerator; import gui.InteractiveTableModel; import java.awt.Color; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; import java.text.ParseException; import java.util.ArrayList; import java.util.List; import java.util.Random; 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 structure.Coordinates; import structure.Layer; import structure.Line; import structure.Place; import structure.Polygon; import structure.Style; import structure.TimeLine; import utils.ThreadLocalSpreadDate; import utils.Utils; public class DiscreteTreeToKML { public long time; // how many millisecond one day holds private static final int DayInMillis = 86400000; // how many days one year holds private static final int DaysInYear = 365; // Earths radius in km private static final double EarthRadius = 6371; private RootedTree tree; private String stateAttName; private InteractiveTableModel table; private String mrsdString; private ThreadLocalSpreadDate mrsd; private int numberOfIntervals; private int timescaler; private double rootHeight; private List<Layer> layers; private double maxAltMapping; 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 polygonsRadiusMultiplier; private double branchWidth; private PrintWriter writer; private TreeImporter importer; private Random generator; public DiscreteTreeToKML() { generator = new Random(); }// END: DiscreteTreeToKML() public void setTimescaler(int timescaler) { this.timescaler = timescaler; } public void setStateAttName(String name) { stateAttName = name; } public void setMrsdString(String mrsd) { mrsdString = mrsd; } public void setNumberOfIntervals(int number) { numberOfIntervals = number; } public void setMaxAltitudeMapping(double max) { maxAltMapping = max; } public void setKmlWriterPath(String kmlpath) throws FileNotFoundException { writer = new PrintWriter(kmlpath); } public void setTreePath(String path) throws FileNotFoundException { importer = new NexusImporter(new FileReader(path)); } public void setTable(InteractiveTableModel tableModel) { table = tableModel; } public void setPolygonsRadiusMultiplier(double multiplier) { polygonsRadiusMultiplier = multiplier; } 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 GenerateKML() throws IOException, ImportException, ParseException, RuntimeException { // start timing time = -System.currentTimeMillis(); tree = (RootedTree) importer.importNextTree(); // this is for time calculations rootHeight = tree.getHeight(tree.getRootNode()); // This is a general time span for the tree mrsd = new ThreadLocalSpreadDate(mrsdString); TimeLine timeLine = new TimeLine(mrsd.getTime() - (rootHeight * DayInMillis * DaysInYear * timescaler), mrsd .getTime(), numberOfIntervals); // this is to generate kml output KMLGenerator kmloutput = new KMLGenerator(); layers = new ArrayList<Layer>(); // Execute threads final int NTHREDS = Runtime.getRuntime().availableProcessors(); ExecutorService executor = Executors.newFixedThreadPool(NTHREDS); executor.submit(new Places()); executor.submit(new Branches()); executor.submit(new Circles()); executor.shutdown(); // Wait until all threads are finished while (!executor.isTerminated()) { } // generate kml kmloutput.generate(writer, timeLine, layers); // stop timing time += System.currentTimeMillis(); }// END: GenerateKML // ////////////// // ---PLACES---// // ////////////// private class Places implements Runnable { public void run() { // this is for Places folder: String placesDescription = null; Layer placesLayer = new Layer("Places", placesDescription); for (int i = 0; i < table.getRowCount(); i++) { String name = String.valueOf(table.getValueAt(i, 0)); Double longitude = Double.valueOf(String.valueOf(table .getValueAt(i, 2))); Double latitude = Double.valueOf(String.valueOf(table .getValueAt(i, 1))); placesLayer.addItem(new Place(name, null, new Coordinates( longitude, latitude), 0, 0)); } layers.add(placesLayer); } }// END: Places class // //////////////// // ---BRANCHES---// // //////////////// private class Branches implements Runnable { public void run() { try { // this is for Branches folder: String branchesDescription = null; Layer branchesLayer = new Layer("Branches", branchesDescription); double treeHeightMax = Utils.getTreeHeightMax(tree); int branchStyleId = 1; for (Node node : tree.getNodes()) { if (!tree.isRoot(node)) { String state = getRandomState((String) node .getAttribute(stateAttName), true); Node parentNode = tree.getParent(node); String parentState = getRandomState((String) parentNode .getAttribute(stateAttName), false); if (state != null && parentState != null) { if (!state.toLowerCase().equals( parentState.toLowerCase())) { float longitude = Utils.matchStateCoordinate( table, state, 2); float latitude = Utils.matchStateCoordinate( table, state, 1); float parentLongitude = Utils .matchStateCoordinate(table, parentState, 2); float parentLatitude = Utils .matchStateCoordinate(table, parentState, 1); double nodeHeight = Utils.getNodeHeight(tree, node); double parentHeight = Utils.getNodeHeight(tree, parentNode); /** * altitude mapping * */ double maxAltitude = Utils.map(Utils .rhumbDistance(parentLongitude, parentLatitude, longitude, latitude), 0, EarthRadius, 0, maxAltMapping); /** * Color mapping * */ 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); /** * opacity mapping * */ int alpha = (int) Utils.map(nodeHeight, 0, treeHeightMax, maxBranchOpacityMapping, minBranchOpacityMapping); Color col = new Color(red, green, blue, alpha); Style linesStyle = new Style(col, branchWidth); linesStyle .setId("branch_style" + branchStyleId); double startTime = mrsd.minus((int) (nodeHeight * DaysInYear * timescaler)); double endTime = mrsd.minus((int) (parentHeight * DaysInYear * timescaler)); // TODO // System.out.println("*******************************"); // System.out.println(parentState + ":" + state); // System.out.println("*******************************"); branchesLayer.addItem(new Line((parentState + ":" + state), // string name new Coordinates(parentLongitude, parentLatitude), // startcoords startTime, // startime linesStyle, // style startstyle new Coordinates(longitude, latitude), // endcoords endTime, // double endtime linesStyle, // style endstyle maxAltitude, // double maxAltitude 0.0) // double duration ); branchStyleId++; }// END: state and parent state equality check }// END: null check }// END: root check }// END: nodes loop layers.add(branchesLayer); } catch (RuntimeException e) { e.printStackTrace(); } }// END: run }// END Branches class // /////////////// // ---CIRCLES---// // /////////////// private class Circles implements Runnable { public void run() { try { // this is for Circles folder: String circlesDescription = null; Layer circlesLayer = new Layer("Circles", circlesDescription); double[][] numberOfLineages = countLineagesHoldingState( numberOfIntervals, rootHeight); double lineagesCountMax = Utils.get2DArrayMax(numberOfLineages); int circleStyleId = 1; for (int i = 0; i < (numberOfIntervals - 1); i++) { for (int j = 0; j < (table.getRowCount()); j++) { if (numberOfLineages[i][j + 1] > 0) { /** * Color mapping * */ int red = (int) Utils.map( numberOfLineages[i][j + 1], 0, lineagesCountMax, minPolygonRedMapping, maxPolygonRedMapping); int green = (int) Utils.map( numberOfLineages[i][j + 1], 0, lineagesCountMax, minPolygonGreenMapping, maxPolygonGreenMapping); int blue = (int) Utils.map( numberOfLineages[i][j + 1], 0, lineagesCountMax, minPolygonBlueMapping, maxPolygonBlueMapping); /** * Opacity mapping * Larger the values more opaque the colors * */ int alpha = (int) Utils.map( numberOfLineages[i][j + 1], 0, lineagesCountMax, maxPolygonOpacityMapping, minPolygonOpacityMapping); Color col = new Color(red, green, blue, alpha); Style circlesStyle = new Style(col, 0); circlesStyle.setId("circle_style" + circleStyleId); circleStyleId++; double radius = Math.round(100 * Math .sqrt(numberOfLineages[i][j + 1])) * polygonsRadiusMultiplier; int days = (int) (numberOfLineages[i][0] * DaysInYear * timescaler); double startTime = mrsd.minus(days); // this is to get duration in milliseconds: double duration = ((rootHeight - numberOfLineages[i][0]) / (i + 1)) * DayInMillis; String name = String .valueOf(table.getValueAt(j, 0)); Double longitude = Double.valueOf(String .valueOf(table.getValueAt(j, 1))); Double latitude = Double.valueOf(String .valueOf(table.getValueAt(j, 2))); circlesLayer.addItem(new Polygon(name + "_" + radius + "_" + "km", // String name Utils.generateCircle( // List<Coordinates> latitude, // centerLat longitude, // centerLong radius, // radius 36), // numPoints circlesStyle, // Style style startTime, // double startime duration * DaysInYear * timescaler // duration )); } }// END: row loop }// END: col loop layers.add(circlesLayer); } catch (RuntimeException e) { e.printStackTrace(); } }// END: run }// END: Circles class private double[][] countLineagesHoldingState(int numberOfIntervals, double rootHeight) { double delta = rootHeight / numberOfIntervals; double[][] numberOfLineages = new double[(numberOfIntervals - 1)][table .getRowCount() + 1]; for (int i = 0; i < (numberOfIntervals - 1); i++) { numberOfLineages[i][0] = rootHeight - ((i + 1) * delta); } for (int i = 0; i < (numberOfIntervals - 1); i++) { for (int j = 0; j < table.getRowCount(); j++) { int numberOfLineagesOfState = 0; for (Node node : tree.getNodes()) { if (!tree.isRoot(node)) { String state = getRandomState((String) node .getAttribute(stateAttName), false); Node parentNode = tree.getParent(node); String parentState = getRandomState((String) parentNode .getAttribute(stateAttName), false); if (state != null && parentState != null) { if ((tree.getHeight(node) <= numberOfLineages[i][0]) && (tree.getHeight(parentNode) > numberOfLineages[i][0])) { String name = String.valueOf(table.getValueAt( j, 0)); if ((state.toLowerCase().equals(parentState .toLowerCase())) && (parentState.toLowerCase() .equals(name.toLowerCase()))) { numberOfLineagesOfState++; }// END: state }// END: height check }// END: null check }// END: root check }// END: node loop numberOfLineages[i][j + 1] = numberOfLineagesOfState; }// END: col loop }// END: row loop return numberOfLineages; }// END: countLineagesHoldingState private String getRandomState(String state, boolean verbose) { // pick always the same states in this run generator.setSeed(time); if (!state.contains("+")) {// single state so return state return state; } else {// this breaks ties if (verbose) System.out.println("Found combined " + stateAttName + " attribute: " + state); state = Utils.pickRand(state.split("\\+"), generator); if (verbose) System.out.println("Randomly picking: " + state); return state; } }// END: getRandomState }// END: DiscreteTreeToKML class