]> git.argeo.org Git - gpl/argeo-suite.git/blob - geo/GeoJson.java
Prepare next development cycle
[gpl/argeo-suite.git] / geo / GeoJson.java
1 package org.argeo.app.geo;
2
3 import org.locationtech.jts.geom.Coordinate;
4 import org.locationtech.jts.geom.Envelope;
5 import org.locationtech.jts.geom.Geometry;
6 import org.locationtech.jts.geom.GeometryCollection;
7 import org.locationtech.jts.geom.LineString;
8 import org.locationtech.jts.geom.LinearRing;
9 import org.locationtech.jts.geom.MultiPoint;
10 import org.locationtech.jts.geom.Point;
11 import org.locationtech.jts.geom.Polygon;
12
13 import jakarta.json.JsonArray;
14 import jakarta.json.JsonObject;
15 import jakarta.json.stream.JsonGenerator;
16
17 /**
18 * GeoJSon format.
19 *
20 * @see https://datatracker.ietf.org/doc/html/rfc7946
21 */
22 public class GeoJson {
23 public final static String POINT_TYPE = "Point";
24 public final static String MULTI_POINT_TYPE = "MultiPoint";
25 public final static String LINE_STRING_TYPE = "LineString";
26 public final static String POLYGON_TYPE = "Polygon";
27 public final static String GEOMETRY_COLLECTION_TYPE = "GeometryCollection";
28
29 public final static String TYPE = "type";
30 public final static String GEOMETRY = "geometry";
31 public final static String GEOMETRIES = "geometries";
32 public final static String COORDINATES = "coordinates";
33 public final static String BBOX = "bbox";
34 public final static String PROPERTIES = "properties";
35
36 /*
37 * WRITE
38 */
39 /** Writes a {@link Geometry} as GeoJSON. */
40 public static void writeGeometry(JsonGenerator g, Geometry geometry) {
41 if (geometry instanceof Point point) {
42 g.write(TYPE, POINT_TYPE);
43 g.writeStartArray(COORDINATES);
44 writeCoordinate(g, point.getCoordinate());
45 g.writeEnd();// coordinates array
46 } else if (geometry instanceof MultiPoint multiPoint) {
47 g.write(TYPE, MULTI_POINT_TYPE);
48 g.writeStartArray(COORDINATES);
49 writeCoordinates(g, multiPoint.getCoordinates());
50 g.writeEnd();// coordinates array
51 } else if (geometry instanceof LineString lineString) {
52 g.write(TYPE, LINE_STRING_TYPE);
53 g.writeStartArray(COORDINATES);
54 writeCoordinates(g, lineString.getCoordinates());
55 g.writeEnd();// coordinates array
56 } else if (geometry instanceof Polygon polygon) {
57 g.write(TYPE, POLYGON_TYPE);
58 g.writeStartArray(COORDINATES);
59 LinearRing exteriorRing = polygon.getExteriorRing();
60 g.writeStartArray();
61 writeCoordinates(g, exteriorRing.getCoordinates());
62 g.writeEnd();
63 for (int i = 0; i < polygon.getNumInteriorRing(); i++) {
64 LinearRing interiorRing = polygon.getInteriorRingN(i);
65 // TODO verify that holes are clockwise
66 g.writeStartArray();
67 writeCoordinates(g, interiorRing.getCoordinates());
68 g.writeEnd();
69 }
70 g.writeEnd();// coordinates array
71 } else if (geometry instanceof GeometryCollection geometryCollection) {// must be last
72 g.write(TYPE, GEOMETRY_COLLECTION_TYPE);
73 g.writeStartArray(GEOMETRIES);
74 for (int i = 0; i < geometryCollection.getNumGeometries(); i++) {
75 g.writeStartObject();
76 writeGeometry(g, geometryCollection.getGeometryN(i));
77 g.writeEnd();// geometry object
78 }
79 g.writeEnd();// geometries array
80 } else {
81 throw new IllegalArgumentException(geometry.getClass() + " is not supported.");
82 }
83 }
84
85 /** Writes a sequence of coordinates [[lat,lon],[lat,lon]] */
86 public static void writeCoordinates(JsonGenerator g, Coordinate[] coordinates) {
87 for (Coordinate coordinate : coordinates) {
88 g.writeStartArray();
89 writeCoordinate(g, coordinate);
90 g.writeEnd();
91 }
92 }
93
94 /** Writes a pair of coordinates [lat,lon]. */
95 public static void writeCoordinate(JsonGenerator g, Coordinate coordinate) {
96 g.write(coordinate.getX());
97 g.write(coordinate.getY());
98 double z = coordinate.getZ();
99 if (!Double.isNaN(z)) {
100 g.write(z);
101 }
102 }
103
104 /**
105 * Writes the {@link Envelope} of a {@link Geometry} as a bbox GeoJSON object.
106 */
107 public static void writeBBox(JsonGenerator g, Geometry geometry) {
108 g.writeStartArray(BBOX);
109 Envelope envelope = geometry.getEnvelopeInternal();
110 g.write(envelope.getMinX());
111 g.write(envelope.getMinY());
112 g.write(envelope.getMaxX());
113 g.write(envelope.getMaxY());
114 g.writeEnd();
115 }
116
117 /*
118 * READ
119 */
120 /** Reads a geometry from the geometry object of a GEoJSON feature. */
121 @SuppressWarnings("unchecked")
122 public static <T extends Geometry> T readGeometry(JsonObject geom, Class<T> clss) {
123 String type = geom.getString(TYPE);
124 JsonArray coordinates = geom.getJsonArray(COORDINATES);
125 Geometry res = switch (type) {
126 case POINT_TYPE: {
127 Coordinate coord = readCoordinate(coordinates);
128 yield JTS.GEOMETRY_FACTORY_WGS84.createPoint(coord);
129 }
130 case LINE_STRING_TYPE: {
131 Coordinate[] coords = readCoordinates(coordinates);
132 yield JTS.GEOMETRY_FACTORY_WGS84.createLineString(coords);
133 }
134 case POLYGON_TYPE: {
135 assert coordinates.size() > 0;
136 LinearRing exterior = JTS.GEOMETRY_FACTORY_WGS84
137 .createLinearRing(readCoordinates(coordinates.getJsonArray(0)));
138 LinearRing[] holes = new LinearRing[coordinates.size() - 1];
139 for (int i = 0; i < coordinates.size() - 1; i++) {
140 holes[i] = JTS.GEOMETRY_FACTORY_WGS84
141 .createLinearRing(readCoordinates(coordinates.getJsonArray(i + 1)));
142 }
143 yield JTS.GEOMETRY_FACTORY_WGS84.createPolygon(exterior, holes);
144 }
145 default:
146 throw new IllegalArgumentException("Unexpected value: " + type);
147 };
148 // res.normalize();
149 return (T) res;
150 }
151
152 /** Reads a coordinate sequence [[lat,lon],[lat,lon]]. */
153 public static Coordinate readCoordinate(JsonArray arr) {
154 assert arr.size() >= 2;
155 return new Coordinate(arr.getJsonNumber(0).doubleValue(), arr.getJsonNumber(1).doubleValue());
156 }
157
158 /** Reads a coordinate pair [lat,lon]. */
159 public static Coordinate[] readCoordinates(JsonArray arr) {
160 Coordinate[] coords = new Coordinate[arr.size()];
161 for (int i = 0; i < arr.size(); i++)
162 coords[i] = readCoordinate(arr.getJsonArray(i));
163 return coords;
164 }
165
166 /** singleton */
167 private GeoJson() {
168 }
169
170 }