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

Last change on this file since 13071 was 12816, checked in by Don-vip, 7 years ago

see #15229 - see #15182 - remove GUI references from I/O subsystem

  • Property svn:eol-style set to native
File size: 8.2 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.OsmPrimitiveVisitor;
31import org.openstreetmap.josm.data.projection.Projection;
32import org.openstreetmap.josm.data.projection.Projections;
33import org.openstreetmap.josm.gui.layer.OsmDataLayer;
34import org.openstreetmap.josm.gui.mappaint.ElemStyles;
35import org.openstreetmap.josm.tools.Logging;
36import org.openstreetmap.josm.tools.Pair;
37
38/**
39 * Writes OSM data as a GeoJSON string, using JSR 353: Java API for JSON Processing (JSON-P).
40 * <p>
41 * See <a href="https://tools.ietf.org/html/rfc7946">RFC7946: The GeoJSON Format</a>
42 */
43public class GeoJSONWriter {
44
45 private final DataSet data;
46 private final Projection projection;
47 private static final boolean SKIP_EMPTY_NODES = true;
48
49 /**
50 * Constructs a new {@code GeoJSONWriter}.
51 * @param layer The OSM data layer to save
52 * @since 10852
53 * @deprecated To be removed end of 2017. Use {@link #GeoJSONWriter(DataSet)} instead
54 */
55 @Deprecated
56 public GeoJSONWriter(OsmDataLayer layer) {
57 this(layer.data);
58 }
59
60 /**
61 * Constructs a new {@code GeoJSONWriter}.
62 * @param ds The OSM data set to save
63 * @since 12806
64 */
65 public GeoJSONWriter(DataSet ds) {
66 this.data = ds;
67 this.projection = Projections.getProjectionByCode("EPSG:4326"); // WGS 84
68 }
69
70 /**
71 * Writes OSM data as a GeoJSON string (prettified).
72 * @return The GeoJSON data
73 */
74 public String write() {
75 return write(true);
76 }
77
78 /**
79 * Writes OSM data as a GeoJSON string (prettified or not).
80 * @param pretty {@code true} to have pretty output, {@code false} otherwise
81 * @return The GeoJSON data
82 * @since 6756
83 */
84 public String write(boolean pretty) {
85 StringWriter stringWriter = new StringWriter();
86 Map<String, Object> config = new HashMap<>(1);
87 config.put(JsonGenerator.PRETTY_PRINTING, pretty);
88 try (JsonWriter writer = Json.createWriterFactory(config).createWriter(stringWriter)) {
89 JsonObjectBuilder object = Json.createObjectBuilder()
90 .add("type", "FeatureCollection")
91 .add("generator", "JOSM");
92 appendLayerBounds(data, object);
93 appendLayerFeatures(data, object);
94 writer.writeObject(object.build());
95 return stringWriter.toString();
96 }
97 }
98
99 private class GeometryPrimitiveVisitor implements OsmPrimitiveVisitor {
100
101 private final JsonObjectBuilder geomObj;
102
103 GeometryPrimitiveVisitor(JsonObjectBuilder geomObj) {
104 this.geomObj = geomObj;
105 }
106
107 @Override
108 public void visit(Node n) {
109 geomObj.add("type", "Point");
110 LatLon ll = n.getCoor();
111 if (ll != null) {
112 geomObj.add("coordinates", getCoorArray(null, n.getCoor()));
113 }
114 }
115
116 @Override
117 public void visit(Way w) {
118 if (w != null) {
119 final JsonArrayBuilder array = getCoorsArray(w.getNodes());
120 if (w.isClosed() && ElemStyles.hasAreaElemStyle(w, false)) {
121 final JsonArrayBuilder container = Json.createArrayBuilder().add(array);
122 geomObj.add("type", "Polygon");
123 geomObj.add("coordinates", container);
124 } else {
125 geomObj.add("type", "LineString");
126 geomObj.add("coordinates", array);
127 }
128 }
129 }
130
131 @Override
132 public void visit(Relation r) {
133 if (r == null || !r.isMultipolygon() || r.hasIncompleteMembers()) {
134 return;
135 }
136 try {
137 final Pair<List<JoinedPolygon>, List<JoinedPolygon>> mp = MultipolygonBuilder.joinWays(r);
138 final JsonArrayBuilder polygon = Json.createArrayBuilder();
139 Stream.concat(mp.a.stream(), mp.b.stream())
140 .map(p -> getCoorsArray(p.getNodes())
141 // since first node is not duplicated as last node
142 .add(getCoorArray(null, p.getNodes().get(0).getCoor())))
143 .forEach(polygon::add);
144 geomObj.add("type", "MultiPolygon");
145 final JsonArrayBuilder multiPolygon = Json.createArrayBuilder().add(polygon);
146 geomObj.add("coordinates", multiPolygon);
147 } catch (MultipolygonBuilder.JoinedPolygonCreationException ex) {
148 Logging.warn("GeoJSON: Failed to export multipolygon {0}", r.getUniqueId());
149 Logging.warn(ex);
150 }
151 }
152 }
153
154 private JsonArrayBuilder getCoorArray(JsonArrayBuilder builder, LatLon c) {
155 return getCoorArray(builder, projection.latlon2eastNorth(c));
156 }
157
158 private static JsonArrayBuilder getCoorArray(JsonArrayBuilder builder, EastNorth c) {
159 return builder != null ? builder : Json.createArrayBuilder()
160 .add(BigDecimal.valueOf(c.getX()).setScale(11, RoundingMode.HALF_UP))
161 .add(BigDecimal.valueOf(c.getY()).setScale(11, RoundingMode.HALF_UP));
162 }
163
164 private JsonArrayBuilder getCoorsArray(Iterable<Node> nodes) {
165 final JsonArrayBuilder builder = Json.createArrayBuilder();
166 for (Node n : nodes) {
167 LatLon ll = n.getCoor();
168 if (ll != null) {
169 builder.add(getCoorArray(null, ll));
170 }
171 }
172 return builder;
173 }
174
175 protected void appendPrimitive(OsmPrimitive p, JsonArrayBuilder array) {
176 if (p.isIncomplete()) {
177 return;
178 } else if (SKIP_EMPTY_NODES && p instanceof Node && p.getKeys().isEmpty()) {
179 return;
180 }
181
182 // Properties
183 final JsonObjectBuilder propObj = Json.createObjectBuilder();
184 for (Entry<String, String> t : p.getKeys().entrySet()) {
185 propObj.add(t.getKey(), t.getValue());
186 }
187
188 // Geometry
189 final JsonObjectBuilder geomObj = Json.createObjectBuilder();
190 p.accept(new GeometryPrimitiveVisitor(geomObj));
191
192 // Build primitive JSON object
193 array.add(Json.createObjectBuilder()
194 .add("type", "Feature")
195 .add("properties", propObj)
196 .add("geometry", geomObj));
197 }
198
199 protected void appendLayerBounds(DataSet ds, JsonObjectBuilder object) {
200 if (ds != null) {
201 Iterator<Bounds> it = ds.getDataSourceBounds().iterator();
202 if (it.hasNext()) {
203 Bounds b = new Bounds(it.next());
204 while (it.hasNext()) {
205 b.extend(it.next());
206 }
207 appendBounds(b, object);
208 }
209 }
210 }
211
212 protected void appendBounds(Bounds b, JsonObjectBuilder object) {
213 if (b != null) {
214 JsonArrayBuilder builder = Json.createArrayBuilder();
215 getCoorArray(builder, b.getMin());
216 getCoorArray(builder, b.getMax());
217 object.add("bbox", builder);
218 }
219 }
220
221 protected void appendLayerFeatures(DataSet ds, JsonObjectBuilder object) {
222 JsonArrayBuilder array = Json.createArrayBuilder();
223 if (ds != null) {
224 ds.allNonDeletedPrimitives().forEach(p -> appendPrimitive(p, array));
225 }
226 object.add("features", array);
227 }
228}
Note: See TracBrowser for help on using the repository browser.