package templates;
import generator.KMLGenerator;
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.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
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 readers.SliceHeightsReader;
import structure.Coordinates;
import structure.Layer;
import structure.Line;
import structure.Polygon;
import structure.Style;
import structure.TimeLine;
import utils.ThreadLocalSpreadDate;
import utils.Utils;
import contouring.ContourMaker;
import contouring.ContourPath;
import contouring.ContourWithSynder;
public class TimeSlicerToKML {
private int analysisType;
public final static int FIRST_ANALYSIS = 1;
public final static int SECOND_ANALYSIS = 2;
public long time;
// // how many days one year holds
// private static final int DaysInYear = 365;
// Concurrency stuff
private ConcurrentMap<Double, List<Coordinates>> slicesMap;
private RootedTree currentTree;
private TreeImporter treeImporter;
private RootedTree tree;
private int numberOfIntervals;
private double[] sliceHeights;
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 ThreadLocalSpreadDate mrsd;
private double timescaler;
private double treeRootHeight;
private double branchWidth;
private TreeImporter treesImporter;
private String mrsdString;
private int burnIn;
private boolean useTrueNoise;
private String coordinatesName;
private String longitudeName;
private String latitudeName;
private String rateString;
private String precisionString;
private List<Layer> layers;
private SimpleDateFormat formatter;
private String kmlPath;
private TimeLine timeLine;
private double startTime;
private double endTime;
private double hpd;
private int gridSize;
public TimeSlicerToKML() {
}
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 setNumberOfIntervals(int numberOfIntervals) {
this.numberOfIntervals = numberOfIntervals;
}
public void setBurnIn(int burnIn) {
this.burnIn = burnIn;
}
public void setUseTrueNoise(boolean useTrueNoise) {
this.useTrueNoise = useTrueNoise;
}
public void setLocationAttributeName(String name) {
coordinatesName = name;
longitudeName = (coordinatesName + 2);
latitudeName = (coordinatesName + 1);
}
public void setRateAttributeName(String name) {
rateString = name;
}
public void setPrecisionAttName(String name) {
precisionString = name;
}
public void setKmlWriterPath(String path) throws FileNotFoundException {
kmlPath = path;
}
public void setMaxAltitudeMapping(double max) {
maxAltMapping = max;
}
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, OutOfMemoryError {
// start timing
time = -System.currentTimeMillis();
mrsd = new ThreadLocalSpreadDate(mrsdString);
switch (analysisType) {
case FIRST_ANALYSIS:
tree = (RootedTree) treeImporter.importNextTree();
treeRootHeight = Utils.getNodeHeight(tree, tree.getRootNode());
sliceHeights = Utils.generateTreeSliceHeights(treeRootHeight,
numberOfIntervals);
timeLine = Utils.generateTreeTimeLine(tree, timescaler, numberOfIntervals, mrsd);
break;
case SECOND_ANALYSIS:
timeLine = Utils.generateCustomTimeLine(sliceHeights, timescaler, mrsd);
break;
}
//sort them in ascending numerical order
Arrays.sort(sliceHeights);
System.out.println("Using as slice times: ");
Utils.printArray(sliceHeights);
System.out.println();
// this is to generate kml output
layers = new ArrayList<Layer>();
// 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
// Wait until all threads are finished
executor.shutdown();
while (!executor.isTerminated()) {
}
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");
}
System.out.println("Generating polygons...");
Iterator<Double> iterator = slicesMap.keySet().iterator();
executor = Executors.newFixedThreadPool(NTHREDS);
formatter = new SimpleDateFormat("yyyy-MM-dd G", Locale.US);
startTime = timeLine.getStartTime();
endTime = timeLine.getEndTime();
System.out.println("Iterating through Map...");
int polygonsStyleId = 1;
while (iterator.hasNext()) {
System.out.println("Key " + polygonsStyleId + "...");
Double sliceTime = iterator.next();
// executor.submit(new Polygons(sliceTime, polygonsStyleId));
new Polygons(sliceTime, polygonsStyleId).run();
polygonsStyleId++;
}// END: while has next
switch (analysisType) {
case FIRST_ANALYSIS:
System.out.println("Generating branches...");
executor.submit(new Branches());
break;
case SECOND_ANALYSIS:
break;
}
executor.shutdown();
while (!executor.isTerminated()) {
}
System.out.println("Writing to kml...");
PrintWriter writer = new PrintWriter(kmlPath);
KMLGenerator kmloutput = new KMLGenerator();
kmloutput.generate(writer, timeLine, layers);
// stop timing
time += System.currentTimeMillis();
}// END: GenerateKML
// ///////////////////////////
// ---CONCURRENT POLYGONS---//
// ///////////////////////////
public class Polygons implements Runnable {
private double sliceTime;
private int polygonsStyleId;
public Polygons(Double sliceTime, int polygonsStyleId) {
this.sliceTime = sliceTime;
this.polygonsStyleId = polygonsStyleId;
}
public void run() throws OutOfMemoryError {
Layer polygonsLayer = new Layer("Time_Slice_"
+ formatter.format(sliceTime), null);
/**
* 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);
// System.out.println("sliceTime: " + sliceTime + " startTime: " +
// startTime + " endTime: " + endTime);
// System.out.println("red: " + red + " green: " + green + " blue: "
// + blue);
Color color = new Color(red, green, blue, alpha);
Style polygonsStyle = new Style(color, 0);
polygonsStyle.setId("polygon_style" + polygonsStyleId);
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++) {
if (list.get(i) == null) {
System.out.println("null found");
}
x[i] = list.get(i).getLatitude();
y[i] = list.get(i).getLongitude();
}
ContourMaker contourMaker = new ContourWithSynder(x, y, gridSize);
ContourPath[] paths = contourMaker.getContourPaths(hpd);
int pathCounter = 1;
for (ContourPath path : paths) {
double[] latitude = path.getAllX();
double[] longitude = path.getAllY();
List<Coordinates> coords = new ArrayList<Coordinates>();
for (int i = 0; i < latitude.length; i++) {
coords.add(new Coordinates(longitude[i], latitude[i], 0.0));
}
polygonsLayer.addItem(new Polygon("HPDRegion_" + pathCounter, // name
coords, // List<Coordinates>
polygonsStyle, // Style style
sliceTime, // double startime
0.0 // double duration
));
pathCounter++;
}// END: paths loop
layers.add(polygonsLayer);
slicesMap.remove(sliceTime);
}// END: run
}// END: Polygons
// ///////////////////////////
// ---CONCURRENT BRANCHES---//
// ///////////////////////////
private class Branches implements Runnable {
public void run() {
try {
double treeHeightMax = Utils.getTreeHeightMax(tree);
// this is for Branches folder:
String branchesDescription = null;
Layer branchesLayer = new Layer("Branches", branchesDescription);
int branchStyleId = 1;
for (Node node : tree.getNodes()) {
if (!tree.isRoot(node)) {
Double longitude = (Double) node
.getAttribute(longitudeName);
Double latitude = (Double) node
.getAttribute(latitudeName);
Double nodeHeight = Utils.getNodeHeight(tree, node);
Node parentNode = tree.getParent(node);
Double parentLongitude = (Double) parentNode
.getAttribute(longitudeName);
Double parentLatitude = (Double) parentNode
.getAttribute(latitudeName);
Double parentHeight = Utils.getNodeHeight(tree,
parentNode);
if (longitude != null && latitude != null
&& parentLongitude != null
&& parentLatitude != null) {
/**
* Mapping
* */
double maxAltitude = Utils.map(nodeHeight, 0,
treeHeightMax, 0, maxAltMapping);
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);
Color col = new Color(red, green, blue, alpha);
Style linesStyle = new Style(col, branchWidth);
linesStyle.setId("branch_style" + branchStyleId);
branchStyleId++;
double startTime = mrsd.minus((int) (nodeHeight
* Utils.DAYS_IN_YEAR * timescaler));
double endTime = mrsd.minus((int) (parentHeight
* Utils.DAYS_IN_YEAR * timescaler));
branchesLayer.addItem(new Line((parentLongitude
+ "," + parentLatitude + ":" + longitude
+ "," + latitude), // name
new Coordinates(parentLongitude,
parentLatitude), // startCoords
startTime, // double startime
linesStyle, // style startstyle
new Coordinates(longitude, latitude), // endCoords
endTime, // double endtime
linesStyle, // style endstyle
maxAltitude, // double maxAltitude
0.0) // double duration
);
}// END: null checks
}// END: root check
}// END: node loop
layers.add(branchesLayer);
} catch (RuntimeException e) {
e.printStackTrace();
}
}// END: run
}// END: Branches class
}// END: class