X-Git-Url: http://git.argeo.org/?a=blobdiff_plain;f=org.argeo.app.geo%2Fsrc%2Forg%2Fargeo%2Fapp%2Fgeo%2FGeoJson.java;fp=org.argeo.app.geo%2Fsrc%2Forg%2Fargeo%2Fapp%2Fgeo%2FGeoJson.java;h=c1ba1d393b71627ce70632e50253344da273d779;hb=b96d17f3eb275a97109cc160db1cfd3731a01d31;hp=0000000000000000000000000000000000000000;hpb=8b7dc8398a17d09c5e20857b4e1e5f82ad28e769;p=gpl%2Fargeo-suite.git diff --git a/org.argeo.app.geo/src/org/argeo/app/geo/GeoJson.java b/org.argeo.app.geo/src/org/argeo/app/geo/GeoJson.java new file mode 100644 index 0000000..c1ba1d3 --- /dev/null +++ b/org.argeo.app.geo/src/org/argeo/app/geo/GeoJson.java @@ -0,0 +1,170 @@ +package org.argeo.app.geo; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Envelope; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryCollection; +import org.locationtech.jts.geom.LineString; +import org.locationtech.jts.geom.LinearRing; +import org.locationtech.jts.geom.MultiPoint; +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; + +/** + * GeoJSon format. + * + * @see https://datatracker.ietf.org/doc/html/rfc7946 + */ +public class GeoJson { + public final static String POINT_TYPE = "Point"; + public final static String MULTI_POINT_TYPE = "MultiPoint"; + public final static String LINE_STRING_TYPE = "LineString"; + public final static String POLYGON_TYPE = "Polygon"; + public final static String GEOMETRY_COLLECTION_TYPE = "GeometryCollection"; + + public final static String TYPE = "type"; + public final static String GEOMETRY = "geometry"; + public final static String GEOMETRIES = "geometries"; + 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 g, Geometry geometry) { + if (geometry instanceof Point point) { + g.write(TYPE, POINT_TYPE); + g.writeStartArray(COORDINATES); + writeCoordinate(g, point.getCoordinate()); + g.writeEnd();// coordinates array + } else if (geometry instanceof MultiPoint multiPoint) { + g.write(TYPE, MULTI_POINT_TYPE); + g.writeStartArray(COORDINATES); + writeCoordinates(g, multiPoint.getCoordinates()); + g.writeEnd();// coordinates array + } else if (geometry instanceof LineString lineString) { + g.write(TYPE, LINE_STRING_TYPE); + g.writeStartArray(COORDINATES); + writeCoordinates(g, lineString.getCoordinates()); + g.writeEnd();// coordinates array + } else if (geometry instanceof Polygon polygon) { + g.write(TYPE, POLYGON_TYPE); + g.writeStartArray(COORDINATES); + LinearRing exteriorRing = polygon.getExteriorRing(); + g.writeStartArray(); + writeCoordinates(g, exteriorRing.getCoordinates()); + g.writeEnd(); + for (int i = 0; i < polygon.getNumInteriorRing(); i++) { + LinearRing interiorRing = polygon.getInteriorRingN(i); + // TODO verify that holes are clockwise + g.writeStartArray(); + writeCoordinates(g, interiorRing.getCoordinates()); + g.writeEnd(); + } + g.writeEnd();// coordinates array + } else if (geometry instanceof GeometryCollection geometryCollection) {// must be last + g.write(TYPE, GEOMETRY_COLLECTION_TYPE); + g.writeStartArray(GEOMETRIES); + for (int i = 0; i < geometryCollection.getNumGeometries(); i++) { + g.writeStartObject(); + writeGeometry(g, geometryCollection.getGeometryN(i)); + g.writeEnd();// geometry object + } + g.writeEnd();// geometries array + } else { + throw new IllegalArgumentException(geometry.getClass() + " is not supported."); + } + } + + /** Writes a sequence of coordinates [[lat,lon],[lat,lon]] */ + public static void writeCoordinates(JsonGenerator g, Coordinate[] coordinates) { + for (Coordinate coordinate : coordinates) { + g.writeStartArray(); + writeCoordinate(g, coordinate); + g.writeEnd(); + } + } + + /** Writes a pair of coordinates [lat,lon]. */ + public static void writeCoordinate(JsonGenerator g, Coordinate coordinate) { + g.write(coordinate.getX()); + g.write(coordinate.getY()); + double z = coordinate.getZ(); + if (!Double.isNaN(z)) { + g.write(z); + } + } + + /** + * Writes the {@link Envelope} of a {@link Geometry} as a bbox GeoJSON object. + */ + public static void writeBBox(JsonGenerator g, Geometry geometry) { + g.writeStartArray(BBOX); + Envelope envelope = geometry.getEnvelopeInternal(); + g.write(envelope.getMinX()); + g.write(envelope.getMinY()); + g.write(envelope.getMaxX()); + g.write(envelope.getMaxY()); + g.writeEnd(); + } + + /* + * READ + */ + /** Reads a geometry from the geometry object of a GEoJSON feature. */ + @SuppressWarnings("unchecked") + public static T readGeometry(JsonObject geom, Class 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() { + } + +}