KmlFormatter.java
/*
* Copyright (c) 2015-2021 by k3b.
*
* This file is part of k3b-geoHelper library.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.k3b.geo.io.kml;
import org.apache.commons.io.FilenameUtils;
import java.io.Closeable;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Locale;
import de.k3b.geo.api.GeoPointDto;
import de.k3b.geo.api.IGeoPointInfo;
import de.k3b.geo.io.GeoFormatter;
import de.k3b.geo.io.GeoUriDef;
import de.k3b.util.XmlUtil;
/** convert List<IGeoPointInfo> to kml file format */
public class KmlFormatter implements Closeable {
private static final String HEADER = "<?xml version='1.0' encoding='UTF-8'?>\n" +
"<kml xmlns='http://www.opengis.net/kml/2.2'\n" +
"\txmlns:atom='http://www.w3.org/2005/Atom'\n" +
"\txmlns:gx='http://www.google.com/kml/ext/2.2'\n" +
"\txmlns:k3b='uri:https://github.com/k3b/k3b-geoHelper/' >\n" +
"\t<Document>";
private static final String FOOTER = "\t</Document>\n" +
"</kml>";
private static final String SYMBOL_HEADER = "\t\t<Style id='default'>";
private static final String SYMBOL_FOOTER = "\t\t</Style>\n";
private static final String IDENT3 = "\t\t\t";
private final PrintWriter printWriter;
private boolean created = false;
private boolean symbolCreated = false;
public static void export(List<IGeoPointInfo> geoInfos, PrintWriter printWriter) throws IOException {
KmlFormatter exporter = null;
try {
exporter = new KmlFormatter(printWriter);
for (IGeoPointInfo geoInfo : geoInfos) {
exporter.writeSymbolDefinition(geoInfo);
}
for (IGeoPointInfo geoInfo : geoInfos) {
if (!GeoPointDto.isEmpty(geoInfo)) {
exporter.writeGeoInformation(geoInfo);
}
}
} finally {
if (exporter != null) {
exporter.close();
}
}
}
private KmlFormatter(PrintWriter printWriter) {
this.printWriter = printWriter;
}
private void writeSymbolDefinition(IGeoPointInfo geoInfo) {
String symbol = geoInfo != null ? geoInfo.getSymbol() : null;
if (symbol != null && symbol.length() > 0) {
writeSymbolHeaderIfNecessary();
String baseName = getSymbolname(symbol);
pr(IDENT3 + "<IconStyle id='" + baseName +"'><Icon><href>" + XmlUtil.escapeXMLElement(symbol) +
"</href></Icon></IconStyle>");
}
}
private String getSymbolname(String symbol) {
return XmlUtil.escapeXmlAttribute(FilenameUtils.getBaseName(symbol));
}
private void writeGeoInformation(IGeoPointInfo geoInfo) {
if (symbolCreated) {
pr(SYMBOL_FOOTER);
symbolCreated = false;
}
if (geoInfo != null) {
writeHeaderIfNecessary();
pr("\t\t<Placemark>");
pr("description", geoInfo.getDescription());
pr("name", geoInfo.getName());
pr("k3b:" + GeoUriDef.ID, geoInfo.getId());
if (geoInfo.getSymbol() != null) {
pr("styleUrl", "#" + getSymbolname(geoInfo.getSymbol()));
}
if (geoInfo.getLink() != null) {
pr(IDENT3 + "<atom:link href='" + XmlUtil.escapeXmlAttribute(geoInfo.getLink()) +"' />");
}
boolean hasGeo = !GeoPointDto.isEmpty(geoInfo);
if (hasGeo || geoInfo.getTimeOfMeasurement() != null) {
pr(IDENT3 + "<Point>");
if (hasGeo) {
// note KmlDef_22.COORDINATES use lon,lat reverse order
pr(String.format(Locale.US, IDENT3 + "\t<coordinates>%f,%f</coordinates>",
geoInfo.getLongitude(), geoInfo.getLatitude()));
}
if (geoInfo.getTimeOfMeasurement() != null) {
pr(IDENT3 + "\t<when>" + XmlUtil.escapeXMLElement(GeoFormatter.formatDate(geoInfo.getTimeOfMeasurement())) + "</when>");
}
pr(IDENT3 + "</Point>");
}
if (geoInfo.getZoomMin() > 0) {
pr("k3b:"+ GeoUriDef.ZOOM, "" + geoInfo.getZoomMin());
}
if (geoInfo.getZoomMax() > 0) {
pr("k3b:"+ GeoUriDef.ZOOM_MAX, "" + geoInfo.getZoomMax());
}
pr("\t\t</Placemark>");
}
}
private void writeHeaderIfNecessary() {
if (!created) {
created = true;
pr(HEADER);
}
}
private void writeSymbolHeaderIfNecessary() {
writeHeaderIfNecessary();
if (!symbolCreated) {
symbolCreated = true;
pr(SYMBOL_HEADER);
}
}
private void pr(String tag, String value) {
if (notEmpty(value)) {
pr(IDENT3 +
"<" + tag +">" +
XmlUtil.escapeXMLElement(value) +
"</" + tag +">");
}
}
private boolean notEmpty(String value) {
return value != null && value.length() > 0;
}
protected void pr(String xml) {
printWriter.println(xml);
}
@Override
public void close() throws IOException {
if (created) pr(FOOTER);
printWriter.flush();
printWriter.close();
}
}