/*
* <copyright>
* Copyright 2011 BBN Technologies
* </copyright>
*/
package com.bbn.openmap.dataAccess.mapTile;
import java.awt.Color;
import java.awt.geom.Point2D;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import com.bbn.openmap.omGraphics.DrawingAttributes;
import com.bbn.openmap.util.FileUtils;
/**
* Creates a KML grid file for import into Google Earth. This file shows the
* tile boundaries for a specified tile zoom level.
*
* @author ddietrick
*/
public class KMLTileGridMaker {
protected String docTitle;
protected int level;
protected List<Tile> tiles;
protected DrawingAttributes drawingAttributes = DrawingAttributes.getDefaultClone();
/**
* @param builder
*/
protected KMLTileGridMaker(Builder builder) {
this.level = builder.zoomLevel;
tiles = makeTiles(level);
this.docTitle = builder.docTitle;
drawingAttributes.setLinePaint(builder.lineColor);
drawingAttributes.setFillPaint(builder.fillColor);
drawingAttributes.setSelectPaint(builder.labelColor);
}
protected List<Tile> makeTiles(int level) {
List<Tile> ret = new ArrayList<Tile>();
ZoomLevelInfo zli = new ZoomLevelInfo();
zli.setZoomLevel(level);
int edgeTileCount = zli.getEdgeTileCount();
OSMMapTileCoordinateTransform transform = new OSMMapTileCoordinateTransform();
zli.setScale(transform.getScaleForZoom(level));
for (int x = 0; x < edgeTileCount; x++) {
for (int y = 0; y < edgeTileCount; y++) {
Point2D ulllp = transform.tileUVToLatLon(new Point2D.Double(x, y), level);
Point2D lrllp = transform.tileUVToLatLon(new Point2D.Double(x + 1, y + 1), level);
ret.add(new Tile(x, y, ulllp, lrllp));
}
}
return ret;
}
protected void writeDoc(OutputStream output)
throws ParserConfigurationException, TransformerException {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder;
documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document document = documentBuilder.newDocument();
Element kmlElement = appendChild(document, document, createKMLElement(document));
Element docElement = appendChild(document, kmlElement, "Document", null);
appendChild(document, docElement, createStyle(document));
appendChild(document, docElement, createTiles(document));
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
DOMSource source = new DOMSource(document);
StreamResult result = new StreamResult(output);
transformer.transform(source, result);
}
protected Element createKMLElement(Document doc) {
Element rootElement = doc.createElement("kml");
rootElement.setAttribute("xmlns", "http://www.opengis.net/kml/2.2");
rootElement.setAttribute("xmlns:gx", "http://www.google.com/kml/ext/2.2");
rootElement.setAttribute("xmlns:kml", "http://www.opengis.net/kml/2.2");
rootElement.setAttribute("xmlns:atom", "http://www.w3.org/2005/Atom");
return rootElement;
}
protected Element createTiles(Document doc) {
Element folderElement = doc.createElement("Folder");
appendChild(doc, folderElement, "name", "Tile Layout for Zoom Level " + level);
appendChild(doc, folderElement, "open", "1");
List<Tile> tiles = makeTiles(level);
for (Tile tile : tiles) {
Point2D ul = tile.ul;
Point2D lr = tile.lr;
StringBuilder buf = new StringBuilder();
buf.append(ul.getX() + "," + ul.getY() + ",0 ");
buf.append(lr.getX() + "," + ul.getY() + ",0 ");
buf.append(lr.getX() + "," + lr.getY() + ",0 ");
buf.append(ul.getX() + "," + lr.getY() + ",0 ");
buf.append(ul.getX() + "," + ul.getY() + ",0 ");
// Polygon representing tile
Element placemark = appendChild(doc, folderElement, "Placemark", null);
appendChild(doc, placemark, "name", tile.title);
appendChild(doc, placemark, "styleUrl", "#" + styleString);
Element polygon = appendChild(doc, placemark, "Polygon", null);
appendChild(doc, polygon, "tessellate", "1");
Element obiElement = appendChild(doc, polygon, "outerBoundaryIs", null);
Element lrElement = appendChild(doc, obiElement, "LinearRing", null);
/*Element coordElement = */appendChild(doc, lrElement, "coordinates", buf.toString());
// Label for tile
Element placemark2 = appendChild(doc, folderElement, "Placemark", null);
appendChild(doc, placemark2, "name", tile.title);
appendChild(doc, placemark2, "styleUrl", "#" + styleString);
Element point = appendChild(doc, placemark2, "Point", null);
appendChild(doc, point, "coordinates", ((lr.getX() + ul.getX()) / 2) + "," + ((ul.getY() + lr.getY()) / 2) + ",0");
}
return folderElement;
}
String styleString = "tileborderstyle";
protected Element createStyle(Document doc) {
Element style = doc.createElement("Style");
style.setAttribute("id", styleString);
Element label = appendChild(doc, style, "LabelStyle", null);
// Element icon = appendChild(doc, style, "IconStyle", null);
Element line = appendChild(doc, style, "LineStyle", null);
Element poly = appendChild(doc, style, "PolyStyle", null);
String labelColorString = Integer.toHexString(((Color) drawingAttributes.getSelectPaint()).getRGB());
String lineColorString = Integer.toHexString(((Color) drawingAttributes.getLinePaint()).getRGB());
String fillColorString = Integer.toHexString(((Color) drawingAttributes.getFillPaint()).getRGB());
appendChild(doc, label, "color", labelColorString);
appendChild(doc, line, "color", lineColorString);
appendChild(doc, poly, "color", fillColorString);
return style;
}
protected Element appendChild(Document doc, Node parent, String childField, String value) {
Element element = (Element) parent.appendChild(doc.createElement(childField));
if (value != null) {
element.setTextContent(value);
}
return element;
}
protected Element appendChild(Document doc, Node parent, Element child) {
parent.appendChild(child);
return child;
}
/**
* @param args
*/
public static void main(String[] args) {
Color fillColor = new Color(0x3300FF00, true);
Color lineColor = new Color(0xFF00FF00);
com.bbn.openmap.util.ArgParser ap = new com.bbn.openmap.util.ArgParser("KMLTileGridMaker");
ap.add("level", "Tile Zoom Level to create grid for.", 1);
if (!ap.parse(args)) {
ap.printUsage();
System.exit(0);
}
String[] arg = ap.getArgValues("level");
if (arg != null) {
try {
int level = Integer.parseInt(arg[0]);
new Builder(level).setLineColor(lineColor).setFillColor(fillColor).setLabelColor(lineColor).go();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (TransformerException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NumberFormatException nfe) {
nfe.printStackTrace();
}
}
}
protected class Tile {
protected int uvx;
protected int uvy;
protected Point2D ul;
protected Point2D lr;
protected String title;
protected Tile(int uvx, int uvy, Point2D ul, Point2D lr) {
this.uvx = uvx;
this.uvy = uvy;
this.ul = ul;
this.lr = lr;
title = uvx + "_" + uvy;
}
}
/**
* Use this class to run the KMLTileGridMake. Create a builder, add any
* customization you want, call go() when you want to create the file.
*
* @author ddietrick
*/
public static class Builder {
int zoomLevel;
Color lineColor = Color.WHITE;
Color fillColor = Color.WHITE;
Color labelColor = Color.WHITE;
String docTitle;
String fileName;
OutputStream output;
boolean closeOutput = false;
public Builder() {
this(5);
}
public Builder(int tileZoomLevel) {
zoomLevel = tileZoomLevel;
docTitle = "Tile Boundaries for Zoom Level " + zoomLevel;
}
public Builder setLineColor(Color lineColor) {
this.lineColor = lineColor;
return this;
}
public Builder setFillColor(Color fillColor) {
this.fillColor = fillColor;
return this;
}
public Builder setLabelColor(Color labelColor) {
this.labelColor = labelColor;
return this;
}
public Builder setDocTitle(String docTitle) {
this.docTitle = docTitle;
return this;
}
public Builder setFileName(String fileName)
throws FileNotFoundException {
this.fileName = fileName;
File file = null;
if (fileName != null) {
file = new File(fileName);
}
if (file == null) {
String filePath = FileUtils.getFilePathToSaveFromUser("Choose location and name of KML file");
if (filePath != null) {
file = new File(filePath);
}
}
if (file != null) {
output = new FileOutputStream(file);
closeOutput = true;
}
return this;
}
/**
* If you provide an OutputStream, you're responsible for closing it.
*
* @param out
* @return this Builder
*/
public Builder setOutputStream(OutputStream out) {
this.output = out;
return this;
}
public void go()
throws ParserConfigurationException, TransformerException, IOException {
KMLTileGridMaker kml = new KMLTileGridMaker(Builder.this);
if (output == null) {
setFileName(null);
}
if (output != null) {
kml.writeDoc(output);
if (closeOutput) {
output.close();
}
} else {
throw new IOException("No output specified");
}
}
}
}