Introduce GeoEntityUtils
authorMathieu Baudier <mbaudier@argeo.org>
Wed, 4 Oct 2023 07:05:20 +0000 (09:05 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Wed, 4 Oct 2023 07:05:20 +0000 (09:05 +0200)
org.argeo.app.api/src/org/argeo/app/api/EntityName.java
org.argeo.app.api/src/org/argeo/app/api/EntityType.java
org.argeo.app.api/src/org/argeo/app/api/entity.cnd
org.argeo.app.geo/src/org/argeo/app/geo/acr/GeoEntityUtils.java [new file with mode: 0644]

index 308f459076b16f900f32cad710d01638f37bb97f..f8906320c7c464559685777846f75c6a13638ac9 100644 (file)
@@ -2,8 +2,14 @@ package org.argeo.app.api;
 
 import org.argeo.api.acr.QNamed;
 
+/** Names used in the entity namespace http://www.argeo.org/ns/entity. */
 public enum EntityName implements QNamed {
        type, //
+       // geography
+       minLat, minLon, maxLat, maxLon,
+       // geo entities
+       place,
+       //
        ;
 
        @Override
index 1e308214aecc99327e141c05d9648e082b5a7974..8a258ebc38d4c60511bc10df3680b8cfd5102a53 100644 (file)
@@ -2,7 +2,7 @@ package org.argeo.app.api;
 
 import org.argeo.api.acr.QNamed;
 
-/** Types related to entities. */
+/** Types used in the entity namespace http://www.argeo.org/ns/entity. */
 public enum EntityType implements QNamed {
        // entity
        entity, local, relatedTo,
@@ -15,7 +15,7 @@ public enum EntityType implements QNamed {
        // graphics
        box,
        // geography
-       geopoint, bearing,
+       geopoint, bearing, geobounded,
        // ldap
        person, user;
 
index 396b6f203187a4b2f0220b915323b6d5de47707f..2ca640bae9afd6a3473cdaae3ecfde23fc234cb4 100644 (file)
@@ -115,3 +115,12 @@ mixin
 [entity:bearing]
 mixin
 - svg:direction (DOUBLE)
+
+[entity:geobounded]
+mixin
+- entity:minLat (DOUBLE) m
+- entity:minLon (DOUBLE) m
+- entity:maxLat (DOUBLE) m
+- entity:maxLon (DOUBLE) m
+- entity:minAlt (DOUBLE)
+- entity:maxAlt (DOUBLE)
diff --git a/org.argeo.app.geo/src/org/argeo/app/geo/acr/GeoEntityUtils.java b/org.argeo.app.geo/src/org/argeo/app/geo/acr/GeoEntityUtils.java
new file mode 100644 (file)
index 0000000..6019ee9
--- /dev/null
@@ -0,0 +1,125 @@
+package org.argeo.app.geo.acr;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.UncheckedIOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.xml.namespace.QName;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.ContentName;
+import org.argeo.api.acr.DName;
+import org.argeo.api.acr.QNamed;
+import org.argeo.app.api.EntityName;
+import org.argeo.app.api.EntityType;
+import org.argeo.app.api.WGS84PosName;
+import org.argeo.app.geo.GeoJson;
+import org.argeo.app.geo.JTS;
+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.Point;
+
+import jakarta.json.Json;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonReader;
+import jakarta.json.stream.JsonGenerator;
+
+/** Utilities around entity types related to geography. */
+public class GeoEntityUtils {
+       public static final String PLACE_GEOM_JSON = "place.geom.json";
+       public static final String _GEOM_JSON = ".geom.json";
+
+       public static void putGeometry(Content c, QNamed name, Geometry geometry) {
+               putGeometry(c, name.qName(), geometry);
+       }
+
+       public static void putGeometry(Content c, QName name, Geometry geometry) {
+               QName jsonFileName = new ContentName(name.getNamespaceURI(), name.getLocalPart() + _GEOM_JSON);
+               Content geom = c.soleChild(jsonFileName).orElseGet(
+                               () -> c.add(jsonFileName, Collections.singletonMap(DName.getcontenttype.qName(), "application/json")));
+               try (OutputStream out = geom.open(OutputStream.class)) {
+                       JsonGenerator g = Json.createGenerator(out);
+                       g.writeStartObject();
+                       GeoJson.writeGeometry(g, geometry);
+                       g.writeEnd();
+                       g.close();
+               } catch (IOException e) {
+                       throw new UncheckedIOException("Cannot add geometry " + name + " to " + c, e);
+               }
+               updateBoundingBox(c);
+       }
+
+       public static <T extends Geometry> T getGeometry(Content c, QNamed name, Class<T> clss) {
+               return getGeometry(c, name.qName(), clss);
+       }
+
+       public static <T extends Geometry> T getGeometry(Content c, QName name, Class<T> clss) {
+               QName jsonFileName = new ContentName(name.getNamespaceURI(), name.getLocalPart() + _GEOM_JSON);
+               Content geom = c.soleChild(jsonFileName).orElse(null);
+               if (geom == null)
+                       return null;
+               try (Reader in = new InputStreamReader(geom.open(InputStream.class), StandardCharsets.UTF_8)) {
+                       JsonReader jsonReader = Json.createReader(in);
+                       JsonObject jsonObject = jsonReader.readObject();
+                       T readGeom = GeoJson.readGeometry(jsonObject, clss);
+                       return readGeom;
+               } catch (IOException e) {
+                       throw new UncheckedIOException("Cannot parse " + c, e);
+               }
+       }
+
+       public static Point toPoint(Content c) {
+               if (c.hasContentClass(EntityType.geopoint)) {
+                       Double lat = c.get(WGS84PosName.lat, Double.class).orElseThrow();
+                       Double lon = c.get(WGS84PosName.lon, Double.class).orElseThrow();
+                       Double alt = c.get(WGS84PosName.alt, Double.class).orElse(null);
+                       return JTS.GEOMETRY_FACTORY_WGS84
+                                       .createPoint(alt != null ? new Coordinate(lat, lon, alt) : new Coordinate(lat, lon));
+               }
+               return null;
+       }
+
+       public static GeometryCollection getGeometries(Content entity) {
+               List<Geometry> geoms = new ArrayList<>();
+               Point geoPoint = toPoint(entity);
+               if (geoPoint != null)
+                       geoms.add(geoPoint);
+
+               Geometry place = getGeometry(entity, EntityName.place.qName(), Geometry.class);
+               if (place != null)
+                       geoms.add(place);
+
+               if (geoms.isEmpty())
+                       return null;
+               GeometryCollection geometryCollection = JTS.GEOMETRY_FACTORY_WGS84
+                               .createGeometryCollection(geoms.toArray(new Geometry[geoms.size()]));
+               return geometryCollection;
+       }
+
+       public static void updateBoundingBox(Content entity) {
+               GeometryCollection geometryCollection = getGeometries(entity);
+               if (geometryCollection == null)
+                       return;
+               entity.addContentClasses(EntityType.geobounded.qName());
+
+               Envelope bbox = geometryCollection.getEnvelopeInternal();
+               entity.put(EntityName.minLat, bbox.getMinX());
+               entity.put(EntityName.minLon, bbox.getMinY());
+               entity.put(EntityName.maxLat, bbox.getMaxX());
+               entity.put(EntityName.maxLon, bbox.getMaxY());
+       }
+
+       /** singleton */
+       private GeoEntityUtils() {
+       }
+
+}