1
2
3
4
5
6
7
8
9 package org.locationtech.spatial4j.io;
10
11 import org.locationtech.spatial4j.context.SpatialContext;
12 import org.locationtech.spatial4j.context.SpatialContextFactory;
13 import org.locationtech.spatial4j.distance.DistanceUtils;
14 import org.locationtech.spatial4j.exception.InvalidShapeException;
15 import org.locationtech.spatial4j.shape.Circle;
16 import org.locationtech.spatial4j.shape.Point;
17 import org.locationtech.spatial4j.shape.Shape;
18 import org.locationtech.spatial4j.shape.ShapeFactory;
19 import org.noggit.JSONParser;
20
21 import java.io.IOException;
22 import java.io.Reader;
23 import java.io.StringReader;
24 import java.text.ParseException;
25 import java.util.ArrayList;
26 import java.util.List;
27
28 public class GeoJSONReader implements ShapeReader {
29
30 protected static final String BUFFER = "buffer";
31 protected static final String BUFFER_UNITS = "buffer_units";
32
33 protected final SpatialContext ctx;
34 protected final ShapeFactory shapeFactory;
35
36 public GeoJSONReader(SpatialContext ctx, SpatialContextFactory factory) {
37 this.ctx = ctx;
38 this.shapeFactory = ctx.getShapeFactory();
39 }
40
41 @Override
42 public String getFormatName() {
43 return ShapeIO.GeoJSON;
44 }
45
46 @Override
47 public final Shape read(Reader reader) throws IOException, ParseException {
48 return readShape(new JSONParser(reader));
49 }
50
51 @Override
52 public Shape read(Object value) throws IOException, ParseException, InvalidShapeException {
53 String v = value.toString().trim();
54 return read(new StringReader(v));
55 }
56
57 @Override
58 public Shape readIfSupported(Object value) throws InvalidShapeException {
59 String v = value.toString().trim();
60 if (!(v.startsWith("{") && v.endsWith("}"))) {
61 return null;
62 }
63 try {
64 return read(new StringReader(v));
65 } catch (IOException ex) {
66 } catch (ParseException e) {
67 }
68 return null;
69 }
70
71
72
73
74
75
76 protected void readCoordXYZ(JSONParser parser, ShapeFactory.PointsBuilder pointsBuilder) throws IOException, ParseException {
77 assert (parser.lastEvent() == JSONParser.ARRAY_START);
78
79 double x = Double.NaN, y = Double.NaN, z = Double.NaN;
80 int idx = 0;
81
82 int evt = parser.nextEvent();
83 while (evt != JSONParser.EOF) {
84 switch (evt) {
85 case JSONParser.LONG:
86 case JSONParser.NUMBER:
87 case JSONParser.BIGNUMBER:
88 double value = parser.getDouble();
89 switch(idx) {
90 case 0: x = value; break;
91 case 1: y = value; break;
92 case 2: z = value; break;
93 }
94 idx++;
95 break;
96
97 case JSONParser.ARRAY_END:
98 if (idx <= 2) {
99 pointsBuilder.pointXY(shapeFactory.normX(x), shapeFactory.normY(y));
100 } else {
101 pointsBuilder.pointXYZ(shapeFactory.normX(x), shapeFactory.normY(y), shapeFactory.normZ(z));
102 }
103 return;
104
105 case JSONParser.STRING:
106 case JSONParser.BOOLEAN:
107 case JSONParser.NULL:
108 case JSONParser.OBJECT_START:
109 case JSONParser.OBJECT_END:
110 case JSONParser.ARRAY_START:
111 default:
112 throw new ParseException("Unexpected " + JSONParser.getEventString(evt),
113 (int) parser.getPosition());
114 }
115 evt = parser.nextEvent();
116 }
117 return;
118 }
119
120 protected void readCoordListXYZ(JSONParser parser, ShapeFactory.PointsBuilder pointsBuilder) throws IOException, ParseException {
121 assert (parser.lastEvent() == JSONParser.ARRAY_START);
122
123 int evt = parser.nextEvent();
124 while (evt != JSONParser.EOF) {
125 switch (evt) {
126 case JSONParser.ARRAY_START:
127 readCoordXYZ(parser, pointsBuilder);
128 break;
129
130 case JSONParser.ARRAY_END:
131 return;
132
133 default:
134 throw new ParseException("Unexpected " + JSONParser.getEventString(evt),
135 (int) parser.getPosition());
136 }
137 evt = parser.nextEvent();
138 }
139 }
140
141 protected void readUntilEvent(JSONParser parser, final int event) throws IOException {
142 int evt = parser.lastEvent();
143 while (true) {
144 if (evt == event || evt == JSONParser.EOF) {
145 return;
146 }
147 evt = parser.nextEvent();
148 }
149 }
150
151 protected Shape readPoint(JSONParser parser) throws IOException, ParseException {
152 assert (parser.lastEvent() == JSONParser.ARRAY_START);
153 OnePointsBuilder onePointsBuilder = new OnePointsBuilder(shapeFactory);
154 readCoordXYZ(parser, onePointsBuilder);
155 Point point = onePointsBuilder.getPoint();
156 readUntilEvent(parser, JSONParser.OBJECT_END);
157 return point;
158 }
159
160 protected Shape readLineString(JSONParser parser) throws IOException, ParseException {
161 assert (parser.lastEvent() == JSONParser.ARRAY_START);
162 ShapeFactory.LineStringBuilder builder = shapeFactory.lineString();
163 readCoordListXYZ(parser, builder);
164
165
166 builder.buffer(readDistance(BUFFER, BUFFER_UNITS, parser));
167
168 Shape out = builder.build();
169 readUntilEvent(parser, JSONParser.OBJECT_END);
170 return out;
171 }
172
173 protected Circle readCircle(JSONParser parser) throws IOException, ParseException {
174 assert (parser.lastEvent() == JSONParser.ARRAY_START);
175 OnePointsBuilder onePointsBuilder = new OnePointsBuilder(shapeFactory);
176 readCoordXYZ(parser, onePointsBuilder);
177 Point point = onePointsBuilder.getPoint();
178
179 return shapeFactory.circle(point, readDistance("radius", "radius_units", parser));
180 }
181
182
183
184
185
186
187
188
189
190 protected double readDistance(String distProperty, String distUnitsProperty, JSONParser parser) throws IOException {
191 double dist = 0;
192
193 String key = null;
194
195 int event = JSONParser.OBJECT_END;
196 int evt = parser.lastEvent();
197 while (true) {
198 if (evt == event || evt == JSONParser.EOF) {
199 break;
200 }
201 evt = parser.nextEvent();
202 if(parser.wasKey()) {
203 key = parser.getString();
204 }
205 else if(evt==JSONParser.NUMBER || evt==JSONParser.LONG) {
206 if(distProperty.equals(key)) {
207 dist = parser.getDouble();
208 }
209 }
210 else if(evt==JSONParser.STRING) {
211 if(distUnitsProperty.equals(key)) {
212 String units = parser.getString();
213
214 if("km".equals(units)) {
215
216 dist =
217 DistanceUtils.dist2Degrees(dist, DistanceUtils.EARTH_MEAN_RADIUS_KM);
218 }
219 }
220 }
221 }
222
223 return shapeFactory.normDist(dist);
224 }
225
226 protected Shape readShape(JSONParser parser) throws IOException, ParseException {
227 String type = null;
228
229 String key = null;
230 int evt = parser.nextEvent();
231 while (evt != JSONParser.EOF) {
232 switch (evt) {
233 case JSONParser.STRING:
234 if (parser.wasKey()) {
235 key = parser.getString();
236 } else {
237 if ("type".equals(key)) {
238 type = parser.getString();
239 } else {
240 throw new ParseException("Unexpected String Value for key: " + key,
241 (int) parser.getPosition());
242 }
243 }
244 break;
245
246 case JSONParser.ARRAY_START:
247 if ("coordinates".equals(key)) {
248 Shape shape = readShapeFromCoordinates(type, parser);
249 readUntilEvent(parser, JSONParser.OBJECT_END);
250 return shape;
251 } else if ("geometries".equals(key)) {
252 List<Shape> shapes = new ArrayList<>();
253 int sub = parser.nextEvent();
254 while (sub != JSONParser.EOF) {
255 if (sub == JSONParser.OBJECT_START) {
256 Shape s = readShape(parser);
257 if (s != null) {
258 shapes.add(s);
259 }
260 } else if (sub == JSONParser.OBJECT_END) {
261 break;
262 }
263 sub = parser.nextEvent();
264 }
265 return ctx.makeCollection(shapes);
266 }
267 else {
268 throw new ParseException("Unknown type: "+type,
269 (int) parser.getPosition());
270 }
271
272 case JSONParser.ARRAY_END:
273 break;
274
275 case JSONParser.OBJECT_START:
276 if (key != null) {
277
278 }
279 break;
280
281 case JSONParser.LONG:
282 case JSONParser.NUMBER:
283 case JSONParser.BIGNUMBER:
284 case JSONParser.BOOLEAN:
285 case JSONParser.NULL:
286 case JSONParser.OBJECT_END:
287
288 break;
289
290 default:
291 throw new ParseException("Unexpected " + JSONParser.getEventString(evt),
292 (int) parser.getPosition());
293 }
294 evt = parser.nextEvent();
295 }
296 throw new RuntimeException("unable to parse shape");
297 }
298
299 protected Shape readShapeFromCoordinates(String type, JSONParser parser) throws IOException, ParseException {
300 switch(type) {
301 case "Point":
302 return readPoint(parser);
303 case "LineString":
304 return readLineString(parser);
305 case "Circle":
306 return readCircle(parser);
307 case "Polygon":
308 return readPolygon(parser, shapeFactory.polygon()).buildOrRect();
309 case "MultiPoint":
310 return readMultiPoint(parser);
311 case "MultiLineString":
312 return readMultiLineString(parser);
313 case "MultiPolygon":
314 return readMultiPolygon(parser);
315 default:
316 throw new ParseException("Unable to make shape type: " + type,
317 (int) parser.getPosition());
318 }
319 }
320
321 protected ShapeFactory.PolygonBuilder readPolygon(JSONParser parser, ShapeFactory.PolygonBuilder polygonBuilder) throws IOException, ParseException {
322 assert (parser.lastEvent() == JSONParser.ARRAY_START);
323 boolean firstRing = true;
324 int evt = parser.nextEvent();
325 while (true) {
326 switch (evt) {
327 case JSONParser.ARRAY_START:
328 if (firstRing) {
329 readCoordListXYZ(parser, polygonBuilder);
330 firstRing = false;
331 } else {
332 ShapeFactory.PolygonBuilder.HoleBuilder holeBuilder = polygonBuilder.hole();
333 readCoordListXYZ(parser, holeBuilder);
334 holeBuilder.endHole();
335 }
336 break;
337 case JSONParser.ARRAY_END:
338 return polygonBuilder;
339 default:
340 throw new ParseException("Unexpected " + JSONParser.getEventString(evt),
341 (int) parser.getPosition());
342 }
343 evt = parser.nextEvent();
344 }
345 }
346
347 protected Shape readMultiPoint(JSONParser parser) throws IOException, ParseException {
348 assert (parser.lastEvent() == JSONParser.ARRAY_START);
349 ShapeFactory.MultiPointBuilder builder = shapeFactory.multiPoint();
350 readCoordListXYZ(parser, builder);
351 return builder.build();
352 }
353
354 protected Shape readMultiLineString(JSONParser parser) throws IOException, ParseException {
355 assert (parser.lastEvent() == JSONParser.ARRAY_START);
356
357 ShapeFactory.MultiLineStringBuilder builder = shapeFactory.multiLineString();
358 int evt = parser.nextEvent();
359 while (true) {
360 switch (evt) {
361 case JSONParser.ARRAY_START:
362 ShapeFactory.LineStringBuilder lineStringBuilder = builder.lineString();
363 readCoordListXYZ(parser, lineStringBuilder);
364 builder.add(lineStringBuilder);
365 break;
366 case JSONParser.ARRAY_END:
367 return builder.build();
368 default:
369 throw new ParseException("Unexpected " + JSONParser.getEventString(evt),
370 (int) parser.getPosition());
371 }
372 evt = parser.nextEvent();
373 }
374 }
375
376 protected Shape readMultiPolygon(JSONParser parser) throws IOException, ParseException {
377 assert (parser.lastEvent() == JSONParser.ARRAY_START);
378
379 ShapeFactory.MultiPolygonBuilder builder = shapeFactory.multiPolygon();
380 int evt = parser.nextEvent();
381 while (true) {
382 switch (evt) {
383 case JSONParser.ARRAY_START:
384 ShapeFactory.PolygonBuilder polygonBuilder = readPolygon(parser, builder.polygon());
385 builder.add(polygonBuilder);
386 break;
387 case JSONParser.ARRAY_END:
388 return builder.build();
389 default:
390 throw new ParseException("Unexpected " + JSONParser.getEventString(evt),
391 (int) parser.getPosition());
392 }
393 evt = parser.nextEvent();
394 }
395 }
396 }