Work on GeoJSon ans JTS support
authorMathieu Baudier <mbaudier@argeo.org>
Fri, 29 Sep 2023 16:19:17 +0000 (18:19 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Fri, 29 Sep 2023 16:19:17 +0000 (18:19 +0200)
org.argeo.app.geo/src/org/argeo/app/geo/GeoJSon.java
org.argeo.app.geo/src/org/argeo/app/geo/JTS.java
org.argeo.app.geo/src/org/argeo/app/geo/http/WfsHttpHandler.java

index b590814ee2961113799111297ad60839691ac87f..5d5b99fdeb32efd6a29b84d665455e5170cc38e0 100644 (file)
@@ -8,6 +8,8 @@ import org.locationtech.jts.geom.LinearRing;
 import org.locationtech.jts.geom.Point;
 import org.locationtech.jts.geom.Polygon;
 
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
 import jakarta.json.stream.JsonGenerator;
 
 /**
@@ -16,31 +18,34 @@ import jakarta.json.stream.JsonGenerator;
  * @see https://datatracker.ietf.org/doc/html/rfc7946
  */
 public class GeoJSon {
-       public static void writeBBox(JsonGenerator generator, Geometry geometry) {
-               generator.writeStartArray("bbox");
-               Envelope envelope = geometry.getEnvelopeInternal();
-               generator.write(envelope.getMinX());
-               generator.write(envelope.getMinY());
-               generator.write(envelope.getMaxX());
-               generator.write(envelope.getMaxY());
-               generator.writeEnd();
-       }
+       public final static String POINT_TYPE = "Point";
+       public final static String LINE_STRING_TYPE = "LineString";
+       public final static String POLYGON_TYPE = "Polygon";
+
+       public final static String TYPE = "type";
+       public final static String GEOMETRY = "geometry";
+       public final static String COORDINATES = "coordinates";
+       public final static String BBOX = "bbox";
+       public final static String PROPERTIES = "properties";
 
+       /*
+        * WRITE
+        */
+       /** Writes a {@link Geometry} as GeoJSON. */
        public static void writeGeometry(JsonGenerator generator, Geometry geometry) {
-               generator.writeStartObject("geometry");
                if (geometry instanceof Point point) {
-                       generator.write("type", "Point");
-                       generator.writeStartArray("coordinates");
+                       generator.write(TYPE, POINT_TYPE);
+                       generator.writeStartArray(COORDINATES);
                        writeCoordinate(generator, point.getCoordinate());
                        generator.writeEnd();// coordinates array
                } else if (geometry instanceof LineString lineString) {
-                       generator.write("type", "LineString");
-                       generator.writeStartArray("coordinates");
+                       generator.write(TYPE, LINE_STRING_TYPE);
+                       generator.writeStartArray(COORDINATES);
                        writeCoordinates(generator, lineString.getCoordinates());
                        generator.writeEnd();// coordinates array
                } else if (geometry instanceof Polygon polygon) {
-                       generator.write("type", "Polygon");
-                       generator.writeStartArray("coordinates");
+                       generator.write(TYPE, POLYGON_TYPE);
+                       generator.writeStartArray(COORDINATES);
                        LinearRing exteriorRing = polygon.getExteriorRing();
                        generator.writeStartArray();
                        writeCoordinates(generator, exteriorRing.getCoordinates());
@@ -54,9 +59,9 @@ public class GeoJSon {
                        }
                        generator.writeEnd();// coordinates array
                }
-               generator.writeEnd();// geometry object
        }
 
+       /** Writes a sequence of coordinates [[lat,lon],[lat,lon]] */
        public static void writeCoordinates(JsonGenerator generator, Coordinate[] coordinates) {
                for (Coordinate coordinate : coordinates) {
                        generator.writeStartArray();
@@ -65,6 +70,7 @@ public class GeoJSon {
                }
        }
 
+       /** Writes a pair of coordinates [lat,lon]. */
        public static void writeCoordinate(JsonGenerator generator, Coordinate coordinate) {
                generator.write(coordinate.getX());
                generator.write(coordinate.getY());
@@ -74,6 +80,68 @@ public class GeoJSon {
                }
        }
 
+       /**
+        * Writes the {@link Envelope} of a {@link Geometry} as a bbox GeoJSON object.
+        */
+       public static void writeBBox(JsonGenerator generator, Geometry geometry) {
+               generator.writeStartArray(BBOX);
+               Envelope envelope = geometry.getEnvelopeInternal();
+               generator.write(envelope.getMinX());
+               generator.write(envelope.getMinY());
+               generator.write(envelope.getMaxX());
+               generator.write(envelope.getMaxY());
+               generator.writeEnd();
+       }
+
+       /*
+        * READ
+        */
+       /** Reads a geometry from the geometry object of a GEoJSON feature. */
+       @SuppressWarnings("unchecked")
+       public static <T extends Geometry> T readGeometry(JsonObject geom, Class<T> clss) {
+               String type = geom.getString(TYPE);
+               JsonArray coordinates = geom.getJsonArray(COORDINATES);
+               Geometry res = switch (type) {
+               case POINT_TYPE: {
+                       Coordinate coord = readCoordinate(coordinates);
+                       yield JTS.GEOMETRY_FACTORY_WGS84.createPoint(coord);
+               }
+               case LINE_STRING_TYPE: {
+                       Coordinate[] coords = readCoordinates(coordinates);
+                       yield JTS.GEOMETRY_FACTORY_WGS84.createLineString(coords);
+               }
+               case POLYGON_TYPE: {
+                       assert coordinates.size() > 0;
+                       LinearRing exterior = JTS.GEOMETRY_FACTORY_WGS84
+                                       .createLinearRing(readCoordinates(coordinates.getJsonArray(0)));
+                       LinearRing[] holes = new LinearRing[coordinates.size() - 1];
+                       for (int i = 0; i < coordinates.size() - 1; i++) {
+                               holes[i] = JTS.GEOMETRY_FACTORY_WGS84
+                                               .createLinearRing(readCoordinates(coordinates.getJsonArray(i + 1)));
+                       }
+                       yield JTS.GEOMETRY_FACTORY_WGS84.createPolygon(exterior, holes);
+               }
+               default:
+                       throw new IllegalArgumentException("Unexpected value: " + type);
+               };
+//             res.normalize();
+               return (T)res;
+       }
+
+       /** Reads a coordinate sequence [[lat,lon],[lat,lon]]. */
+       public static Coordinate readCoordinate(JsonArray arr) {
+               assert arr.size() >= 2;
+               return new Coordinate(arr.getJsonNumber(0).doubleValue(), arr.getJsonNumber(1).doubleValue());
+       }
+
+       /** Reads a coordinate pair [lat,lon]. */
+       public static Coordinate[] readCoordinates(JsonArray arr) {
+               Coordinate[] coords = new Coordinate[arr.size()];
+               for (int i = 0; i < arr.size(); i++)
+                       coords[i] = readCoordinate(arr.getJsonArray(i));
+               return coords;
+       }
+
        /** singleton */
        private GeoJSon() {
        }
index 26237120bb25cd80856d7c68a6962b1194d1bb73..ba7ecf9ba92172e36744254a3ddfac51d717747d 100644 (file)
@@ -2,6 +2,7 @@ package org.argeo.app.geo;
 
 import org.argeo.api.cms.CmsLog;
 import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.PrecisionModel;
 
 /**
  * Factories initialisation and workarounds for the JTS library. The idea is to
@@ -12,12 +13,17 @@ import org.locationtech.jts.geom.GeometryFactory;
 public class JTS {
        private final static CmsLog log = CmsLog.getLog(JTS.class);
 
+       public final static int WGS84_SRID = 4326;
+
+       /** A geometry factory with no SRID specified */
        public final static GeometryFactory GEOMETRY_FACTORY;
+       /** A geometry factory with SRID 4326 (WGS84 in the EPSG database) */
+       public final static GeometryFactory GEOMETRY_FACTORY_WGS84;
 
        static {
                try {
-                       // GEOMETRY_FACTORY = JTSFactoryFinder.getGeometryFactory();
                        GEOMETRY_FACTORY = new GeometryFactory();
+                       GEOMETRY_FACTORY_WGS84 = new GeometryFactory(new PrecisionModel(), WGS84_SRID);
                } catch (RuntimeException e) {
                        log.error("Basic JTS initialisation failed, geographical utilities are probably not available", e);
                        throw e;
index ae2940c576d8f08eb648add627897ad681fa7a97..4ab3654f456ffd1f9cd53703b22f618ff4d12244 100644 (file)
@@ -202,9 +202,11 @@ public class WfsHttpHandler implements HttpHandler {
                        if (featureId != null)
                                generator.write("id", featureId);
                        GeoJSon.writeBBox(generator, defaultGeometry);
+                       generator.writeStartObject(GeoJSon.GEOMETRY);
                        GeoJSon.writeGeometry(generator, defaultGeometry);
+                       generator.writeEnd();// geometry object
 
-                       generator.writeStartObject("properties");
+                       generator.writeStartObject(GeoJSon.PROPERTIES);
                        AcrJsonUtils.writeTimeProperties(generator, c);
                        if (featureAdapter != null)
                                featureAdapter.writeProperties(generator, c, typeName);