package jeql.command.io.kml;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import jeql.JeqlVersion;
import jeql.api.command.Command;
import jeql.api.error.ExecutionException;
import jeql.api.row.Row;
import jeql.api.row.RowIterator;
import jeql.api.row.RowSchema;
import jeql.api.row.RowUtil;
import jeql.api.table.Table;
import jeql.engine.EngineContext;
import jeql.engine.Scope;
import jeql.util.StringUtil;
import com.vividsolutions.jts.geom.Geometry;
public class KMLWriterCommand implements Command
{
private static final String DEFAULT_ICON_HREF = "http://maps.google.com/mapfiles/kml/shapes/donut.png";
private String filename = null;
private List<Table> dataTables = new ArrayList<Table>();
private PrintWriter writer = null;
private RowSchema schema;
private Table styleTbl = null;
private String docComment = null;
private String docName = null;
private String docDescription = null;
private boolean addLabelPoint = false;
private int precision = -1;
private boolean hasStyleColumns;
// Geometry attributes
private boolean defaultExtrude = false;
private double defaultAltitude = Double.NaN;
private String defaultAltitudeMode = null;
private KMLStyle defaultStyle = new KMLStyle(KMLCol.DEFAULT_STYLE_ID, "ffffffff");
private int nameIndex = -1;
private int descriptionIndex = -1;
private int styleUrlIndex = -1;
private int timeSpanBeginIndex = -1;
private int timeSpanEndIndex = -1;
private int timeStampWhenIndex = -1;
private StyleMapWriter styleMapWriter = null;
private FolderWriter folderWriter = null;
// this will be non-null if a KMZ file was written
// it needs to be closed after writing
private ZipOutputStream zipos = null;
public KMLWriterCommand()
{
defaultStyle = createDefaultStyle();
}
private static KMLStyle createDefaultStyle()
{
KMLStyle style = new KMLStyle(KMLCol.DEFAULT_STYLE_ID, "ffffffff", 1, "a0a0a0a0");
style.setPolyColorMode(KMLCol.KML_RANDOM);
style.setIconHref(DEFAULT_ICON_HREF);
style.setIconColor("00ff00");
style.setLabelColor("ffffff");
return style;
}
public void setFile(String filename)
{
this.filename = filename;
}
public void setDefault(Table tbl)
{
dataTables.add(tbl);
}
public void setData(Table tbl)
{
dataTables.add(tbl);
}
public void setStyles(Table styles)
{
this.styleTbl = styles;
}
public void setComment(String comment)
{
docComment = comment;
}
public void setName(String name)
{
docName = name;
}
public void setDescription(String desc)
{
docDescription = desc;
}
public void setStyleId(String id)
{
defaultStyle.setId(id);
}
public void setPrecision(int precision)
{
this.precision = precision;
}
public void setLineStyleColor(String color)
{
defaultStyle.setLineColor(color);
}
public void setLineStyleColorMode(String mode)
{
defaultStyle.setLineColorMode(mode);
}
public void setLineStyleWidth(int width)
{
defaultStyle.setLineWidth(width);
}
public void setPolyStyleColor(String color)
{
defaultStyle.setPolyColor(color);
}
public void setPolyStyleColorMode(String mode)
{
defaultStyle.setPolyColorMode(mode);
}
public void setPolyStyleFill(int fill)
{
defaultStyle.setPolyFill(fill);
}
public void setPolyStyleOutline(int outline)
{
defaultStyle.setPolyOutline(outline);
}
public void setLabelStyleColor(String color)
{
defaultStyle.setLabelColor(color);
}
public void setLabelStyleColorMode(String mode)
{
defaultStyle.setLabelColor(mode);
}
public void setLabelStyleScale(double scale)
{
defaultStyle.setLabelScale(scale);
}
public void setIconStyleColor(String color)
{
defaultStyle.setIconColor(color);
}
public void setIconStyleColorMode(String mode)
{
defaultStyle.setIconColorMode(mode);
}
public void setIconStyleScale(double scale)
{
defaultStyle.setIconScale(scale);
}
public void setIconStyleHref(String href)
{
defaultStyle.setIconHref(href);
}
public void setExtrude(int extrude)
{
defaultExtrude = extrude == 1;
}
public void setAltitude(double altitude)
{
defaultAltitude = altitude;
}
public void setAltitudeMode(String altitudeMode)
{
defaultAltitudeMode = altitudeMode;
}
/**
* Sets whether to add a label point to non-point features.
*
* @param addLabelPoint
*/
public void setAddLabelPoint(boolean addLabelPoint)
{
this.addLabelPoint = addLabelPoint;
}
protected PrintWriter getKmlWriter(String filename) throws Exception
{
if (StringUtil.endsWithIgnoreCase(filename, ".kmz")) {
return getKmzWriter(filename);
}
return getWriter(filename);
}
private static PrintWriter getWriter(String filename) throws IOException
{
if (filename != null) {
return new PrintWriter(new FileWriter(new File(filename)));
}
return EngineContext.OUTPUT_WRITER;
}
private static final String KMZ_CONTENT_FILE = "doc.kml";
private static PrintWriter getKmzWriter(String filename) throws Exception
{
ZipOutputStream zipos = new ZipOutputStream(new FileOutputStream(new File(filename)));
zipos.putNextEntry(new ZipEntry(KMZ_CONTENT_FILE));
return new PrintWriter(zipos);
}
public void execute(Scope scope) throws Exception
{
write();
}
private void write() throws Exception
{
// writer = getWriter(); // OLD
writer = getKmlWriter(filename);
try {
writePrologue();
styleMapWriter = new StyleMapWriter(styleTbl);
// styles & styleMaps
styleMapWriter.write(writer);
if (defaultStyle != null)
defaultStyle.write(writer);
if (styleTbl != null)
writeStyles(styleTbl);
for (Table tbl : dataTables) {
write(tbl);
}
writeEpilogue();
}
finally {
writer.close();
}
}
private void write(Table tbl) throws Exception
{
RowIterator ri = tbl.getRows().iterator();
schema = ri.getSchema();
folderWriter = new FolderWriter(schema);
hasStyleColumns = KMLStyle.hasStyleColumns(schema);
nameIndex = schema.getColIndexIgnoreCase(KMLCol.KML_NAME);
descriptionIndex = schema.getColIndexIgnoreCase(KMLCol.KML_DESCRIPTION);
styleUrlIndex = schema.getColIndexIgnoreCase(KMLCol.KML_STYLEURL);
timeSpanBeginIndex = schema.getColIndexIgnoreCase(KMLCol.KML_TIMESPAN_BEGIN);
timeSpanEndIndex = schema.getColIndexIgnoreCase(KMLCol.KML_TIMESPAN_END);
timeStampWhenIndex = schema.getColIndexIgnoreCase(KMLCol.KML_TIMESTAMP_WHEN);
write(ri);
}
private void write(RowIterator ri) throws Exception
{
// placemarks (also folders if specified)
while (true) {
Row row = ri.next();
if (row == null) break;
writePlacemark(row);
}
folderWriter.finishedRows(writer);
}
private void writeStyles(Table styleTbl) throws IOException
{
RowIterator it = styleTbl.getRows().iterator();
RowSchema schema = it.getSchema();
while (true) {
Row row = it.next();
if (row == null)
break;
KMLStyle style = new KMLStyle(schema, row, true);
style.write(writer);
}
}
private void writePlacemark(Row row) throws IOException
{
folderWriter.nextRow(row, writer);
writer.println();
writer.println("<Placemark>");
XMLWriter.writeElement(writer, 2, "name", nameIndex, row, null, true);
XMLWriter.writeElement(writer, 2, "description", descriptionIndex, row, null, true);
writeStyleUrl(writer, 2, row);
if (hasStyleColumns) {
KMLStyle style = new KMLStyle(schema, row, true);
style.write(writer);
}
writeTime(writer, 2, row);
// Geometry attributes
double altitude = RowUtil.getDouble(schema, KMLCol.KML_ALTITUDE, row,
defaultAltitude);
boolean extrude = KMLUtil.toBooleanLoose(schema, KMLCol.KML_EXTRUDE, row, defaultExtrude);
String altitudeMode = RowUtil.getString(schema, KMLCol.KML_ALTITUDE_MODE, row,
defaultAltitudeMode);
writeGeometry(KMLUtil.getGeometry(schema, row), altitude, extrude, altitudeMode);
writer.println("</Placemark>");
}
private static String formatStyleUrl(String url)
{
// if link assume correct!
if (url.startsWith("http:"))
return url;
// add a # if not present
if (url.indexOf("#") < 0)
return "#" + url;
return url;
}
private void writeStyleUrl(PrintWriter writer, int indent, Row row)
{
String indentStr = XMLWriter.indentStr(indent);
String styleName = "#" + defaultStyle.getId();
if (styleUrlIndex >= 0) {
styleName = formatStyleUrl(row.getValue(styleUrlIndex).toString());
}
writer.println(indentStr + "<styleUrl>" + styleName + "</styleUrl>");
}
private void writeTime(PrintWriter writer, int indent, Row row)
{
String indentStr = XMLWriter.indentStr(indent);
if (timeStampWhenIndex >= 0) {
writer.println(indentStr + "<TimeStamp>");
writer.println(indentStr + " <when>" + KMLUtil.formatDate(row.getValue(timeStampWhenIndex))
+ "</when>");
writer.println(indentStr + "</TimeStamp>");
return;
}
if (timeSpanBeginIndex >= 0 || timeSpanEndIndex >= 0) {
writer.println(indentStr + "<TimeSpan>");
if (timeSpanBeginIndex >= 0) {
writer.println(indentStr + " <begin>" + KMLUtil.formatDate(row.getValue(timeSpanBeginIndex))
+ "</begin>");
}
if (timeSpanEndIndex >= 0) {
writer.println(indentStr + " <end>" + KMLUtil.formatDate(row.getValue(timeSpanEndIndex))
+ "</end>");
}
writer.println(indentStr + "</TimeSpan>");
}
}
private void writePrologue() throws IOException
{
writer.println("<?xml version='1.0' encoding='UTF-8'?>");
writer.println("<!-- Generated by Jeql (ver. " + JeqlVersion.VERSION + ")" + " "
+ (new Date()) + " -->");
if (docComment != null) {
writer.println();
writer.println("<!-- " + docComment + " -->");
}
writer.println();
writer.println("<kml xmlns='http://earth.google.com/kml/2.1'>");
writer.println("<Document>");
if (docName != null)
writer.println("<name>" + docName + "</name>");
if (docDescription != null)
writer.println("<description>" + docDescription + "</description>");
writer.println("");
}
private void writeEpilogue() throws IOException
{
writer.println("</Document>");
writer.println("</kml>");
}
private void writeGeometry(Geometry g, double altitude, boolean extrude, String altitudeMode)
throws IOException
{
Geometry gout = g;
// TODO: check if geometry already contains a point
if (addLabelPoint && g.getDimension() > 0) {
gout = KMLUtil.geomWithLabelPoint(g);
}
writer.print(KMLGeometryWriter.writeGeometry(gout, altitude, precision, extrude, altitudeMode));
}
}