Introduce WFS HTTP handler
authorMathieu Baudier <mbaudier@argeo.org>
Sat, 2 Sep 2023 12:34:02 +0000 (14:34 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Sat, 2 Sep 2023 12:34:02 +0000 (14:34 +0200)
org.argeo.app.geo/OSGI-INF/geoJsonHttpHandler.xml
org.argeo.app.geo/src/org/argeo/app/geo/CqlUtils.java
org.argeo.app.geo/src/org/argeo/app/geo/http/WfsHttpHandler.java [new file with mode: 0644]
org.argeo.app.geo/src/org/argeo/app/internal/geo/http/GeoJsonHttpHandler.java [deleted file]
org.argeo.app.geo/src/org/argeo/app/internal/geo/http/GmlHttpHandler.java [deleted file]

index 4fbec8ff6ae544947089da7246b3eeb36ec33d93..352595bdbaaa4d43ae9a51fb8059b166aea50e40 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true">
-   <implementation class="org.argeo.app.internal.geo.http.GeoJsonHttpHandler"/>
+   <implementation class="org.argeo.app.geo.http.WfsHttpHandler"/>
    <service>
       <provide interface="com.sun.net.httpserver.HttpHandler"/>
    </service>
index 8d6c138f4591ebed9156f9559dce5254cb8a230c..f4413ad054debb681ec871a762de37f062547261 100644 (file)
@@ -12,10 +12,9 @@ import org.opengis.filter.PropertyIsEqualTo;
 import org.opengis.filter.expression.Literal;
 import org.opengis.filter.expression.PropertyName;
 
+/** Utilities around the CQL query format. */
 public class CqlUtils {
 
-       public final static String CQL_FILTER = "cql_filter";
-
        public static void filter(BasicSearch search, String cql) {
                try {
                        filter(search, CQL.toFilter(cql));
diff --git a/org.argeo.app.geo/src/org/argeo/app/geo/http/WfsHttpHandler.java b/org.argeo.app.geo/src/org/argeo/app/geo/http/WfsHttpHandler.java
new file mode 100644 (file)
index 0000000..6408b40
--- /dev/null
@@ -0,0 +1,162 @@
+package org.argeo.app.geo.http;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Stream;
+
+import javax.xml.namespace.QName;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.ContentSession;
+import org.argeo.api.acr.NamespaceUtils;
+import org.argeo.api.acr.ldap.LdapAttr;
+import org.argeo.api.acr.spi.ProvidedRepository;
+import org.argeo.app.api.EntityName;
+import org.argeo.app.api.EntityType;
+import org.argeo.app.api.WGS84PosName;
+import org.argeo.app.geo.CqlUtils;
+import org.argeo.app.geo.GpxUtils;
+import org.argeo.cms.http.HttpHeader;
+import org.argeo.cms.http.server.HttpServerUtils;
+import org.geotools.data.DataUtilities;
+import org.geotools.data.geojson.GeoJSONWriter;
+import org.geotools.feature.SchemaException;
+import org.geotools.feature.simple.SimpleFeatureBuilder;
+import org.geotools.geometry.jts.JTSFactoryFinder;
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.GeometryFactory;
+import org.opengis.feature.simple.SimpleFeature;
+import org.opengis.feature.simple.SimpleFeatureType;
+
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+
+/** A partially implemented WFS 2.0 server. */
+public class WfsHttpHandler implements HttpHandler {
+       private ProvidedRepository contentRepository;
+
+       // HTTP parameters
+       final static String OUTPUT_FORMAT = "outputFormat";
+       final static String TYPE_NAMES = "typeNames";
+       final static String CQL_FILTER = "cql_filter";
+
+       @Override
+       public void handle(HttpExchange exchange) throws IOException {
+               String path = HttpServerUtils.subPath(exchange);
+               ContentSession session = HttpServerUtils.getContentSession(contentRepository, exchange);
+               // Content content = session.get(path);
+
+               Map<String, List<String>> parameters = HttpServerUtils.parseParameters(exchange);
+               String cql = parameters.containsKey(CQL_FILTER) ? parameters.get(CQL_FILTER).get(0) : null;
+               String typeNamesStr = parameters.containsKey(TYPE_NAMES) ? parameters.get(TYPE_NAMES).get(0) : null;
+               String outputFormat = parameters.containsKey(OUTPUT_FORMAT) ? parameters.get(OUTPUT_FORMAT).get(0) : null;
+               if (outputFormat == null) {
+                       outputFormat = "application/json";
+               }
+
+               switch (outputFormat) {
+               case "application/json" -> {
+                       exchange.getResponseHeaders().set(HttpHeader.CONTENT_TYPE.getHeaderName(), "application/json");
+               }
+
+               default -> throw new IllegalArgumentException("Unexpected value: " + outputFormat);
+               }
+
+               QName[] typeNames;
+               if (typeNamesStr != null) {
+                       String[] arr = typeNamesStr.split(",");
+                       typeNames = new QName[arr.length];
+                       for (int i = 0; i < arr.length; i++) {
+                               typeNames[i] = NamespaceUtils.parsePrefixedName(arr[i]);
+                       }
+               } else {
+                       typeNames = new QName[] { EntityType.local.qName() };
+               }
+
+               if (cql != null) {
+                       Stream<Content> res = session.search((search) -> {
+                               CqlUtils.filter(search.from(path), cql);
+                               search.getWhere().any((f) -> {
+                                       for (QName typeName : typeNames)
+                                               f.isContentClass(typeName);
+                               });
+                       });
+
+                       exchange.sendResponseHeaders(200, 0);
+
+                       // BODY PROCESSING
+                       GeoJSONWriter geoJSONWriter = new GeoJSONWriter(exchange.getResponseBody());
+                       geoJSONWriter.setPrettyPrinting(true);
+
+                       boolean gpx = false;
+                       SimpleFeatureType TYPE;
+                       try {
+                               if (gpx)
+                                       TYPE = DataUtilities.createType("Content", "the_geom:Polygon:srid=4326,path:String,type:String");
+                               else
+                                       TYPE = DataUtilities.createType("Content", "the_geom:Point:srid=4326,path:String,type:String");
+                       } catch (SchemaException e) {
+                               throw new RuntimeException(e);
+                       }
+                       SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(TYPE);
+                       GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
+
+                       res.forEach((c) -> {
+                               Geometry the_geom;
+                               if (gpx) {// experimental
+                                       Content area = c.getContent("gpx/area.gpx").orElse(null);
+                                       if (area == null)
+                                               return;
+                                       try (InputStream in = area.open(InputStream.class)) {
+                                               SimpleFeature feature = GpxUtils.parseGpxToPolygon(in);
+                                               the_geom = (Geometry) feature.getDefaultGeometry();
+                                       } catch (IOException e) {
+                                               throw new UncheckedIOException("Cannot parse " + c, e);
+                                       }
+                               } else {
+                                       if (!c.hasContentClass(EntityType.geopoint))
+                                               return;
+
+                                       double latitude = c.get(WGS84PosName.lat, Double.class).get();
+                                       double longitude = c.get(WGS84PosName.lng, Double.class).get();
+
+                                       Coordinate coordinate = new Coordinate(longitude, latitude);
+                                       the_geom = geometryFactory.createPoint(coordinate);
+
+                               }
+
+                               featureBuilder.add(the_geom);
+                               String pth = c.getPath();
+                               featureBuilder.add(pth);
+                               if (c.hasContentClass(EntityType.local)) {
+                                       String type = c.attr(EntityName.type);
+                                       featureBuilder.add(type);
+                               } else {
+                                       List<QName> contentClasses = c.getContentClasses();
+                                       if (!contentClasses.isEmpty()) {
+                                               featureBuilder.add(NamespaceUtils.toPrefixedName(contentClasses.get(0)));
+                                       }
+                               }
+
+                               String uuid = c.attr(LdapAttr.entryUUID);
+
+                               SimpleFeature feature = featureBuilder.buildFeature(uuid);
+                               try {
+                                       geoJSONWriter.write(feature);
+                               } catch (IOException e) {
+                                       throw new UncheckedIOException(e);
+                               }
+                       });
+                       geoJSONWriter.close();
+               }
+
+       }
+
+       public void setContentRepository(ProvidedRepository contentRepository) {
+               this.contentRepository = contentRepository;
+       }
+}
diff --git a/org.argeo.app.geo/src/org/argeo/app/internal/geo/http/GeoJsonHttpHandler.java b/org.argeo.app.geo/src/org/argeo/app/internal/geo/http/GeoJsonHttpHandler.java
deleted file mode 100644 (file)
index ca81258..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-package org.argeo.app.internal.geo.http;
-
-import static org.argeo.app.geo.CqlUtils.CQL_FILTER;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UncheckedIOException;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Stream;
-
-import javax.xml.namespace.QName;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.acr.ContentSession;
-import org.argeo.api.acr.NamespaceUtils;
-import org.argeo.api.acr.ldap.LdapAttr;
-import org.argeo.api.acr.spi.ProvidedRepository;
-import org.argeo.app.api.EntityName;
-import org.argeo.app.api.EntityType;
-import org.argeo.app.api.WGS84PosName;
-import org.argeo.app.geo.CqlUtils;
-import org.argeo.app.geo.GpxUtils;
-import org.argeo.cms.http.HttpHeader;
-import org.argeo.cms.http.server.HttpServerUtils;
-import org.geotools.data.DataUtilities;
-import org.geotools.data.geojson.GeoJSONWriter;
-import org.geotools.feature.SchemaException;
-import org.geotools.feature.simple.SimpleFeatureBuilder;
-import org.geotools.filter.text.cql2.CQL;
-import org.geotools.geometry.jts.JTSFactoryFinder;
-import org.locationtech.jts.geom.Coordinate;
-import org.locationtech.jts.geom.Geometry;
-import org.locationtech.jts.geom.GeometryFactory;
-import org.opengis.feature.simple.SimpleFeature;
-import org.opengis.feature.simple.SimpleFeatureType;
-import org.opengis.filter.Filter;
-
-import com.sun.net.httpserver.HttpExchange;
-import com.sun.net.httpserver.HttpHandler;
-
-public class GeoJsonHttpHandler implements HttpHandler {
-       private ProvidedRepository contentRepository;
-
-       @Override
-       public void handle(HttpExchange exchange) throws IOException {
-               String path = HttpServerUtils.subPath(exchange);
-               ContentSession session = HttpServerUtils.getContentSession(contentRepository, exchange);
-               // Content content = session.get(path);
-
-               exchange.getResponseHeaders().set(HttpHeader.CONTENT_TYPE.getHeaderName(), "application/json");
-
-               Map<String, List<String>> parameters = HttpServerUtils.parseParameters(exchange);
-               String cql = parameters.containsKey(CQL_FILTER) ? parameters.get(CQL_FILTER).get(0) : null;
-
-               if (cql != null) {
-                       Stream<Content> res = session.search((search) -> {
-                               CqlUtils.filter(search.from(path), cql);
-                               search.getWhere().isContentClass(EntityType.local);
-                       });
-
-                       exchange.sendResponseHeaders(200, 0);
-
-                       // BODY PROCESSING
-                       GeoJSONWriter geoJSONWriter = new GeoJSONWriter(exchange.getResponseBody());
-                       geoJSONWriter.setPrettyPrinting(true);
-
-//                     Writer writer = new OutputStreamWriter(exchange.getResponseBody());
-//                     writer.write("""
-//                                     {
-//                                       "type": "FeatureCollection",
-//                                       "features": [
-//                                     """);
-
-                       boolean gpx = false;
-                       SimpleFeatureType TYPE;
-                       try {
-                               if (gpx)
-                                       TYPE = DataUtilities.createType("Content", "the_geom:Polygon:srid=4326,path:String,type:String");
-                               else
-                                       TYPE = DataUtilities.createType("Content", "the_geom:Point:srid=4326,path:String,type:String");
-                       } catch (SchemaException e) {
-                               throw new RuntimeException(e);
-                       }
-                       SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(TYPE);
-                       GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
-
-                       res.forEach((c) -> {
-                               Geometry the_geom;
-                               if (gpx) {// experimental
-                                       Content area = c.getContent("gpx/area.gpx").orElse(null);
-                                       if (area == null)
-                                               return;
-                                       try (InputStream in = area.open(InputStream.class)) {
-                                               SimpleFeature feature = GpxUtils.parseGpxToPolygon(in);
-                                               the_geom = (Geometry) feature.getDefaultGeometry();
-                                       } catch (IOException e) {
-                                               throw new UncheckedIOException("Cannot parse " + c, e);
-                                       }
-                               } else {
-                                       if (!c.hasContentClass(EntityType.geopoint))
-                                               return;
-
-                                       double latitude = c.get(WGS84PosName.lat, Double.class).get();
-                                       double longitude = c.get(WGS84PosName.lng, Double.class).get();
-
-                                       Coordinate coordinate = new Coordinate(longitude, latitude);
-                                       the_geom = geometryFactory.createPoint(coordinate);
-
-                               }
-
-                               featureBuilder.add(the_geom);
-                               String pth = c.getPath();
-                               featureBuilder.add(pth);
-                               if (c.hasContentClass(EntityType.local)) {
-                                       String type = c.attr(EntityName.type);
-                                       featureBuilder.add(type);
-                               } else {
-                                       List<QName> contentClasses = c.getContentClasses();
-                                       if (!contentClasses.isEmpty()) {
-                                               featureBuilder.add(NamespaceUtils.toPrefixedName(contentClasses.get(0)));
-                                       }
-                               }
-
-                               String uuid = c.attr(LdapAttr.entryUUID);
-
-                               SimpleFeature feature = featureBuilder.buildFeature(uuid);
-//                             String json = GeoJSONWriter.toGeoJSON(feature);
-                               try {
-                                       geoJSONWriter.write(feature);
-                               } catch (IOException e) {
-                                       throw new UncheckedIOException(e);
-                               }
-                       });
-//                     writer.write("""
-//                                       ]
-//                                     }
-//                                     """);
-                       geoJSONWriter.close();
-               }
-
-       }
-
-       public void setContentRepository(ProvidedRepository contentRepository) {
-               this.contentRepository = contentRepository;
-       }
-
-       public static void main(String[] args) throws Exception {
-               Filter filter = CQL.toFilter("entity:type='apafField' AND jcr:isCheckedOut=false");
-               System.out.println(CQL.toCQL(filter));
-       }
-}
diff --git a/org.argeo.app.geo/src/org/argeo/app/internal/geo/http/GmlHttpHandler.java b/org.argeo.app.geo/src/org/argeo/app/internal/geo/http/GmlHttpHandler.java
deleted file mode 100644 (file)
index 7213cd2..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-package org.argeo.app.internal.geo.http;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Stream;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.acr.ContentSession;
-import org.argeo.api.acr.spi.ProvidedRepository;
-import org.argeo.app.geo.CqlUtils;
-import org.argeo.cms.http.server.HttpServerUtils;
-
-import com.sun.net.httpserver.HttpExchange;
-import com.sun.net.httpserver.HttpHandler;
-
-public class GmlHttpHandler implements HttpHandler {
-       private ProvidedRepository contentRepository;
-
-       @Override
-       public void handle(HttpExchange exchange) throws IOException {
-               String path = HttpServerUtils.subPath(exchange);
-               ContentSession session = HttpServerUtils.getContentSession(contentRepository, exchange);
-//             Content content = session.get(path);
-       }
-
-       public void setContentRepository(ProvidedRepository contentRepository) {
-               this.contentRepository = contentRepository;
-       }
-
-}