/*
* TreeGeoJSONGenerator.java
*
* Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard
*
* This file is part of BEAST.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership and licensing.
*
* BEAST is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* BEAST is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with BEAST; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
package dr.app.phylogeography.tools;
import dr.app.phylogeography.tools.kml.*;
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 java.awt.*;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Andrew Rambaut
*/
public class TreeGeoJSONGenerator {
/*
{ "type": "FeatureCollection",
"features": [
{ "type": "Feature",
"geometry": {"type": "Point", "coordinates": [102.0, 0.5]},
"properties": {"prop0": "value0"}
},
{ "type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": [
[102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0]
]
},
"properties": {
"prop0": "value0",
"prop1": 0.0
}
},
{ "type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0],
[100.0, 1.0], [100.0, 0.0] ]
]
},
"properties": {
"prop0": "value0",
"prop1": {"this": "that"}
}
}
]
}
*/
private final RootedTree tree;
private final Map<String, Location> locationMap = new HashMap<String, Location>();
private final List<GeoPoint> points = new ArrayList<GeoPoint>();
private final List<GeoLine> lines = new ArrayList<GeoLine>();
public TreeGeoJSONGenerator(RootedTree tree, List<Location> locationList) {
this.tree = tree;
if (locationList != null) {
double minLat = Double.MAX_VALUE;
double maxLat = -Double.MAX_VALUE;
double minLong = Double.MAX_VALUE;
double maxLong = -Double.MAX_VALUE;
for (Location location : locationList) {
if (location.getLatitude() < minLat) {
minLat = location.getLatitude();
}
if (location.getLatitude() > maxLat) {
maxLat = location.getLatitude();
}
if (location.getLongitude() < minLong) {
minLong = location.getLongitude();
}
if (location.getLongitude() > maxLong) {
maxLong = location.getLongitude();
}
locationMap.put(location.getState(), location);
}
}
traverseTree(tree, tree.getRootNode());
}
private GeoPoint traverseTree(RootedTree tree, Node node) {
GeoPoint point = new GeoPoint();
point.time = tree.getHeight(node);
point.location = (String)node.getAttribute("location");
point.host = (String)node.getAttribute("host");
point.longitude = locationMap.get(point.location).getLongitude();
point.latitude = locationMap.get(point.location).getLatitude();
if (tree.isExternal(node)) {
point.label = tree.getTaxon(node).getName();
point.probability = 1;
} else {
point.label = "";
point.probability = (Double)node.getAttribute("posterior");
for (Node child : tree.getChildren(node)) {
GeoPoint point1 = traverseTree(tree, child);
GeoLine line = new GeoLine();
line.label = "";
line.time0 = point.time;
line.time1 = point1.time;
line.location0 = point.location;
line.location1 = point1.location;
line.host0 = point.host;
line.host1 = point1.host;
line.longitude0 = point.longitude;
line.longitude1 = point1.longitude;
line.latitude0 = point.latitude;
line.latitude1 = point1.latitude;
lines.add(line);
}
}
points.add(point);
return point;
}
public void generate(String documentName, PrintWriter writer) {
writer.println("{ \"type\": \"FeatureCollection\",");
writer.println("\t\"features\": [");
for (GeoPoint point : points) {
generatePoint(writer, point);
}
writer.println("\t]");
writer.println("},");
writer.println("{ \"type\": \"FeatureCollection\",");
writer.println("\t\"features\": [");
for (GeoLine line : lines) {
generateLine(writer, line);
}
writer.println("\t]");
writer.println("}");
}
public void generatePoint(PrintWriter writer, GeoPoint point) {
writer.println("\t\t{ \"type\": \"Feature\",");
writer.println("\t\t\t\"geometry\": {\"type\": \"Point\", \"coordinates\": [" + point.longitude + ", " + point.latitude + "]},");
writer.println("\t\t\t\"properties\": {\"label\": \"" + point.label + "\", \"time\": \"" + point.time + "\", \"probability\": \"" + point.probability +
"\", \"location\": \"" + point.location + "\", \"host\": \"" + point.host + "\"}");
writer.println("},");
}
public void generateLine(PrintWriter writer, GeoLine line) {
writer.println("\t\t{ \"type\": \"Feature\",");
writer.println("\t\t\t\"geometry\": {\"type\": \"LineString\", \"coordinates\": [[" + line.longitude0 + ", " + line.latitude0 + "], [" + line.longitude1 + ", " + line.latitude1 + "]]},");
writer.println("\t\t\t\"properties\": {\"label\": \"" + line.label + "\", \"time0\": \"" + line.time0 + "\", \"time0\": \"" + line.time1 +
"\", \"location0\": \"" + line.location0 + "\", \"location1\": \"" + line.location1 +
"\", \"host\": \"" + line.host0 + "\", \"host1\": \"" + line.host1 + "\"}");
writer.println("},");
}
class GeoPoint {
String label;
double time;
double probability;
String location;
String host;
double longitude;
double latitude;
}
class GeoLine {
String label;
double time0;
double time1;
String location0;
String location1;
String host0;
String host1;
double longitude0;
double latitude0;
double longitude1;
double latitude1;
}
public static void main(String[] args) {
String inputTreeFile = args[0];
RootedTree tree = null;
try {
TreeImporter importer = new NexusImporter(new FileReader(inputTreeFile));
tree = (RootedTree)importer.importNextTree();
} catch (ImportException e) {
e.printStackTrace();
return;
} catch (IOException e) {
e.printStackTrace();
return;
}
TreeGeoJSONGenerator generator = new TreeGeoJSONGenerator(tree, null);
try {
generator.generate("", new PrintWriter(new File("output.geojson")));
} catch (FileNotFoundException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
}
}