source: josm/trunk/src/org/openstreetmap/josm/io/GeoJSONWriter.java@ 10841

Last change on this file since 10841 was 10817, checked in by simon04, 8 years ago

see #7307 - Enhance GeoJSON export

  • Save ways as LineString or Polygon depending on the area style.
  • Save multipolygons as MultiPolygon.
  • Property svn:eol-style set to native
File size: 7.6 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.io;
3
4import java.io.StringWriter;
5import java.math.BigDecimal;
6import java.math.RoundingMode;
7import java.util.HashMap;
8import java.util.Iterator;
9import java.util.List;
10import java.util.Map;
11import java.util.Map.Entry;
12import java.util.stream.Stream;
13
14import javax.json.Json;
15import javax.json.JsonArrayBuilder;
16import javax.json.JsonObjectBuilder;
17import javax.json.JsonWriter;
18import javax.json.stream.JsonGenerator;
19
20import org.openstreetmap.josm.data.Bounds;
21import org.openstreetmap.josm.data.coor.EastNorth;
22import org.openstreetmap.josm.data.coor.LatLon;
23import org.openstreetmap.josm.data.osm.DataSet;
24import org.openstreetmap.josm.data.osm.MultipolygonBuilder;
25import org.openstreetmap.josm.data.osm.MultipolygonBuilder.JoinedPolygon;
26import org.openstreetmap.josm.data.osm.Node;
27import org.openstreetmap.josm.data.osm.OsmPrimitive;
28import org.openstreetmap.josm.data.osm.Relation;
29import org.openstreetmap.josm.data.osm.Way;
30import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
31import org.openstreetmap.josm.data.projection.Projection;
32import org.openstreetmap.josm.gui.layer.OsmDataLayer;
33import org.openstreetmap.josm.gui.mappaint.ElemStyles;
34import org.openstreetmap.josm.tools.Pair;
35
36/**
37 * Writes OSM data as a GeoJSON string, using JSR 353: Java API for JSON Processing (JSON-P).
38 */
39public class GeoJSONWriter {
40
41 private final OsmDataLayer layer;
42 private final Projection projection;
43 private static final boolean skipEmptyNodes = true;
44
45 /**
46 * Constructs a new {@code GeoJSONWriter}.
47 * @param layer The OSM data layer to save
48 * @param projection The projection to use for coordinates
49 */
50 public GeoJSONWriter(OsmDataLayer layer, Projection projection) {
51 this.layer = layer;
52 this.projection = projection;
53 }
54
55 /**
56 * Writes OSM data as a GeoJSON string (prettified).
57 * @return The GeoJSON data
58 */
59 public String write() {
60 return write(true);
61 }
62
63 /**
64 * Writes OSM data as a GeoJSON string (prettified or not).
65 * @param pretty {@code true} to have pretty output, {@code false} otherwise
66 * @return The GeoJSON data
67 * @since 6756
68 */
69 public String write(boolean pretty) {
70 StringWriter stringWriter = new StringWriter();
71 Map<String, Object> config = new HashMap<>(1);
72 config.put(JsonGenerator.PRETTY_PRINTING, pretty);
73 try (JsonWriter writer = Json.createWriterFactory(config).createWriter(stringWriter)) {
74 JsonObjectBuilder object = Json.createObjectBuilder()
75 .add("type", "FeatureCollection")
76 .add("crs", Json.createObjectBuilder().add("type", "name").add(
77 "properties", Json.createObjectBuilder().add("name", projection.toCode())))
78 .add("generator", "JOSM");
79 appendLayerBounds(layer.data, object);
80 appendLayerFeatures(layer.data, object);
81 writer.writeObject(object.build());
82 return stringWriter.toString();
83 }
84 }
85
86 private class GeometryPrimitiveVisitor extends AbstractVisitor {
87
88 private final JsonObjectBuilder geomObj;
89
90 GeometryPrimitiveVisitor(JsonObjectBuilder geomObj) {
91 this.geomObj = geomObj;
92 }
93
94 @Override
95 public void visit(Node n) {
96 geomObj.add("type", "Point");
97 LatLon ll = n.getCoor();
98 if (ll != null) {
99 geomObj.add("coordinates", getCoorArray(null, n.getCoor()));
100 }
101 }
102
103 @Override
104 public void visit(Way w) {
105 if (w != null) {
106 final JsonArrayBuilder array = getCoorsArray(w.getNodes());
107 if (ElemStyles.hasAreaElemStyle(w, false)) {
108 final JsonArrayBuilder container = Json.createArrayBuilder().add(array);
109 geomObj.add("type", "Polygon");
110 geomObj.add("coordinates", container);
111 } else {
112 geomObj.add("type", "LineString");
113 geomObj.add("coordinates", array);
114 }
115 }
116 }
117
118 @Override
119 public void visit(Relation r) {
120 if (r != null && r.isMultipolygon() && !r.hasIncompleteMembers()) {
121 final Pair<List<JoinedPolygon>, List<JoinedPolygon>> mp = MultipolygonBuilder.joinWays(r);
122 final JsonArrayBuilder polygon = Json.createArrayBuilder();
123 Stream.concat(mp.a.stream(), mp.b.stream())
124 .map(p -> getCoorsArray(p.getNodes())
125 // since first node is not duplicated as last node
126 .add(getCoorArray(null, p.getNodes().get(0).getCoor())))
127 .forEach(polygon::add);
128 geomObj.add("type", "MultiPolygon");
129 final JsonArrayBuilder multiPolygon = Json.createArrayBuilder().add(polygon);
130 geomObj.add("coordinates", multiPolygon);
131 }
132 }
133 }
134
135 private JsonArrayBuilder getCoorArray(JsonArrayBuilder builder, LatLon c) {
136 return getCoorArray(builder, projection.latlon2eastNorth(c));
137 }
138
139 private static JsonArrayBuilder getCoorArray(JsonArrayBuilder builder, EastNorth c) {
140 return builder != null ? builder : Json.createArrayBuilder()
141 .add(BigDecimal.valueOf(c.getX()).setScale(11, RoundingMode.HALF_UP))
142 .add(BigDecimal.valueOf(c.getY()).setScale(11, RoundingMode.HALF_UP));
143 }
144
145 private JsonArrayBuilder getCoorsArray(Iterable<Node> nodes) {
146 final JsonArrayBuilder builder = Json.createArrayBuilder();
147 for (Node n : nodes) {
148 LatLon ll = n.getCoor();
149 if (ll != null) {
150 builder.add(getCoorArray(null, ll));
151 }
152 }
153 return builder;
154 }
155
156 protected void appendPrimitive(OsmPrimitive p, JsonArrayBuilder array) {
157 if (p.isIncomplete()) {
158 return;
159 } else if (skipEmptyNodes && p instanceof Node && p.getKeys().isEmpty()) {
160 return;
161 }
162
163 // Properties
164 final JsonObjectBuilder propObj = Json.createObjectBuilder();
165 for (Entry<String, String> t : p.getKeys().entrySet()) {
166 propObj.add(t.getKey(), t.getValue());
167 }
168
169 // Geometry
170 final JsonObjectBuilder geomObj = Json.createObjectBuilder();
171 p.accept(new GeometryPrimitiveVisitor(geomObj));
172
173 // Build primitive JSON object
174 array.add(Json.createObjectBuilder()
175 .add("type", "Feature")
176 .add("properties", propObj)
177 .add("geometry", geomObj));
178 }
179
180 protected void appendLayerBounds(DataSet ds, JsonObjectBuilder object) {
181 if (ds != null) {
182 Iterator<Bounds> it = ds.getDataSourceBounds().iterator();
183 if (it.hasNext()) {
184 Bounds b = new Bounds(it.next());
185 while (it.hasNext()) {
186 b.extend(it.next());
187 }
188 appendBounds(b, object);
189 }
190 }
191 }
192
193 protected void appendBounds(Bounds b, JsonObjectBuilder object) {
194 if (b != null) {
195 JsonArrayBuilder builder = Json.createArrayBuilder();
196 getCoorArray(builder, b.getMin());
197 getCoorArray(builder, b.getMax());
198 object.add("bbox", builder);
199 }
200 }
201
202 protected void appendLayerFeatures(DataSet ds, JsonObjectBuilder object) {
203 JsonArrayBuilder array = Json.createArrayBuilder();
204 if (ds != null) {
205 ds.allPrimitives().forEach(p -> appendPrimitive(p, array));
206 }
207 object.add("features", array);
208 }
209}
Note: See TracBrowser for help on using the repository browser.