001package ca.bc.webarts.tools; 002 003import java.io.StringWriter; 004import java.util.ArrayList; 005import java.util.Collections; 006import java.util.List; 007 008import javax.xml.stream.XMLOutputFactory; 009import javax.xml.stream.XMLStreamException; 010import javax.xml.stream.XMLStreamWriter; 011 012import org.slf4j.Logger; 013import org.slf4j.LoggerFactory; 014 015import com.google.gson.JsonArray; 016import com.google.gson.JsonElement; 017import com.google.gson.JsonObject; 018 019/** 020 * A minimal converter of GeoJSON to KML (as used in openLCA). 021 * 022 * @see https://developers.google.com/kml/documentation/kmlreference 023 * @see http://geojson.org/geojson-spec.html 024 */ 025public class GeoJson2Kml { 026 027 private XMLStreamWriter kml; 028 029 private int indent = 0; 030 031 private GeoJson2Kml(XMLStreamWriter kml) { 032 this.kml = kml; 033 } 034 035 public static String convert(JsonObject geoJson) { 036 if (geoJson == null) 037 return null; 038 XMLOutputFactory fac = XMLOutputFactory.newInstance(); 039 try (StringWriter writer = new StringWriter()) { 040 XMLStreamWriter kml = fac.createXMLStreamWriter(writer); 041 new GeoJson2Kml(kml).doIt(geoJson); 042 kml.flush(); 043 writer.flush(); 044 kml.close(); 045 return writer.toString(); 046 } catch (Exception e) { 047 Logger log = LoggerFactory.getLogger(GeoJson2Kml.class); 048 log.error("failed to convert GeoJSON", e); 049 return null; 050 } 051 } 052 053 private void doIt(JsonObject geoJson) throws Exception { 054 kml.writeStartDocument("utf-8", "1.0"); 055 kml.setDefaultNamespace("http://earth.google.com/kml/2.0"); 056 startElem("kml"); 057 kml.writeNamespace("", "http://earth.google.com/kml/2.0"); 058 startElems("Folder", "Placemark"); 059 writeGeometry(geoJson); 060 endElems(3); 061 kml.writeEndDocument(); 062 } 063 064 private void writeGeometry(JsonObject geoJson) throws Exception { 065 JsonElement typeElem = geoJson.get("type"); 066 if (typeElem == null || !typeElem.isJsonPrimitive()) 067 return; 068 String type = typeElem.getAsString(); 069 switch (type) { 070 case "Point": 071 writePoint(geoJson); 072 break; 073 case "LineString": 074 writeLineString(geoJson); 075 break; 076 case "Polygon": 077 writePolygon(geoJson); 078 break; 079 case "GeometryCollection": 080 writeGeometryCollection(geoJson); 081 break; 082 } 083 084 } 085 086 private void writePoint(JsonObject geoJson) throws Exception { 087 String coordinate = getCoordinate(geoJson.get("coordinates")); 088 if (coordinate == null) 089 return; 090 startElems("Point", "coordinates"); 091 writeCoordinate(coordinate); 092 endElem(); 093 endElem(); 094 } 095 096 private void writeLineString(JsonObject geoJson) throws Exception { 097 JsonElement elem = geoJson.get("coordinates"); 098 List<String> coordinates = getCoordinates(elem); 099 if (coordinates.isEmpty()) 100 return; 101 startElems("LineString", "coordinates"); 102 for (String coordinate : coordinates) 103 writeCoordinate(coordinate); 104 endElems(2); 105 } 106 107 private void writePolygon(JsonObject geoJson) throws Exception { 108 JsonElement elem = geoJson.get("coordinates"); 109 if (elem == null || !elem.isJsonArray()) 110 return; 111 JsonArray array = elem.getAsJsonArray(); 112 if (array.size() == 0) 113 return; 114 List<String> outerRing = getCoordinates(array.get(0)); 115 if (outerRing.isEmpty()) 116 return; 117 startElem("Polygon"); 118 startElems("outerBoundaryIs", "LinearRing", "coordinates"); 119 for (String coordinate : outerRing) 120 writeCoordinate(coordinate); 121 endElems(3); 122 if (array.size() > 1) { 123 List<String> innerRing = getCoordinates(array.get(1)); 124 startElems("innerBoundaryIs", "LinearRing", "coordinates"); 125 for (String coordinate : innerRing) 126 writeCoordinate(coordinate); 127 endElems(3); 128 } 129 endElem(); 130 } 131 132 private void writeGeometryCollection(JsonObject geoJson) throws Exception { 133 JsonElement elem = geoJson.get("geometries"); 134 if (elem == null || !elem.isJsonArray()) 135 return; 136 startElem("MultiGeometry"); 137 for (JsonElement geom : elem.getAsJsonArray()) { 138 if (!geom.isJsonObject()) 139 continue; 140 writeGeometry(geom.getAsJsonObject()); 141 } 142 endElem(); 143 } 144 145 private void writeCoordinate(String coordinate) throws XMLStreamException { 146 kml.writeCharacters("\n"); 147 for (int i = 0; i < indent; i++) 148 kml.writeCharacters(" "); 149 kml.writeCharacters(coordinate); 150 } 151 152 private List<String> getCoordinates(JsonElement elem) { 153 if (elem == null || !elem.isJsonArray()) 154 return Collections.emptyList(); 155 JsonArray array = elem.getAsJsonArray(); 156 List<String> coordinates = new ArrayList<>(); 157 for (JsonElement ce : array) { 158 String coordinate = getCoordinate(ce); 159 if (coordinate == null) 160 return Collections.emptyList(); 161 coordinates.add(coordinate); 162 } 163 return coordinates; 164 } 165 166 private String getCoordinate(JsonElement elem) { 167 if (elem == null || !elem.isJsonArray()) 168 return null; 169 JsonArray point = elem.getAsJsonArray(); 170 StringBuilder s = new StringBuilder(); 171 boolean first = true; 172 for (JsonElement num : point) { 173 if (!num.isJsonPrimitive()) 174 return null; 175 if (!first) 176 s.append(','); 177 else 178 first = false; 179 double d = num.getAsDouble(); 180 s.append(d); 181 } 182 return s.toString(); 183 } 184 185 private void startElems(String... names) throws Exception { 186 for (String name : names) 187 startElem(name); 188 } 189 190 private void startElem(String name) throws Exception { 191 kml.writeCharacters("\n"); 192 for (int i = 0; i < indent; i++) 193 kml.writeCharacters(" "); 194 kml.writeStartElement(name); 195 indent++; 196 } 197 198 private void endElems(int count) throws Exception { 199 for (int i = 0; i < count; i++) 200 endElem(); 201 } 202 203 private void endElem() throws Exception { 204 kml.writeCharacters("\n"); 205 indent--; 206 for (int i = 0; i < indent; i++) 207 kml.writeCharacters(" "); 208 kml.writeEndElement(); 209 } 210 211}