package generator; import java.io.IOException; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Locale; import kmlframework.kml.AltitudeModeEnum; import kmlframework.kml.Document; import kmlframework.kml.Feature; import kmlframework.kml.Folder; import kmlframework.kml.Kml; import kmlframework.kml.KmlException; import kmlframework.kml.LineString; import kmlframework.kml.LineStyle; import kmlframework.kml.LinearRing; import kmlframework.kml.Placemark; import kmlframework.kml.Point; import kmlframework.kml.PolyStyle; import kmlframework.kml.StyleSelector; import kmlframework.kml.TimePrimitive; import kmlframework.kml.TimeSpan; import structure.Container; import structure.Coordinates; import structure.Item; import structure.Layer; import structure.Line; import structure.Place; import structure.Polygon; import structure.Style; import structure.TimeLine; import utils.GeoIntermediate; import utils.Utils; public class KMLGenerator implements Generator { private List<StyleSelector> styles = new ArrayList<StyleSelector>(); private TimeLine timeLine; private SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd", Locale.US); private Document document = new Document(); private GeoIntermediate rhumbIntermediate; // 01-01-01 in millis before 1970-01-01 private static final double YearZeroInMillis = -62135773200000.0; public KMLGenerator() { } public void generate(PrintWriter writer, final TimeLine timeLine, final Collection<Layer> layers) throws IOException { this.timeLine = timeLine; // We create a new KML Document Kml kml = new Kml(); kml.setXmlIndent(true); kml.setGenerateObjectIds(false); // We add a document to the kml kml.setFeature(document); for (Layer layer : layers) { document.addFeature(generateLayer(layer)); } // We generate the kml file try { kml.createKml(writer); } catch (KmlException e) { e.printStackTrace(); } } private Feature generateLayer(final Layer layer) { Folder folder = new Folder(); folder.setName(layer.getName()); folder.setDescription(layer.getDescription()); generateContent(folder, layer); return folder; } private Feature generateContent(final Folder folder, final Container container) { for (Item item : container.getItems()) { folder.addFeature(generateItem(item)); } return null; } private Feature generateItem(final Item item) { if (item instanceof Line) { return generateLine((Line) item); } else if (item instanceof Polygon) { return generatePolygon((Polygon) item); } else if (item instanceof Place) { return generatePlacemark((Place) item); } throw new IllegalArgumentException("unknown item type"); } private Feature generatePolygon(final Polygon polygon) { Placemark placemark = new Placemark(polygon.getName()); double startTime = polygon.getStartTime(); double duration = polygon.getDuration(); // Style Style style = polygon.getPolyStyle(); PolyStyle polyStyle = new PolyStyle(); polyStyle.setOutline(false); polyStyle.setColor(Utils.getKMLColor(style.getStrokeColor())); style.setPolyStyle(polyStyle); styles.add(style); placemark.setStyleUrl(style.getId()); document.setStyleSelectors(styles); // Time // Parse minus if date is BC placemark.setTimePrimitive(new TimeSpan( startTime < YearZeroInMillis ? "-" + formatter.format(startTime) : formatter .format(startTime), duration > 0.0 ? (startTime + duration < YearZeroInMillis ? "-" + formatter.format(startTime + duration) : formatter .format(startTime + duration)) : "")); // Polygon OuterBoundaryIs LinearRing LinearRing linearRing = new LinearRing(); // linearRing.setCoordinates(); linearRing.setCoordinates(Utils.convertToPoint(polygon .getPolyCoordinates())); kmlframework.kml.Polygon localPolygon = new kmlframework.kml.Polygon(); localPolygon.setTessellate(true); localPolygon.setOuterBoundary(linearRing); placemark.setGeometry(localPolygon); return placemark; } private Feature generateLine(final Line line) { Feature feature; if (line.getEndStyle() == null || line.getStartStyle().equals(line.getEndStyle())) { double startTime = line.getStartTime(); double endTime = line.getEndTime(); double duration = line.getDuration(); double maxAltitude = line.getMaxAltitude(); double timeRange = endTime - startTime; if (timeLine.isInstantaneous() || timeRange == 0.0) { feature = generateLineSegment(line.getStartLocation(), line .getEndLocation(), startTime, duration, line .getStartStyle()); } else { Folder folder = new Folder(); // Parse start and end point coordinates double startLon = line.getStartLocation().getLongitude(); double startLat = line.getStartLocation().getLatitude(); double endLon = line.getEndLocation().getLongitude(); double endLat = line.getEndLocation().getLatitude(); int sliceCount = timeLine.getSliceCount(); rhumbIntermediate = new GeoIntermediate(startLon, startLat, endLon, endLat, sliceCount); double coords[][] = rhumbIntermediate.getCoords(); // Utils.print2DArray(coords); double a = -2 * maxAltitude / (Math.pow(sliceCount, 2) - sliceCount); double b = 2 * maxAltitude / (sliceCount - 1); for (int i = 0; i < sliceCount; i++) { double startAltitude = a * Math.pow((double) i, 2) + b * (double) i; double endAltitude = a * Math.pow((double) (i + 1), 2) + b * (double) (i + 1); double segmentStartTime = endTime - (i + 1) * ((endTime - startTime) / sliceCount); //TODO // System.out.println(startTime < YearZeroInMillis ? "-" // + formatter.format(startTime) : formatter // .format(startTime)); Placemark lineSegment = generateLineSegment( new Coordinates(coords[i][0], coords[i][1], startAltitude),// startCoordinates new Coordinates(coords[i + 1][0], coords[i + 1][1], endAltitude),// endCoordinates segmentStartTime,// startTime duration,// duration line.getStartStyle()// Style ); folder.addFeature(lineSegment); } feature = folder; } } else { Folder folder = new Folder(); // for (int i = 0; i < divisionCount; i++) { // Placemark lineSegment = generateLineSegment(loc1, loc2, time, // duration, style); // folder.addFeature(lineSegment); // } feature = folder; } feature.setName(line.getName()); return feature; } private Placemark generateLineSegment(Coordinates startCoordinates, Coordinates endCoordinates, double startTime, double duration, Style style) { LineStyle lineStyle = new LineStyle(); lineStyle.setColor(Utils.getKMLColor(style.getStrokeColor())); lineStyle.setWidth(style.getStrokeWidth()); style.setLineStyle(lineStyle); styles.add(style); document.setStyleSelectors(styles); LineString lineString = new LineString(); lineString.setTessellate(true); lineString.setAltitudeMode(AltitudeModeEnum.relativeToGround); List<Point> points = new ArrayList<Point>(); points.add(generatePoint(startCoordinates)); points.add(generatePoint(endCoordinates)); lineString.setCoordinates(points); Placemark placemark = new Placemark(); if (!Double.isNaN(duration)) { // Parse minus if date is BC TimePrimitive timePrimitive = new TimeSpan( startTime < YearZeroInMillis ? "-" + formatter.format(startTime) : formatter .format(startTime), duration > 0.0 ? (startTime + duration < YearZeroInMillis ? "-" + formatter.format(startTime + duration) : formatter.format(startTime + duration)) : ""); // System.out.println(formatter.format(startTime + duration)); placemark.setTimePrimitive(timePrimitive); } placemark.setStyleUrl(style.getId()); placemark.setGeometry(lineString); return placemark; } private Placemark generatePlacemark(final Place place) { Placemark placemark = new Placemark(); placemark.setGeometry(generatePoint(place.getCoordinates())); placemark.setName(place.getName()); return placemark; } private Point generatePoint(final Coordinates coordinates) { Point point = new Point(); point.setAltitudeMode(AltitudeModeEnum.relativeToGround); point.setAltitude(coordinates.getAltitude()); point.setLongitude(coordinates.getLongitude()); point.setLatitude(coordinates.getLatitude()); return point; } @Override public String toString() { return "KML"; } }