package org.openlca.app.editors.processes.kml;
import java.io.StringReader;
import java.io.StringWriter;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.openlca.app.M;
import org.openlca.geo.kml.FeatureType;
import org.openlca.util.BinUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class KmlUtil {
private KmlUtil() {
}
public static String toKml(byte[] kmz) {
if (kmz == null)
return null;
try {
return new String(BinUtils.unzip(kmz), "utf-8");
} catch (Exception e) {
Logger log = LoggerFactory.getLogger(KmlUtil.class);
log.error("failed to unzip KMZ", e);
return null;
}
}
public static String prettyFormat(String kml) throws Exception {
if (kml == null)
return null;
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(new StringReader(kml));
XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
StringWriter writer = new StringWriter();
outputter.output(doc, writer);
return writer.toString();
}
public static String getDisplayText(byte[] kmz) {
String kml = toKml(kmz);
if (kml == null)
return "none";
try {
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(new StringReader(kml));
return getDisplayText(doc.getRootElement());
} catch (Exception e) {
Logger log = LoggerFactory.getLogger(KmlUtil.class);
log.error("failed to parse KML", e);
return "invalid XML";
}
}
private static String getDisplayText(Element root) {
FeatureType type = null;
int polygons = count(root, "Polygon");
int lines = count(root, "LineString");
int points = count(root, "Point");
if (polygons > 1)
type = FeatureType.MULTI_POLYGON;
else if (polygons > 0)
type = FeatureType.POLYGON;
else if (lines > 1)
type = FeatureType.MULTI_LINE;
else if (lines > 0)
type = FeatureType.LINE;
else if (points > 1)
type = FeatureType.MULTI_POINT;
else if (points > 0)
type = FeatureType.POINT;
if (type == null)
return getLabel(type);
List<Element> elements = findElements(root, "coordinates");
if (elements.size() == 0)
return "";
return getLabel(type) + " [" + getCoordinates(elements, type) + "]";
}
private static String getLabel(FeatureType type) {
switch (type) {
case POINT:
return M.Point;
case LINE:
return M.Line;
case POLYGON:
return M.Polygon;
case MULTI_POINT:
return M.MultiPoint;
case MULTI_LINE:
return M.MultiLine;
case MULTI_POLYGON:
return M.MultiPolygon;
default:
return "unsupported shape";
}
}
private static String getCoordinates(List<Element> elements,
FeatureType type) {
String[] first = null;
String[] last = null;
int count = 0;
for (Element element : elements) {
String text = element.getTextTrim();
if (text == null || text.isEmpty())
continue;
String[] parts = text.split(" ");
if (parts.length == 0)
continue;
if (first == null)
first = parts;
else
last = parts;
count++;
}
if (first == null || first.length == 0)
return "";
String coords = getCoordinates(first, type);
if (last == null || last.length == 0)
return coords;
coords += " ";
if (count > 2)
coords += "... ";
coords += getCoordinates(last, type);
return coords;
}
private static String getCoordinates(String[] parts, FeatureType type) {
if (parts == null || parts.length == 0)
return "";
String first = formatCoordinate(parts[0]);
if (type == FeatureType.POINT || type == FeatureType.MULTI_POINT)
return first;
if (parts.length < 2)
return "";
String last = formatCoordinate(parts[parts.length - 1]);
if (parts.length == 2)
return first + " " + last;
return first + " .. " + last;
}
private static String formatCoordinate(String part) {
try {
DecimalFormat format = new DecimalFormat("###.00",
new DecimalFormatSymbols(Locale.US));
String[] texts = part.split(",");
double lat = Double.parseDouble(texts[0]);
double lon = Double.parseDouble(texts[1]);
return format.format(lat) + "," + format.format(lon);
} catch (Exception e) {
Logger log = LoggerFactory.getLogger(KmlUtil.class);
log.error("failed to parse coordinate " + part, e);
return "invalid coordinate";
}
}
private static int count(Element root, String name) {
return findElements(root, name).size();
}
private static List<Element> findElements(Element root, String name) {
if (root == null || name == null)
return Collections.emptyList();
if (Objects.equals(name, root.getName()))
return Collections.singletonList(root);
List<Element> elements = new ArrayList<>();
for (Object obj : root.getChildren()) {
if (!(obj instanceof Element))
continue;
Element child = (Element) obj;
elements.addAll(findElements(child, name));
}
return elements;
}
}