package org.openlca.jsonld.output;
import java.io.StringReader;
import java.util.Scanner;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.events.Characters;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import org.openlca.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
/**
* A minimal converter of KML to GeoJSON (as used in openLCA).
*
* @see https://developers.google.com/kml/documentation/kmlreference
* @see http://geojson.org/geojson-spec.html
*/
class Kml2GeoJson {
private Kml2GeoJson() {
}
static JsonObject convert(String kml) {
if (kml == null)
return null;
try {
return new Kml2GeoJson().parse(kml);
} catch (Exception e) {
Logger log = LoggerFactory.getLogger(Kml2GeoJson.class);
log.error("failed to parse kml " + Strings.cut(kml, 75), e);
return null;
}
}
private JsonObject parse(String kml) throws Exception {
XMLInputFactory xif = XMLInputFactory.newFactory();
try (StringReader string = new StringReader(kml)) {
XMLEventReader events = xif.createXMLEventReader(string);
while (events.hasNext()) {
XMLEvent event = events.nextEvent();
if (isStartElement(event, "Placemark")) {
return readGeometry(events);
}
}
}
return null;
}
private JsonObject readGeometry(XMLEventReader events) throws Exception {
while (events.hasNext()) {
XMLEvent event = events.nextEvent();
if (isStartElement(event, "Point"))
return readPoint(events);
if (isStartElement(event, "LineString"))
return readLineString(events);
if (isStartElement(event, "LinearRing"))
return readLineString(events);
if (isStartElement(event, "Polygon"))
return readPolygon(events);
if (isStartElement(event, "MultiGeometry"))
return readMultiGeometry(events);
}
return null;
}
private JsonObject readPoint(XMLEventReader events) throws Exception {
JsonObject point = new JsonObject();
point.addProperty("type", "Point");
StringBuilder coordinates = null;
while (events.hasNext()) {
XMLEvent event = events.nextEvent();
if (isStartElement(event, "coordinates"))
coordinates = new StringBuilder();
if (isEndElement(event, "coordinates"))
break;
if (event.isCharacters() && coordinates != null) {
Characters chars = event.asCharacters();
coordinates.append(chars.getData());
}
}
if (coordinates != null) {
point.add("coordinates", readCoordinate(coordinates.toString()));
}
return point;
}
private JsonObject readLineString(XMLEventReader events) throws Exception {
JsonObject line = new JsonObject();
line.addProperty("type", "LineString");
StringBuilder coordinates = null;
while (events.hasNext()) {
XMLEvent event = events.nextEvent();
if (isStartElement(event, "coordinates"))
coordinates = new StringBuilder();
if (isEndElement(event, "coordinates"))
break;
if (event.isCharacters() && coordinates != null) {
Characters chars = event.asCharacters();
coordinates.append(chars.getData());
}
}
if (coordinates != null)
line.add("coordinates", readCoordinates(coordinates.toString()));
return line;
}
private JsonObject readPolygon(XMLEventReader events) throws Exception {
JsonObject polygon = new JsonObject();
polygon.addProperty("type", "Polygon");
JsonObject outerBoundary = null;
JsonObject innerBoundary = null;
int boundaryType = 0; // 0 = undefined; 1 = outer; 2 = inner
while (events.hasNext()) {
XMLEvent event = events.nextEvent();
if (isStartElement(event, "outerBoundaryIs")) {
boundaryType = 1;
continue;
}
if (isStartElement(event, "innerBoundaryIs")) {
boundaryType = 2;
continue;
}
if (isStartElement(event, "LinearRing")) {
if (boundaryType == 1)
outerBoundary = readLineString(events);
else if (boundaryType == 2)
innerBoundary = readLineString(events);
boundaryType = 0;
}
}
JsonArray coordinates = new JsonArray();
polygon.add("coordinates", coordinates);
if (outerBoundary != null) {
coordinates.add(outerBoundary.get("coordinates"));
if (innerBoundary != null) {
coordinates.add(innerBoundary.get("coordinates"));
}
}
return polygon;
}
private JsonObject readMultiGeometry(XMLEventReader events) throws Exception {
JsonObject obj = new JsonObject();
obj.addProperty("type", "GeometryCollection");
JsonArray geometries = new JsonArray();
obj.add("geometries", geometries);
JsonObject geo;
while ((geo = readGeometry(events)) != null) {
geometries.add(geo);
}
return obj;
}
private boolean isStartElement(XMLEvent event, String name) {
if (!event.isStartElement())
return false;
StartElement elem = event.asStartElement();
String n = elem.getName().getLocalPart();
return name.equals(n);
}
private boolean isEndElement(XMLEvent event, String name) {
if (!event.isEndElement())
return false;
EndElement elem = event.asEndElement();
String n = elem.getName().getLocalPart();
return name.equals(n);
}
private JsonArray readCoordinates(String s) {
JsonArray array = new JsonArray();
try (Scanner scanner = new Scanner(s)) {
while (scanner.hasNext()) {
JsonArray coordinate = readCoordinate(scanner.next());
array.add(coordinate);
}
}
return array;
}
private JsonArray readCoordinate(String s) {
String[] parts = s.split(",");
JsonArray array = new JsonArray();
for (int i = 0; i < parts.length; i++) {
double num = Double.parseDouble(parts[i]);
array.add(new JsonPrimitive(num));
}
return array;
}
}