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

Last change on this file since 14397 was 14154, checked in by Don-vip, 6 years ago

see #15229 - fix warnings

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