X-Git-Url: http://git.argeo.org/?a=blobdiff_plain;f=org.argeo.app.geo%2Fsrc%2Forg%2Fargeo%2Fapp%2Fgeo%2Fhttp%2FWfsHttpHandler.java;h=150977394d4c5bb00093a5020a8646c8294c8a84;hb=03c171b5d502b461c534816c3c11cae889aef3c1;hp=2a637575d04844a29a35a11f297507966bea30c3;hpb=b4ffcdbc144ccb0633f033bb0be498988374ae68;p=gpl%2Fargeo-suite.git 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 index 2a63757..1509773 100644 --- 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 @@ -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; @@ -28,33 +30,35 @@ import org.argeo.app.api.EntityType; 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.GeoJson; +import org.argeo.app.geo.GeoUtils; import org.argeo.app.geo.GpxUtils; import org.argeo.app.geo.JTS; import org.argeo.cms.acr.json.AcrJsonUtils; import org.argeo.cms.http.HttpHeader; 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; 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 com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; @@ -71,15 +75,35 @@ public class WfsHttpHandler implements HttpHandler { final static String OUTPUT_FORMAT = "outputFormat"; final static String TYPE_NAMES = "typeNames"; final static String CQL_FILTER = "cql_filter"; + final static String BBOX = "bbox"; private final Map featureAdapters = new HashMap<>(); @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); + // PARAMETERS Map> parameters = HttpServerUtils.parseParameters(exchange); String cql = getKvpParameter(parameters, CQL_FILTER); String typeNamesStr = getKvpParameter(parameters, TYPE_NAMES); @@ -87,17 +111,67 @@ public class WfsHttpHandler implements HttpHandler { if (outputFormat == null) { outputFormat = "application/json"; } + String bboxStr = getKvpParameter(parameters, BBOX); + log.debug(bboxStr); + final Envelope bbox; + if (bboxStr != null) { + String srs; + String[] arr = bboxStr.split(","); + // TODO check SRS and convert to WGS84 + double minLat = Double.parseDouble(arr[0]); + double minLon = Double.parseDouble(arr[1]); + double maxLat = Double.parseDouble(arr[2]); + double maxLon = Double.parseDouble(arr[3]); + if (arr.length == 5) { + srs = arr[4]; + } else { + srs = null; + } + + if (srs != null && !srs.equals(GeoUtils.EPSG_4326)) { + try { + // TODO optimise + CoordinateReferenceSystem sourceCRS = CRS.decode(srs); + 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); + } catch (FactoryException | TransformException e) { + throw new IllegalArgumentException("Cannot convert bounding box", e); + // bbox = null; + } + } else { + bbox = new Envelope(new Coordinate(minLat, minLon), new Coordinate(maxLat, maxLon)); + } + } else { + bbox = null; + } + + // 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 + "\""); - switch (outputFormat) { - case "application/json" -> { - exchange.getResponseHeaders().set(HttpHeader.CONTENT_TYPE.getHeaderName(), "application/json"); } - 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 typeNames = new ArrayList<>(); @@ -113,14 +187,13 @@ public class WfsHttpHandler implements HttpHandler { if (typeNames.size() > 1) throw new UnsupportedOperationException("Only one type name is currently supported"); + // QUERY Stream res = session.search((search) -> { if (cql != null) { - CqlUtils.filter(search.from(path), cql); + CqlUtils.filter(search.from(pathToUse), cql); } else { - search.from(path).where((and) -> { - }); + search.from(pathToUse); } -// search.getWhere().any((f) -> { for (QName typeName : typeNames) { FeatureAdapter featureAdapter = featureAdapters.get(typeName); if (featureAdapter == null) @@ -128,13 +201,35 @@ public class WfsHttpHandler implements HttpHandler { // f.isContentClass(typeName); featureAdapter.addConstraintsForFeature((AndFilter) search.getWhere(), typeName); } -// }); + + if (bbox != null) { + 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)) { @@ -193,19 +288,49 @@ public class WfsHttpHandler implements HttpHandler { featureAdapter = featureAdapters.get(typeName); } - Geometry defaultGeometry = featureAdapter != null ? featureAdapter.getDefaultGeometry(c, typeName) - : getDefaultGeometry(c); - if (defaultGeometry == null) - return; - generator.writeStartObject(); - generator.write("type", "Feature"); - String featureId = getFeatureId(c); - if (featureId != null) - generator.write("id", featureId); - GeoJSon.writeBBox(generator, defaultGeometry); - GeoJSon.writeGeometry(generator, defaultGeometry); - - generator.writeStartObject("properties"); + boolean geometryWritten = false; +// if (typeName.getLocalPart().equals("fieldSimpleFeature")) { +// Content area = c.getContent("place.geom.json").orElse(null); +// if (area != null) { +// generator.writeStartObject(); +// generator.write("type", "Feature"); +// String featureId = getFeatureId(c); +// if (featureId != null) +// generator.write("id", featureId); +// +// generator.flush(); +// try (InputStream in = area.open(InputStream.class)) { +// out.write(",\"geometry\":".getBytes()); +// StreamUtils.copy(in, out); +// //out.flush(); +// } catch (Exception e) { +// log.error(c.getPath() + " : " + e.getMessage()); +// } finally { +// } +// geometryWritten = true; +// }else { +// return; +// } +// } + + if (!geometryWritten) { + + Geometry defaultGeometry = featureAdapter != null ? featureAdapter.getDefaultGeometry(c, typeName) + : getDefaultGeometry(c); + if (defaultGeometry == null) + return; + generator.writeStartObject(); + generator.write("type", "Feature"); + String featureId = getFeatureId(c); + 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(GeoJson.PROPERTIES); AcrJsonUtils.writeTimeProperties(generator, c); if (featureAdapter != null) featureAdapter.writeProperties(generator, c, typeName); @@ -260,78 +385,6 @@ public class WfsHttpHandler implements HttpHandler { } - protected void encodeCollectionAsGeoJSonOld(Stream 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 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 features, OutputStream out) throws IOException { String entityType = "entity"; URL schemaLocation = getClass().getResource("/org/argeo/app/api/entity.xsd");