1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.bremersee.geojson;
18
19 import static java.util.Objects.isNull;
20
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.InputStreamReader;
24 import java.io.Reader;
25 import java.math.BigDecimal;
26 import java.nio.charset.Charset;
27 import java.nio.charset.StandardCharsets;
28 import java.util.Arrays;
29 import java.util.Collection;
30 import java.util.Collections;
31 import java.util.Optional;
32 import org.bremersee.geojson.model.LatLon;
33 import org.bremersee.geojson.model.LatLonAware;
34 import org.bremersee.geojson.model.LatitudeLongitude;
35 import org.locationtech.jts.geom.Coordinate;
36 import org.locationtech.jts.geom.CoordinateFilter;
37 import org.locationtech.jts.geom.CoordinateSequenceFactory;
38 import org.locationtech.jts.geom.Geometry;
39 import org.locationtech.jts.geom.GeometryCollection;
40 import org.locationtech.jts.geom.GeometryFactory;
41 import org.locationtech.jts.geom.LineString;
42 import org.locationtech.jts.geom.LinearRing;
43 import org.locationtech.jts.geom.MultiLineString;
44 import org.locationtech.jts.geom.MultiPoint;
45 import org.locationtech.jts.geom.MultiPolygon;
46 import org.locationtech.jts.geom.Point;
47 import org.locationtech.jts.geom.Polygon;
48 import org.locationtech.jts.geom.PrecisionModel;
49 import org.locationtech.jts.io.ParseException;
50 import org.locationtech.jts.io.WKTReader;
51
52
53
54
55
56
57 public class GeoJsonGeometryFactory extends GeometryFactory {
58
59
60
61
62
63
64
65
66 public static Coordinate createCoordinate(double x, double y) {
67 return new Coordinate(x, y);
68 }
69
70
71
72
73
74
75
76
77
78 public static Coordinate createCoordinate(BigDecimal x, BigDecimal y) {
79
80 return Optional.ofNullable(x)
81 .map(xx -> Optional.ofNullable(y)
82 .map(yy -> new Coordinate(xx.doubleValue(), yy.doubleValue()))
83 .orElseThrow(() -> new IllegalArgumentException("Y must not be null.")))
84 .orElseThrow(() -> new IllegalArgumentException("X must not be null."));
85 }
86
87
88
89
90
91
92
93 public static Coordinate createCoordinate(LatLonAware latLon) {
94 if (isNull(latLon)) {
95 return null;
96 }
97 return createCoordinate(latLon.getLongitude(), latLon.getLatitude());
98 }
99
100
101
102
103
104
105
106
107 public GeoJsonGeometryFactory(PrecisionModel precisionModel, int srid,
108 CoordinateSequenceFactory coordinateSequenceFactory) {
109 super(precisionModel, srid, coordinateSequenceFactory);
110 }
111
112
113
114
115
116
117 public GeoJsonGeometryFactory(
118 CoordinateSequenceFactory coordinateSequenceFactory) {
119 super(coordinateSequenceFactory);
120 }
121
122
123
124
125
126
127 public GeoJsonGeometryFactory(PrecisionModel precisionModel) {
128 super(precisionModel);
129 }
130
131
132
133
134
135
136
137 public GeoJsonGeometryFactory(PrecisionModel precisionModel, int srid) {
138 super(precisionModel, srid);
139 }
140
141
142
143
144 public GeoJsonGeometryFactory() {
145 }
146
147
148
149
150
151
152
153
154 public Point createPoint(double x, double y) {
155 return createPoint(createCoordinate(x, y));
156 }
157
158
159
160
161
162
163
164
165 public Point createPoint(BigDecimal x, BigDecimal y) {
166 return createPoint(createCoordinate(x, y));
167 }
168
169
170
171
172
173
174
175 public Point createPoint(LatLonAware latLon) {
176 if (isNull(latLon)) {
177 return null;
178 }
179 return createPoint(createCoordinate(latLon));
180 }
181
182
183
184
185
186
187
188
189 public LineString createLineString(Collection<? extends Coordinate> coordinates) {
190 return Optional.ofNullable(coordinates)
191 .map(c -> c.toArray(new Coordinate[0]))
192 .map(this::createLineString)
193 .orElseGet(this::createLineString);
194 }
195
196
197
198
199
200
201
202
203 public LinearRing createLinearRing(Collection<? extends Coordinate> coordinates) {
204
205 return Optional.ofNullable(coordinates)
206 .map(c -> c.toArray(new Coordinate[0]))
207 .map(this::createLinearRing)
208 .orElseGet(this::createLinearRing);
209 }
210
211
212
213
214
215
216
217
218
219
220 public Polygon createPolygon(
221 LinearRing shell,
222 Collection<? extends LinearRing> holes) {
223
224 return Optional.ofNullable(shell)
225 .map(s -> Optional.ofNullable(holes)
226 .map(c -> c.toArray(new LinearRing[0]))
227 .map(a -> createPolygon(s, a))
228 .orElseGet(() -> createPolygon(s)))
229 .orElseGet(this::createPolygon);
230 }
231
232
233
234
235
236
237
238
239 public MultiPoint createMultiPoint(Collection<? extends Point> points) {
240 return Optional.ofNullable(points)
241 .map(c -> c.toArray(new Point[0]))
242 .map(this::createMultiPoint)
243 .orElseGet(this::createMultiPoint);
244 }
245
246
247
248
249
250
251
252
253 public MultiLineString createMultiLineString(Collection<? extends LineString> lineStrings) {
254
255 return Optional.ofNullable(lineStrings)
256 .map(c -> c.toArray(new LineString[0]))
257 .map(this::createMultiLineString)
258 .orElseGet(this::createMultiLineString);
259 }
260
261
262
263
264
265
266
267
268 public MultiPolygon createMultiPolygon(Collection<? extends Polygon> polygons) {
269
270 return Optional.ofNullable(polygons)
271 .map(c -> c.toArray(new Polygon[0]))
272 .map(this::createMultiPolygon)
273 .orElseGet(this::createMultiPolygon);
274 }
275
276
277
278
279
280
281
282 public GeometryCollection createGeometryCollection(Collection<? extends Geometry> geometries) {
283
284 return Optional.ofNullable(geometries)
285 .map(g -> g.toArray(new Geometry[0]))
286 .map(this::createGeometryCollection)
287 .orElseGet(this::createGeometryCollection);
288 }
289
290
291
292
293
294
295
296 public static LatLon createLatLon(Coordinate coordinate) {
297 if (isNull(coordinate)) {
298 return null;
299 }
300 return new LatLon(
301 BigDecimal.valueOf(coordinate.getY()),
302 BigDecimal.valueOf(coordinate.getX()));
303 }
304
305
306
307
308
309
310
311 public static LatLon createLatLon(Point point) {
312 if (isNull(point)) {
313 return null;
314 }
315 return createLatLon(point.getCoordinate());
316 }
317
318
319
320
321
322
323
324 public static LatitudeLongitude createLatitudeLongitude(Coordinate coordinate) {
325 if (isNull(coordinate)) {
326 return null;
327 }
328 return new LatitudeLongitude(
329 BigDecimal.valueOf(coordinate.getY()),
330 BigDecimal.valueOf(coordinate.getX()));
331 }
332
333
334
335
336
337
338
339 public static LatitudeLongitude createLatitudeLongitude(Point point) {
340 if (isNull(point)) {
341 return null;
342 }
343 return createLatitudeLongitude(point.getCoordinate());
344 }
345
346
347
348
349
350
351
352
353
354
355
356 public static boolean equals(Geometry g1, Geometry g2) {
357 if (isNull(g1) && isNull(g2)) {
358 return true;
359 }
360 if (isNull(g1) || isNull(g2)) {
361 return false;
362 }
363 if (g1 == g2) {
364 return true;
365 }
366 if (g1 instanceof GeometryCollection && g2 instanceof GeometryCollection) {
367 return equals((GeometryCollection) g1, (GeometryCollection) g2);
368 }
369 if (g1 instanceof GeometryCollection || g2 instanceof GeometryCollection) {
370 return false;
371 }
372 return g1.equals(g2);
373 }
374
375
376
377
378
379
380
381
382 private static boolean equals(GeometryCollection gc1, GeometryCollection gc2) {
383 if (gc1.getNumGeometries() != gc2.getNumGeometries()) {
384 return false;
385 }
386 for (int i = 0; i < gc1.getNumGeometries(); i++) {
387 Geometry g1 = gc1.getGeometryN(i);
388 Geometry g2 = gc2.getGeometryN(i);
389 if (!equals(g1, g2)) {
390 return false;
391 }
392 }
393 return true;
394 }
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409 public static double[] getBoundingBox(Geometry geometry) {
410 return Optional.ofNullable(geometry)
411 .map(g -> getBoundingBox(Collections.singletonList(g)))
412 .orElse(null);
413 }
414
415
416
417
418
419
420
421 @SuppressWarnings("ForLoopReplaceableByForEach")
422 public static double[] getBoundingBox(Collection<? extends Geometry> geometries) {
423 if (isNull(geometries) || geometries.isEmpty()) {
424 return null;
425 }
426 double minX = Double.NaN;
427 double minY = Double.NaN;
428 double minZ = Double.NaN;
429 double maxX = Double.NaN;
430 double maxY = Double.NaN;
431 double maxZ = Double.NaN;
432 for (Geometry geometry : geometries) {
433 if (geometry != null && geometry.getCoordinates() != null) {
434 Coordinate[] coords = geometry.getCoordinates();
435 for (int i = 0; i < coords.length; i++) {
436 if (Double.isNaN(minX)) {
437 minX = coords[i].getX();
438 } else if (!Double.isNaN(coords[i].getX())) {
439 minX = Math.min(minX, coords[i].getX());
440 }
441 if (Double.isNaN(minY)) {
442 minY = coords[i].getY();
443 } else if (!Double.isNaN(coords[i].getY())) {
444 minY = Math.min(minY, coords[i].getY());
445 }
446 if (Double.isNaN(minZ)) {
447 minZ = coords[i].getZ();
448 } else if (!Double.isNaN(coords[i].getZ())) {
449 minZ = Math.min(minZ, coords[i].getZ());
450 }
451
452 if (Double.isNaN(maxX)) {
453 maxX = coords[i].getX();
454 } else if (!Double.isNaN(coords[i].getX())) {
455 maxX = Math.max(maxX, coords[i].getX());
456 }
457 if (Double.isNaN(maxY)) {
458 maxY = coords[i].getY();
459 } else if (!Double.isNaN(coords[i].getY())) {
460 maxY = Math.max(maxY, coords[i].getY());
461 }
462 if (Double.isNaN(maxZ)) {
463 maxZ = coords[i].getZ();
464 } else if (!Double.isNaN(coords[i].getZ())) {
465 maxZ = Math.max(maxZ, coords[i].getZ());
466 }
467 }
468 }
469 }
470 if (!Double.isNaN(minX) && !Double.isNaN(maxX) && !Double.isNaN(minY) && !Double.isNaN(maxY)) {
471 if (!Double.isNaN(minZ) && !Double.isNaN(maxZ)) {
472 return new double[]{minX, minY, minZ, maxX, maxY, maxZ};
473 }
474 return new double[]{minX, minY, maxX, maxY};
475 }
476 return null;
477 }
478
479
480
481
482
483
484
485 public static Coordinate getSouthWest(double[] boundingBox) {
486 if (boundingBox == null || !(boundingBox.length == 4 || boundingBox.length == 6)) {
487 return null;
488 }
489 return createCoordinate(boundingBox[0], boundingBox[1]);
490 }
491
492
493
494
495
496
497
498 public static Coordinate getNorthWest(double[] boundingBox) {
499 if (boundingBox == null || !(boundingBox.length == 4 || boundingBox.length == 6)) {
500 return null;
501 }
502 if (boundingBox.length == 6) {
503
504
505 return createCoordinate(boundingBox[0], boundingBox[4]);
506 } else {
507
508
509 return createCoordinate(boundingBox[0], boundingBox[3]);
510 }
511 }
512
513
514
515
516
517
518
519 public static Coordinate getNorthEast(double[] boundingBox) {
520 if (boundingBox == null || !(boundingBox.length == 4 || boundingBox.length == 6)) {
521 return null;
522 }
523 if (boundingBox.length == 6) {
524
525
526 return createCoordinate(boundingBox[3], boundingBox[4]);
527 } else {
528
529
530 return createCoordinate(boundingBox[2], boundingBox[3]);
531 }
532 }
533
534
535
536
537
538
539
540 public static Coordinate getSouthEast(double[] boundingBox) {
541 if (boundingBox == null || !(boundingBox.length == 4 || boundingBox.length == 6)) {
542 return null;
543 }
544 if (boundingBox.length == 6) {
545
546
547 return createCoordinate(boundingBox[3], boundingBox[1]);
548 } else {
549
550
551 return createCoordinate(boundingBox[2], boundingBox[1]);
552 }
553 }
554
555
556
557
558
559
560
561 public Polygon getBoundingBoxAsPolygon2D(double[] boundingBox) {
562
563 Coordinate sw = getSouthWest(boundingBox);
564 Coordinate se = getSouthEast(boundingBox);
565 Coordinate ne = getNorthEast(boundingBox);
566 Coordinate nw = getNorthWest(boundingBox);
567 if (isNull(sw) || isNull(se) || isNull(ne) || isNull(nw)) {
568 return null;
569 }
570 float x1 = (float) sw.getX();
571 float x2 = (float) se.getX();
572 if (x1 == x2) {
573 return null;
574 }
575 float y1 = (float) sw.getY();
576 float y2 = (float) nw.getY();
577 if (y1 == y2) {
578 return null;
579 }
580 return createPolygon(new Coordinate[]{sw, se, ne, nw, sw});
581 }
582
583
584
585
586
587
588
589 public Polygon getBoundingBoxAsPolygon2D(Geometry geometry) {
590 return getBoundingBoxAsPolygon2D(getBoundingBox(geometry));
591 }
592
593
594
595
596
597
598
599
600
601 public Geometry createGeometryFromWellKnownText(String wkt) throws IllegalArgumentException {
602 try {
603 return new WKTReader(this).read(wkt);
604 } catch (NullPointerException n) {
605 return null;
606 } catch (ParseException e) {
607 throw new IllegalArgumentException(String.format("Parsing WKT [%s] failed.", wkt), e);
608 }
609 }
610
611
612
613
614
615
616
617
618
619
620 public Geometry createGeometryFromWellKnownText(Reader reader)
621 throws IllegalArgumentException, IOException {
622
623 try (Reader r = reader) {
624 return new WKTReader(this).read(r);
625 } catch (NullPointerException n) {
626 return null;
627 } catch (ParseException e) {
628 throw new IllegalArgumentException(e);
629 }
630 }
631
632
633
634
635
636
637
638
639
640
641
642 public Geometry createGeometryFromWellKnownText(
643 InputStream inputStream,
644 Charset charset) throws IllegalArgumentException, IOException {
645
646 Charset cs = isNull(charset) ? StandardCharsets.UTF_8 : charset;
647 try (InputStreamReader reader = new InputStreamReader(inputStream, cs)) {
648 return new WKTReader(this).read(reader);
649
650 } catch (NullPointerException n) {
651 return null;
652 } catch (ParseException e) {
653 throw new IllegalArgumentException(e);
654 }
655 }
656
657
658
659
660
661
662
663
664 public static Geometry copyAndApplyFilters(
665 Geometry geometry,
666 CoordinateFilter... filters) {
667
668 Geometry result;
669 if (isNull(geometry) || isNull(filters) || filters.length == 0) {
670 result = geometry;
671 } else {
672 result = geometry.copy();
673 Arrays.stream(filters).forEach(result::apply);
674 }
675 return result;
676 }
677
678
679
680
681
682
683
684
685 public static Geometry copyAndApplyFilters(
686 Geometry geometry,
687 Collection<? extends CoordinateFilter> filters) {
688
689 Geometry result;
690 if (isNull(geometry) || isNull(filters) || filters.isEmpty()) {
691 result = geometry;
692 } else {
693 result = geometry.copy();
694 filters.forEach(result::apply);
695 }
696 return result;
697 }
698
699 }