package templates;
import gui.InteractiveTableModel;
import java.io.FileReader;
import java.io.IOException;
import java.util.List;
import java.util.Random;
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 processing.core.PFont;
import structure.Coordinates;
import utils.Utils;
@SuppressWarnings("serial")
public class DiscreteTreeToProcessing extends PApplet {
public long time;
private TreeImporter importer;
private RootedTree tree;
private InteractiveTableModel table;
private String stateAttName;
private int numberOfIntervals;
private double rootHeight;
private MapBackground mapBackground;
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 minPolygonRedMapping;
private double minPolygonGreenMapping;
private double minPolygonBlueMapping;
private double minPolygonOpacityMapping;
private double maxPolygonRedMapping;
private double maxPolygonGreenMapping;
private double maxPolygonBlueMapping;
private double maxPolygonOpacityMapping;
private double branchWidth;
private double polygonsRadiusMultiplier;
private Random generator;
// min/max longitude
private float minX, maxX;
// min/max latitude
private float minY, maxY;
public DiscreteTreeToProcessing() {
}// END: DiscreteTreeToProcessing
public void setStateAttName(String name) {
stateAttName = name;
}
public void setNumberOfIntervals(int number) {
numberOfIntervals = number;
}
public void setTreePath(String path) throws IOException, ImportException {
importer = new NexusImporter(new FileReader(path));
}
public void setTable(InteractiveTableModel tableModel) {
table = tableModel;
}
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 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 setPolygonsRadiusMultiplier(double multiplier) {
polygonsRadiusMultiplier = multiplier;
}
public void setBranchWidth(double width) {
branchWidth = width;
}
public void setup() {
try {
minX = -180;
maxX = 180;
minY = -90;
maxY = 90;
generator = new Random();
// will improve font rendering speed with default renderer
hint(ENABLE_NATIVE_FONTS);
PFont plotFont = createFont("Monaco", 12);
textFont(plotFont);
tree = (RootedTree) importer.importNextTree();
// this is for time calculations
rootHeight = tree.getHeight(tree.getRootNode());
mapBackground = new MapBackground(this);
} catch (IOException e) {
e.printStackTrace();
} catch (ImportException e) {
e.printStackTrace();
}
}// END: setup
public void draw() {
// start timing
time = -System.currentTimeMillis();
noLoop();
smooth();
mapBackground.drawMapBackground();
drawCircles();
drawPlaces();
drawBranches();
drawPlacesLabels();
// stop timing
time += System.currentTimeMillis();
}// END:draw
// //////////////
// ---PLACES---//
// //////////////
private void drawPlaces() {
float radius = 7;
// White
fill(255, 255, 255);
noStroke();
for (int row = 0; row < table.getRowCount(); row++) {
Float longitude = Float.valueOf(String.valueOf(table.getValueAt(
row, 2)));
Float latitude = Float.valueOf(String.valueOf(table.getValueAt(row,
1)));
// Equirectangular projection:
float X = map(longitude, minX, maxX, 0, width);
float Y = map(latitude, maxY, minY, 0, height);
ellipse(X, Y, radius, radius);
}
}// END DrawPlaces
private void drawPlacesLabels() {
textSize(7);
// Black labels
fill(0, 0, 0);
for (int row = 0; row < table.getRowCount(); row++) {
String name = String.valueOf(table.getValueAt(row, 0));
Float longitude = Float.valueOf(String.valueOf(table.getValueAt(
row, 2)));
Float latitude = Float.valueOf(String.valueOf(table.getValueAt(row,
1)));
float X = map(longitude, minX, maxX, 0, width);
float Y = map(latitude, maxY, minY, 0, height);
text(name, X, Y);
}
}// END: DrawPlacesLabels
// ////////////////
// ---BRANCHES---//
// ////////////////
private void drawBranches() {
strokeWeight((float) branchWidth);
double treeHeightMax = Utils.getTreeHeightMax(tree);
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);
float x0 = map(parentLongitude, minX, maxX, 0, width);
float y0 = map(parentLatitude, maxY, minY, 0, height);
float x1 = map(longitude, minX, maxX, 0, width);
float y1 = map(latitude, maxY, minY, 0, height);
/**
* Color mapping
* */
double nodeHeight = tree.getHeight(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(x0, y0, x1, y1);
}// END: state and parent state equality check
}// END: null check
}// END: root check
}// END: nodes loop
}// END: DrawBranches
// ///////////////
// ---CIRCLES---//
// ///////////////
private void drawCircles() {
double[][] numberOfLineages = CountLineagesHoldingState(
numberOfIntervals, rootHeight);
double lineagesCountMax = Utils.get2DArrayMax(numberOfLineages);
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);
stroke(red, green, blue, alpha);
fill(red, green, blue, alpha);
double radius = Math.round(100 * Math
.sqrt(numberOfLineages[i][j + 1]))
* polygonsRadiusMultiplier;
Double longitude = Double.valueOf(String.valueOf(table
.getValueAt(j, 1)));
Double latitude = Double.valueOf(String.valueOf(table
.getValueAt(j, 2)));
List<Coordinates> coordinates = Utils.generateCircle(
latitude, // centerLat
longitude, // centerLong
radius, // radius
36); // numPoints
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: if numberOfLineages
}// END: table rows loop
}// END: numberOfIntervals loop
}// END: drawCircles
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 ((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: node loop
numberOfLineages[i][j + 1] = numberOfLineagesOfState;
}// END: col loop
}// END: row loop
return numberOfLineages;
}// END: CountLineagesHoldingState
private String getRandomState(String state, boolean verbose) {
generator.setSeed(time);
if (!state.contains("+")) {
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: PlotOnMap class