Fix lat/lon vs. lon/lat issues
[gpl/argeo-suite.git] / org.argeo.app.geo / src / org / argeo / app / geo / http / WfsHttpHandler.java
index 580866dec4c5c13577d7aa34f908b17ad8f0184c..8c143dbf01510d7eb10a56b588ec50b82ac819ea 100644 (file)
@@ -13,6 +13,8 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.stream.Stream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
 
 import javax.xml.namespace.QName;
 
@@ -29,19 +31,28 @@ import org.argeo.app.api.WGS84PosName;
 import org.argeo.app.api.geo.FeatureAdapter;
 import org.argeo.app.geo.CqlUtils;
 import org.argeo.app.geo.GeoJson;
+import org.argeo.app.geo.GeoUtils;
 import org.argeo.app.geo.GpxUtils;
 import org.argeo.app.geo.JTS;
+import org.argeo.app.geo.acr.GeoEntityUtils;
 import org.argeo.cms.acr.json.AcrJsonUtils;
+import org.argeo.cms.auth.RemoteAuthUtils;
 import org.argeo.cms.http.HttpHeader;
+import org.argeo.cms.http.RemoteAuthHttpExchange;
 import org.argeo.cms.http.server.HttpServerUtils;
 import org.argeo.cms.util.LangUtils;
-import org.geotools.data.DataUtilities;
-import org.geotools.data.geojson.GeoJSONWriter;
+import org.geotools.api.feature.GeometryAttribute;
+import org.geotools.api.feature.simple.SimpleFeature;
+import org.geotools.api.feature.simple.SimpleFeatureType;
+import org.geotools.api.feature.type.AttributeDescriptor;
+import org.geotools.api.feature.type.Name;
+import org.geotools.api.referencing.FactoryException;
+import org.geotools.api.referencing.crs.CoordinateReferenceSystem;
+import org.geotools.api.referencing.operation.MathTransform;
+import org.geotools.api.referencing.operation.TransformException;
 import org.geotools.feature.DefaultFeatureCollection;
 import org.geotools.feature.NameImpl;
-import org.geotools.feature.SchemaException;
 import org.geotools.feature.simple.SimpleFeatureBuilder;
-import org.geotools.geometry.jts.JTSFactoryFinder;
 import org.geotools.referencing.CRS;
 import org.geotools.referencing.crs.DefaultGeographicCRS;
 import org.geotools.wfs.GML;
@@ -49,18 +60,7 @@ import org.geotools.wfs.GML.Version;
 import org.locationtech.jts.geom.Coordinate;
 import org.locationtech.jts.geom.Envelope;
 import org.locationtech.jts.geom.Geometry;
-import org.locationtech.jts.geom.GeometryFactory;
-import org.locationtech.jts.geom.Point;
 import org.locationtech.jts.geom.Polygon;
-import org.opengis.feature.GeometryAttribute;
-import org.opengis.feature.simple.SimpleFeature;
-import org.opengis.feature.simple.SimpleFeatureType;
-import org.opengis.feature.type.AttributeDescriptor;
-import org.opengis.feature.type.Name;
-import org.opengis.referencing.FactoryException;
-import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.opengis.referencing.operation.MathTransform;
-import org.opengis.referencing.operation.TransformException;
 
 import com.sun.net.httpserver.HttpExchange;
 import com.sun.net.httpserver.HttpHandler;
@@ -84,6 +84,24 @@ public class WfsHttpHandler implements HttpHandler {
        @Override
        public void handle(HttpExchange exchange) throws IOException {
                String path = HttpServerUtils.subPath(exchange);
+
+               // content path
+               final String pathToUse;
+               int lastSlash = path.lastIndexOf('/');
+               String fileName = null;
+               if (lastSlash > 0) {
+                       fileName = path.substring(lastSlash + 1);
+               }
+               boolean zipped = false;
+               if (fileName != null) {
+                       pathToUse = path.substring(0, lastSlash);
+                       if (path.endsWith(".zip")) {
+                               zipped = true;
+                       }
+               } else {
+                       pathToUse = path;
+               }
+
                ContentSession session = HttpServerUtils.getContentSession(contentRepository, exchange);
                // Content content = session.get(path);
 
@@ -96,7 +114,8 @@ public class WfsHttpHandler implements HttpHandler {
                        outputFormat = "application/json";
                }
                String bboxStr = getKvpParameter(parameters, BBOX);
-               log.debug(bboxStr);
+               if (log.isTraceEnabled())
+                       log.trace(bboxStr);
                final Envelope bbox;
                if (bboxStr != null) {
                        String srs;
@@ -112,10 +131,11 @@ public class WfsHttpHandler implements HttpHandler {
                                srs = null;
                        }
 
-                       if (srs != null) {
+                       if (srs != null && !srs.equals(GeoUtils.EPSG_4326)) {
                                try {
+                                       // TODO optimise
                                        CoordinateReferenceSystem sourceCRS = CRS.decode(srs);
-                                       CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:4326");
+                                       CoordinateReferenceSystem targetCRS = CRS.decode(GeoUtils.EPSG_4326);
                                        MathTransform transform = CRS.findMathTransform(sourceCRS, targetCRS, true);
                                        bbox = org.geotools.geometry.jts.JTS.transform(
                                                        new Envelope(new Coordinate(minLat, minLon), new Coordinate(maxLat, maxLon)), transform);
@@ -130,16 +150,31 @@ public class WfsHttpHandler implements HttpHandler {
                        bbox = null;
                }
 
-               switch (outputFormat) {
-               case "application/json" -> {
-                       exchange.getResponseHeaders().set(HttpHeader.CONTENT_TYPE.getHeaderName(), "application/json");
+               // response headers
+               exchange.getResponseHeaders().set(HttpHeader.DATE.getHeaderName(), Long.toString(System.currentTimeMillis()));
+
+               if (fileName != null) {
+                       exchange.getResponseHeaders().set(HttpHeader.CONTENT_DISPOSITION.getHeaderName(),
+                                       HttpHeader.ATTACHMENT + ";" + HttpHeader.FILENAME + "=\"" + fileName + "\"");
+
                }
-               case "GML3" -> {
+
+               // content type
+               if (zipped) {
+                       exchange.getResponseHeaders().set(HttpHeader.CONTENT_TYPE.getHeaderName(), "application/zip");
+
+               } else {
+                       switch (outputFormat) {
+                       case "application/json" -> {
+                               exchange.getResponseHeaders().set(HttpHeader.CONTENT_TYPE.getHeaderName(), "application/json");
+                       }
+                       case "GML3" -> {
 //                     exchange.getResponseHeaders().set(HttpHeader.CONTENT_TYPE.getHeaderName(), "application/gml+xml");
-                       exchange.getResponseHeaders().set(HttpHeader.CONTENT_TYPE.getHeaderName(), "application/xml");
-               }
+                               exchange.getResponseHeaders().set(HttpHeader.CONTENT_TYPE.getHeaderName(), "application/xml");
+                       }
 
-               default -> throw new IllegalArgumentException("Unexpected value: " + outputFormat);
+                       default -> throw new IllegalArgumentException("Unexpected value: " + outputFormat);
+                       }
                }
 
                List<QName> typeNames = new ArrayList<>();
@@ -158,30 +193,49 @@ public class WfsHttpHandler implements HttpHandler {
                // QUERY
                Stream<Content> res = session.search((search) -> {
                        if (cql != null) {
-                               CqlUtils.filter(search.from(path), cql);
+                               CqlUtils.filter(search.from(pathToUse), cql);
                        } else {
-                               search.from(path);
+                               search.from(pathToUse);
                        }
                        for (QName typeName : typeNames) {
                                FeatureAdapter featureAdapter = featureAdapters.get(typeName);
                                if (featureAdapter == null)
                                        throw new IllegalStateException("No feature adapter found for " + typeName);
                                // f.isContentClass(typeName);
-                               featureAdapter.addConstraintsForFeature((AndFilter) search.getWhere(), typeName);
+                               RemoteAuthUtils.doAs(() -> {
+                                       featureAdapter.addConstraintsForFeature((AndFilter) search.getWhere(), typeName);
+                                       return null;
+                               }, new RemoteAuthHttpExchange(exchange));
                        }
 
                        if (bbox != null) {
-                               search.getWhere().gte(EntityName.minLat, bbox.getMinX());
-                               search.getWhere().gte(EntityName.minLon, bbox.getMinY());
-                               search.getWhere().lte(EntityName.maxLat, bbox.getMaxX());
-                               search.getWhere().lte(EntityName.maxLon, bbox.getMaxY());
+                               search.getWhere().any((or) -> {
+                                       or.all((and) -> {
+                                               and.gte(EntityName.minLat, bbox.getMinX());
+                                               and.gte(EntityName.minLon, bbox.getMinY());
+                                               and.lte(EntityName.maxLat, bbox.getMaxX());
+                                               and.lte(EntityName.maxLon, bbox.getMaxY());
+                                       });
+                                       or.all((and) -> {
+                                               and.gte(WGS84PosName.lat, bbox.getMinX());
+                                               and.gte(WGS84PosName.lon, bbox.getMinY());
+                                               and.lte(WGS84PosName.lat, bbox.getMaxX());
+                                               and.lte(WGS84PosName.lon, bbox.getMaxY());
+                                       });
+                               });
                        }
                });
 
                exchange.sendResponseHeaders(200, 0);
 
                final int BUFFER_SIZE = 100 * 1024;
-               try (BufferedOutputStream out = new BufferedOutputStream(exchange.getResponseBody(), BUFFER_SIZE)) {
+               try (OutputStream out = zipped ? new ZipOutputStream(exchange.getResponseBody())
+                               : new BufferedOutputStream(exchange.getResponseBody(), BUFFER_SIZE)) {
+                       if (out instanceof ZipOutputStream zipOut) {
+                               String unzippedFileName = fileName.substring(0, fileName.length() - ".zip".length());
+                               zipOut.putNextEntry(new ZipEntry(unzippedFileName));
+                       }
+
                        if ("GML3".equals(outputFormat)) {
                                encodeCollectionAsGML(res, out);
                        } else if ("application/json".equals(outputFormat)) {
@@ -307,12 +361,7 @@ public class WfsHttpHandler implements HttpHandler {
 
        protected Geometry getDefaultGeometry(Content content) {
                if (content.hasContentClass(EntityType.geopoint)) {
-                       double latitude = content.get(WGS84PosName.lat, Double.class).get();
-                       double longitude = content.get(WGS84PosName.lon, Double.class).get();
-
-                       Coordinate coordinate = new Coordinate(longitude, latitude);
-                       Point the_geom = JTS.GEOMETRY_FACTORY.createPoint(coordinate);
-                       return the_geom;
+                       return GeoEntityUtils.toPoint(content);
                }
                return null;
        }
@@ -337,78 +386,6 @@ public class WfsHttpHandler implements HttpHandler {
 
        }
 
-       protected void encodeCollectionAsGeoJSonOld(Stream<Content> features, OutputStream out) throws IOException {
-
-               // BODY PROCESSING
-               try (GeoJSONWriter geoJSONWriter = new GeoJSONWriter(out)) {
-                       geoJSONWriter.setPrettyPrinting(true);
-                       geoJSONWriter.setEncodeFeatureBounds(true);
-
-                       boolean gpx = true;
-                       SimpleFeatureType TYPE;
-                       try {
-                               if (gpx)
-                                       TYPE = DataUtilities.createType("Content",
-                                                       "the_geom:Polygon:srid=4326,path:String,type:String,name:String");
-                               else
-                                       TYPE = DataUtilities.createType("Content",
-                                                       "the_geom:Point:srid=4326,path:String,type:String,name:String");
-                       } catch (SchemaException e) {
-                               throw new RuntimeException(e);
-                       }
-                       SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(TYPE);
-                       GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
-
-                       features.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.lon, 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)));
-                                       }
-                               }
-                               featureBuilder.add(NamespaceUtils.toPrefixedName(c.getName()));
-
-                               String uuid = c.attr(LdapAttr.entryUUID);
-
-                               SimpleFeature feature = featureBuilder.buildFeature(uuid);
-                               try {
-                                       geoJSONWriter.write(feature);
-                               } catch (IOException e) {
-                                       throw new UncheckedIOException(e);
-                               }
-                       });
-               }
-       }
-
        protected void encodeCollectionAsGML(Stream<Content> features, OutputStream out) throws IOException {
                String entityType = "entity";
                URL schemaLocation = getClass().getResource("/org/argeo/app/api/entity.xsd");