source: josm/trunk/src/org/openstreetmap/josm/io/GeoJSONReader.java@ 15427

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

fix recent SonarQube issues

  • Property svn:eol-style set to native
File size: 12.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.io;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.io.InputStream;
7import java.util.List;
8import java.util.Map;
9import java.util.Optional;
10import java.util.TreeMap;
11import java.util.stream.Collectors;
12
13import javax.json.Json;
14import javax.json.JsonArray;
15import javax.json.JsonObject;
16import javax.json.JsonString;
17import javax.json.JsonStructure;
18import javax.json.JsonValue;
19import javax.json.stream.JsonParser;
20import javax.json.stream.JsonParser.Event;
21
22import org.openstreetmap.josm.data.coor.LatLon;
23import org.openstreetmap.josm.data.osm.DataSet;
24import org.openstreetmap.josm.data.osm.Node;
25import org.openstreetmap.josm.data.osm.OsmPrimitive;
26import org.openstreetmap.josm.data.osm.Relation;
27import org.openstreetmap.josm.data.osm.RelationMember;
28import org.openstreetmap.josm.data.osm.Way;
29import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
30import org.openstreetmap.josm.gui.progress.ProgressMonitor;
31import org.openstreetmap.josm.tools.Logging;
32
33/**
34 * Reader that reads GeoJSON files. See <a href="https://tools.ietf.org/html/rfc7946">RFC7946</a> for more information.
35 * @since 15424
36 */
37public class GeoJSONReader extends AbstractReader {
38
39 private static final String COORDINATES = "coordinates";
40 private static final String FEATURES = "features";
41 private static final String PROPERTIES = "properties";
42 private static final String GEOMETRY = "geometry";
43 private static final String TYPE = "type";
44 private JsonParser parser;
45
46 GeoJSONReader() {
47 // Restricts visibility
48 }
49
50 private void setParser(final JsonParser parser) {
51 this.parser = parser;
52 }
53
54 private void parse() {
55 while (parser.hasNext()) {
56 Event event = parser.next();
57 if (event == Event.START_OBJECT) {
58 parseRoot(parser.getObject());
59 }
60 }
61 parser.close();
62 }
63
64 private void parseRoot(final JsonObject object) {
65 switch (object.getString(TYPE)) {
66 case "FeatureCollection":
67 parseFeatureCollection(object.getJsonArray(FEATURES));
68 break;
69 case "Feature":
70 parseFeature(object);
71 break;
72 case "GeometryCollection":
73 parseGeometryCollection(null, object);
74 break;
75 default:
76 parseGeometry(null, object);
77 }
78 }
79
80 private void parseFeatureCollection(final JsonArray features) {
81 for (JsonValue feature : features) {
82 if (feature instanceof JsonObject) {
83 JsonObject item = (JsonObject) feature;
84 parseFeature(item);
85 }
86 }
87 }
88
89 private void parseFeature(final JsonObject feature) {
90 JsonValue geometry = feature.get(GEOMETRY);
91 if (geometry != null && geometry.getValueType() == JsonValue.ValueType.OBJECT) {
92 parseGeometry(feature, geometry.asJsonObject());
93 } else {
94 JsonValue properties = feature.get(PROPERTIES);
95 if (properties != null && properties.getValueType() == JsonValue.ValueType.OBJECT) {
96 parseNonGeometryFeature(feature, properties.asJsonObject());
97 } else {
98 Logging.warn(tr("Relation/non-geometry feature without properties found: {0}", feature));
99 }
100 }
101 }
102
103 private void parseNonGeometryFeature(final JsonObject feature, final JsonObject properties) {
104 // get relation type
105 JsonValue type = properties.get(TYPE);
106 if (type == null || properties.getValueType() == JsonValue.ValueType.STRING) {
107 Logging.warn(tr("Relation/non-geometry feature without type found: {0}", feature));
108 return;
109 }
110
111 // create misc. non-geometry feature
112 final Relation relation = new Relation();
113 relation.put(TYPE, type.toString());
114 fillTagsFromFeature(feature, relation);
115 getDataSet().addPrimitive(relation);
116 }
117
118 private void parseGeometryCollection(final JsonObject feature, final JsonObject geometry) {
119 JsonArray geometries = geometry.getJsonArray("geometries");
120 for (JsonValue jsonValue : geometries) {
121 parseGeometry(feature, jsonValue.asJsonObject());
122 }
123 }
124
125 private void parseGeometry(final JsonObject feature, final JsonObject geometry) {
126 if (geometry == null) {
127 parseNullGeometry(feature);
128 return;
129 }
130
131 switch (geometry.getString(TYPE)) {
132 case "Point":
133 parsePoint(feature, geometry.getJsonArray(COORDINATES));
134 break;
135 case "MultiPoint":
136 parseMultiPoint(feature, geometry);
137 break;
138 case "LineString":
139 parseLineString(feature, geometry.getJsonArray(COORDINATES));
140 break;
141 case "MultiLineString":
142 parseMultiLineString(feature, geometry);
143 break;
144 case "Polygon":
145 parsePolygon(feature, geometry.getJsonArray(COORDINATES));
146 break;
147 case "MultiPolygon":
148 parseMultiPolygon(feature, geometry);
149 break;
150 case "GeometryCollection":
151 parseGeometryCollection(feature, geometry);
152 break;
153 default:
154 parseUnknown(geometry);
155 }
156 }
157
158 private void parsePoint(final JsonObject feature, final JsonArray coordinates) {
159 double lat = coordinates.getJsonNumber(1).doubleValue();
160 double lon = coordinates.getJsonNumber(0).doubleValue();
161 Node node = createNode(lat, lon);
162 fillTagsFromFeature(feature, node);
163 }
164
165 private void parseMultiPoint(final JsonObject feature, final JsonObject geometry) {
166 JsonArray coordinates = geometry.getJsonArray(COORDINATES);
167 for (JsonValue coordinate : coordinates) {
168 parsePoint(feature, coordinate.asJsonArray());
169 }
170 }
171
172 private void parseLineString(final JsonObject feature, final JsonArray coordinates) {
173 if (coordinates.isEmpty()) {
174 return;
175 }
176 createWay(coordinates, false)
177 .ifPresent(way -> fillTagsFromFeature(feature, way));
178 }
179
180 private void parseMultiLineString(final JsonObject feature, final JsonObject geometry) {
181 JsonArray coordinates = geometry.getJsonArray(COORDINATES);
182 for (JsonValue coordinate : coordinates) {
183 parseLineString(feature, coordinate.asJsonArray());
184 }
185 }
186
187 private void parsePolygon(final JsonObject feature, final JsonArray coordinates) {
188 if (coordinates.size() == 1) {
189 createWay(coordinates.getJsonArray(0), true)
190 .ifPresent(way -> fillTagsFromFeature(feature, way));
191 } else if (coordinates.size() > 1) {
192 // create multipolygon
193 final Relation multipolygon = new Relation();
194 multipolygon.put(TYPE, "multipolygon");
195 createWay(coordinates.getJsonArray(0), true)
196 .ifPresent(way -> multipolygon.addMember(new RelationMember("outer", way)));
197
198 for (JsonValue interiorRing : coordinates.subList(1, coordinates.size())) {
199 createWay(interiorRing.asJsonArray(), true)
200 .ifPresent(way -> multipolygon.addMember(new RelationMember("inner", way)));
201 }
202
203 fillTagsFromFeature(feature, multipolygon);
204 getDataSet().addPrimitive(multipolygon);
205 }
206 }
207
208 private void parseMultiPolygon(final JsonObject feature, final JsonObject geometry) {
209 JsonArray coordinates = geometry.getJsonArray(COORDINATES);
210 for (JsonValue coordinate : coordinates) {
211 parsePolygon(feature, coordinate.asJsonArray());
212 }
213 }
214
215 private Node createNode(final double lat, final double lon) {
216 final Node node = new Node(new LatLon(lat, lon));
217 getDataSet().addPrimitive(node);
218 return node;
219 }
220
221 private Optional<Way> createWay(final JsonArray coordinates, final boolean autoClose) {
222 if (coordinates.isEmpty()) {
223 return Optional.empty();
224 }
225
226 final List<LatLon> latlons = coordinates.stream().map(coordinate -> {
227 final JsonArray jsonValues = coordinate.asJsonArray();
228 return new LatLon(
229 jsonValues.getJsonNumber(1).doubleValue(),
230 jsonValues.getJsonNumber(0).doubleValue()
231 );
232 }).collect(Collectors.toList());
233
234 final int size = latlons.size();
235 final boolean doAutoclose;
236 if (size > 1) {
237 if (latlons.get(0).equals(latlons.get(size - 1))) {
238 // Remove last coordinate, but later add first node to the end
239 latlons.remove(size - 1);
240 doAutoclose = true;
241 } else {
242 doAutoclose = autoClose;
243 }
244 } else {
245 doAutoclose = false;
246 }
247
248 final Way way = new Way();
249 way.setNodes(latlons.stream().map(Node::new).collect(Collectors.toList()));
250 if (doAutoclose) {
251 way.addNode(way.getNode(0));
252 }
253
254 way.getNodes().stream().distinct().forEach(it -> getDataSet().addPrimitive(it));
255 getDataSet().addPrimitive(way);
256
257 return Optional.of(way);
258 }
259
260 private static void fillTagsFromFeature(final JsonObject feature, final OsmPrimitive primitive) {
261 if (feature != null) {
262 primitive.setKeys(getTags(feature));
263 }
264 }
265
266 private static void parseUnknown(final JsonObject object) {
267 Logging.warn(tr("Unknown json object found {0}", object));
268 }
269
270 private static void parseNullGeometry(JsonObject feature) {
271 Logging.warn(tr("Geometry of feature {0} is null", feature));
272 }
273
274 private static Map<String, String> getTags(final JsonObject feature) {
275 final Map<String, String> tags = new TreeMap<>();
276
277 if (feature.containsKey(PROPERTIES) && !feature.isNull(PROPERTIES)) {
278 JsonValue properties = feature.get(PROPERTIES);
279 if (properties != null && properties.getValueType() == JsonValue.ValueType.OBJECT) {
280 for (Map.Entry<String, JsonValue> stringJsonValueEntry : properties.asJsonObject().entrySet()) {
281 final JsonValue value = stringJsonValueEntry.getValue();
282
283 if (value instanceof JsonString) {
284 tags.put(stringJsonValueEntry.getKey(), ((JsonString) value).getString());
285 } else if (value instanceof JsonStructure) {
286 Logging.warn(
287 "The GeoJSON contains an object with property '" + stringJsonValueEntry.getKey()
288 + "' whose value has the unsupported type '" + value.getClass().getSimpleName()
289 + "'. That key-value pair is ignored!"
290 );
291 } else if (value.getValueType() != JsonValue.ValueType.NULL) {
292 tags.put(stringJsonValueEntry.getKey(), value.toString());
293 }
294 }
295 }
296 }
297 return tags;
298 }
299
300 @Override
301 protected DataSet doParseDataSet(InputStream source, ProgressMonitor progressMonitor) throws IllegalDataException {
302 setParser(Json.createParser(source));
303 parse();
304 return getDataSet();
305 }
306
307 /**
308 * Parse the given input source and return the dataset.
309 *
310 * @param source the source input stream. Must not be null.
311 * @param progressMonitor the progress monitor. If null, {@link NullProgressMonitor#INSTANCE} is assumed
312 * @return the dataset with the parsed data
313 * @throws IllegalDataException if an error was found while parsing the data from the source
314 * @throws IllegalArgumentException if source is null
315 */
316 public static DataSet parseDataSet(InputStream source, ProgressMonitor progressMonitor) throws IllegalDataException {
317 return new GeoJSONReader().doParseDataSet(source, progressMonitor);
318 }
319}
Note: See TracBrowser for help on using the repository browser.