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

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

see #15310 - remove most of deprecated APIs

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