001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.io; 003 004import java.io.StringWriter; 005import java.util.HashMap; 006import java.util.Iterator; 007import java.util.Map; 008import java.util.Map.Entry; 009 010import javax.json.Json; 011import javax.json.JsonArrayBuilder; 012import javax.json.JsonObjectBuilder; 013import javax.json.JsonWriter; 014import javax.json.stream.JsonGenerator; 015 016import org.openstreetmap.josm.data.Bounds; 017import org.openstreetmap.josm.data.coor.LatLon; 018import org.openstreetmap.josm.data.osm.DataSet; 019import org.openstreetmap.josm.data.osm.INode; 020import org.openstreetmap.josm.data.osm.IRelation; 021import org.openstreetmap.josm.data.osm.IWay; 022import org.openstreetmap.josm.data.osm.Node; 023import org.openstreetmap.josm.data.osm.OsmPrimitive; 024import org.openstreetmap.josm.data.osm.Way; 025import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor; 026import org.openstreetmap.josm.gui.layer.OsmDataLayer; 027 028/** 029 * Writes OSM data as a GeoJSON string, using JSR 353: Java API for JSON Processing (JSON-P). 030 */ 031public class GeoJSONWriter { 032 033 private OsmDataLayer layer; 034 private static final boolean skipEmptyNodes = true; 035 036 /** 037 * Constructs a new {@code GeoJSONWriter}. 038 * @param layer The OSM data layer to save 039 */ 040 public GeoJSONWriter(OsmDataLayer layer) { 041 this.layer = layer; 042 } 043 044 /** 045 * Writes OSM data as a GeoJSON string (prettified). 046 * @return The GeoJSON data 047 */ 048 public String write() { 049 return write(true); 050 } 051 052 /** 053 * Writes OSM data as a GeoJSON string (prettified or not). 054 * @param pretty {@code true} to have pretty output, {@code false} otherwise 055 * @return The GeoJSON data 056 * @since 6756 057 */ 058 public String write(boolean pretty) { 059 StringWriter stringWriter = new StringWriter(); 060 Map<String, Object> config = new HashMap<>(1); 061 config.put(JsonGenerator.PRETTY_PRINTING, pretty); 062 try (JsonWriter writer = Json.createWriterFactory(config).createWriter(stringWriter)) { 063 JsonObjectBuilder object = Json.createObjectBuilder() 064 .add("type", "FeatureCollection") 065 .add("generator", "JOSM"); 066 appendLayerBounds(layer.data, object); 067 appendLayerFeatures(layer.data, object); 068 writer.writeObject(object.build()); 069 return stringWriter.toString(); 070 } 071 } 072 073 private static class GeometryPrimitiveVisitor implements PrimitiveVisitor { 074 075 private final JsonObjectBuilder geomObj; 076 077 public GeometryPrimitiveVisitor(JsonObjectBuilder geomObj) { 078 this.geomObj = geomObj; 079 } 080 081 @Override 082 public void visit(INode n) { 083 geomObj.add("type", "Point"); 084 LatLon ll = n.getCoor(); 085 if (ll != null) { 086 geomObj.add("coordinates", getCoorArray(n.getCoor())); 087 } 088 } 089 090 @Override 091 public void visit(IWay w) { 092 geomObj.add("type", "LineString"); 093 if (w instanceof Way) { 094 JsonArrayBuilder array = Json.createArrayBuilder(); 095 for (Node n : ((Way)w).getNodes()) { 096 LatLon ll = n.getCoor(); 097 if (ll != null) { 098 array.add(getCoorArray(ll)); 099 } 100 } 101 geomObj.add("coordinates", array); 102 } 103 } 104 105 @Override 106 public void visit(IRelation r) { 107 } 108 109 private JsonArrayBuilder getCoorArray(LatLon c) { 110 return Json.createArrayBuilder().add(c.lon()).add(c.lat()); 111 } 112 } 113 114 protected static void appendPrimitive(OsmPrimitive p, JsonArrayBuilder array) { 115 if (p.isIncomplete()) { 116 return; 117 } else if (skipEmptyNodes && p instanceof Node && p.getKeys().isEmpty()) { 118 return; 119 } 120 121 // Properties 122 final JsonObjectBuilder propObj = Json.createObjectBuilder(); 123 for (Entry<String, String> t : p.getKeys().entrySet()) { 124 propObj.add(t.getKey(), t.getValue()); 125 } 126 127 // Geometry 128 final JsonObjectBuilder geomObj = Json.createObjectBuilder(); 129 p.accept(new GeometryPrimitiveVisitor(geomObj)); 130 131 // Build primitive JSON object 132 array.add(Json.createObjectBuilder() 133 .add("type", "Feature") 134 .add("properties", propObj) 135 .add("geometry", geomObj)); 136 } 137 138 protected static void appendLayerBounds(DataSet ds, JsonObjectBuilder object) { 139 if (ds != null) { 140 Iterator<Bounds> it = ds.getDataSourceBounds().iterator(); 141 if (it.hasNext()) { 142 Bounds b = new Bounds(it.next()); 143 while (it.hasNext()) { 144 b.extend(it.next()); 145 } 146 appendBounds(b, object); 147 } 148 } 149 } 150 151 protected static void appendBounds(Bounds b, JsonObjectBuilder object) { 152 if (b != null) { 153 object.add("bbox", Json.createArrayBuilder() 154 .add(b.getMinLon()).add(b.getMinLat()) 155 .add(b.getMaxLon()).add(b.getMaxLat())); 156 } 157 } 158 159 protected static void appendLayerFeatures(DataSet ds, JsonObjectBuilder object) { 160 JsonArrayBuilder array = Json.createArrayBuilder(); 161 if (ds != null) { 162 for (Node n : ds.getNodes()) { 163 appendPrimitive(n, array); 164 } 165 for (Way w : ds.getWays()) { 166 appendPrimitive(w, array); 167 } 168 } 169 object.add("features", array); 170 } 171}