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

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

sonar - fix some recent warnings

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