X-Git-Url: http://git.argeo.org/?a=blobdiff_plain;f=org.argeo.app.geo%2Fsrc%2Forg%2Fargeo%2Fapp%2Fgeo%2Fhttp%2FWfsHttpHandler.java;h=d60d51d788249af5d334f4b632d7095da2e606a3;hb=57f3528fda509d840818a72ebcd38b6ab3afa435;hp=b10a10f4b186844500034fc65b97948c6696a648;hpb=eabf7eed1799f890337b45ab9857b1d55192cd30;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 b10a10f..d60d51d 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 @@ -1,26 +1,33 @@ package org.argeo.app.geo.http; +import java.io.BufferedOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.io.UncheckedIOException; import java.net.URL; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; 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.DName; import org.argeo.api.acr.NamespaceUtils; +import org.argeo.api.acr.QNamed; import org.argeo.api.acr.ldap.LdapAttr; import org.argeo.api.acr.spi.ProvidedRepository; +import org.argeo.api.cms.CmsLog; 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.GeoTools; +import org.argeo.app.geo.GeoJSon; import org.argeo.app.geo.GpxUtils; +import org.argeo.app.geo.JTS; import org.argeo.cms.http.HttpHeader; import org.argeo.cms.http.server.HttpServerUtils; import org.geotools.data.DataUtilities; @@ -36,6 +43,7 @@ import org.geotools.wfs.GML.Version; import org.locationtech.jts.geom.Coordinate; 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; @@ -46,8 +54,12 @@ import org.opengis.feature.type.Name; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; +import jakarta.json.Json; +import jakarta.json.stream.JsonGenerator; + /** A partially implemented WFS 2.0 server. */ public class WfsHttpHandler implements HttpHandler { + private final static CmsLog log = CmsLog.getLog(WfsHttpHandler.class); private ProvidedRepository contentRepository; // HTTP parameters @@ -107,88 +119,139 @@ public class WfsHttpHandler implements HttpHandler { exchange.sendResponseHeaders(200, 0); - if ("GML3".equals(outputFormat)) { - String entityType = "apafField"; - URL schemaLocation = new URL("http://localhost:7070/pkg/eu.netiket.on.apaf/apaf.xsd"); - String namespace = "http://apaf.on.netiket.eu/ns/apaf"; - - GML gml = new GML(Version.WFS1_1); - gml.setCoordinateReferenceSystem(DefaultGeographicCRS.WGS84); - gml.setNamespace("local", namespace); + final int BUFFER_SIZE = 100 * 1024; + try (BufferedOutputStream out = new BufferedOutputStream(exchange.getResponseBody(), BUFFER_SIZE)) { + if ("GML3".equals(outputFormat)) { + encodeCollectionAsGML(res, out); + } else if ("application/json".equals(outputFormat)) { + encodeCollectionAsGeoJSon(res, out); + } + } + } - SimpleFeatureType featureType = gml.decodeSimpleFeatureType(schemaLocation, - new NameImpl(namespace, entityType + "Feature")); + protected void encodeCollectionAsGeoJSon(Stream features, OutputStream out) throws IOException { + long begin = System.currentTimeMillis(); + AtomicLong count = new AtomicLong(0); + JsonGenerator generator = Json.createGenerator(out); + generator.writeStartObject(); + generator.write("type", "FeatureCollection"); + generator.writeStartArray("features"); + features.forEach((c) -> { + Geometry defaultGeometry = 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"); + writeTimeProperties(generator, c); + writeProperties(generator, c); + generator.writeEnd();// properties object + + generator.writeEnd();// feature object + + if (count.incrementAndGet() % 10 == 0) + try { + out.flush(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + generator.writeEnd();// features array + generator.writeEnd().close(); -// CoordinateReferenceSystem crs=DefaultGeographicCRS.WGS84; -// QName featureName = new QName(namespace,"apafFieldFeature"); -// GMLConfiguration configuration = new GMLConfiguration(); -// FeatureType parsed = GTXML.parseFeatureType(configuration, featureName, crs); -// SimpleFeatureType featureType = DataUtilities.simple(parsed); + log.debug("GeoJSon encoding took " + (System.currentTimeMillis() - begin) + " ms."); + } - SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(featureType); + protected Geometry getDefaultGeometry(Content content) { + if (content.hasContentClass(EntityType.geopoint)) { + double latitude = content.get(WGS84PosName.lat, Double.class).get(); + double longitude = content.get(WGS84PosName.lng, Double.class).get(); - DefaultFeatureCollection featureCollection = new DefaultFeatureCollection(); + Coordinate coordinate = new Coordinate(longitude, latitude); + Point the_geom = JTS.GEOMETRY_FACTORY.createPoint(coordinate); + return the_geom; + } + return null; + } - res.forEach((c) -> { -// boolean gpx = false; - Geometry the_geom = null; - Polygon the_area = null; -// if (gpx) { - Content area = c.getContent("gpx/area.gpx").orElse(null); - if (area != null) { + protected String getFeatureId(Content content) { + String uuid = content.attr(LdapAttr.entryUUID); + return uuid; + } - try (InputStream in = area.open(InputStream.class)) { - the_area = GpxUtils.parseGpxTrackTo(in, Polygon.class); - } catch (IOException e) { - throw new UncheckedIOException("Cannot parse " + c, e); - } - } -// } else { - if (c.hasContentClass(EntityType.geopoint)) { - double latitude = c.get(WGS84PosName.lat, Double.class).get(); - double longitude = c.get(WGS84PosName.lng, Double.class).get(); + private final QName JCR_CREATED = NamespaceUtils.parsePrefixedName("jcr:created"); + + private final QName JCR_LAST_MODIFIED = NamespaceUtils.parsePrefixedName("jcr:lastModified"); + + protected void writeTimeProperties(JsonGenerator g, Content content) { + String creationDate = content.attr(DName.creationdate); + if (creationDate == null) + creationDate = content.attr(JCR_CREATED); + if (creationDate != null) + g.write(DName.creationdate.get(), creationDate); + String lastModified = content.attr(DName.getlastmodified); + if (lastModified == null) + lastModified = content.attr(JCR_LAST_MODIFIED); + if (lastModified != null) + g.write(DName.getlastmodified.get(), lastModified); + } - Coordinate coordinate = new Coordinate(longitude, latitude); - the_geom = GeoTools.GEOMETRY_FACTORY.createPoint(coordinate); - } + protected void writeProperties(JsonGenerator generator, Content content) { + String path = content.getPath(); + generator.write("path", path); + if (content.hasContentClass(EntityType.local)) { + String type = content.attr(EntityName.type); + generator.write("type", type); + } else { + List contentClasses = content.getContentClasses(); + if (!contentClasses.isEmpty()) { + generator.write("type", NamespaceUtils.toPrefixedName(contentClasses.get(0))); + } + } -// } - if (the_geom != null) - featureBuilder.set(new NameImpl(namespace, "geopoint"), the_geom); - if (the_area != null) - featureBuilder.set(new NameImpl(namespace, "area"), the_area); - - List attrDescs = featureType.getAttributeDescriptors(); - for (AttributeDescriptor attrDesc : attrDescs) { - if (attrDesc instanceof GeometryAttribute) - continue; - Name name = attrDesc.getName(); - QName qName = new QName(name.getNamespaceURI(), name.getLocalPart()); - String value = c.attr(qName); - if (value == null) { - value = c.attr(name.getLocalPart()); - } - if (value != null) { - featureBuilder.set(name, value); - } - } + } - String uuid = c.attr(LdapAttr.entryUUID); + protected void writeAttr(JsonGenerator g, Content content, String attr) { + writeAttr(g, content, NamespaceUtils.parsePrefixedName(attr)); + } - SimpleFeature feature = featureBuilder.buildFeature(uuid); - featureCollection.add(feature); + protected void writeAttr(JsonGenerator g, Content content, QNamed attr) { + writeAttr(g, content, attr.qName()); + } - }); - gml.encode(exchange.getResponseBody(), featureCollection); - exchange.getResponseBody().close(); + protected void writeAttr(JsonGenerator g, Content content, QName attr) { + // String value = content.attr(attr); + Object value = content.get(attr); + if (value != null) { + // TODO specify NamespaceContext + String key = NamespaceUtils.toPrefixedName(attr); + if (value instanceof Double v) + g.write(key, v); + else if (value instanceof Long v) + g.write(key, v); + else if (value instanceof Integer v) + g.write(key, v); + else if (value instanceof Boolean v) + g.write(key, v); + else + g.write(key, value.toString()); + } + } - } else if ("application/json".equals(outputFormat)) { + protected void encodeCollectionAsGeoJSonOld(Stream features, OutputStream out) throws IOException { - // BODY PROCESSING - GeoJSONWriter geoJSONWriter = new GeoJSONWriter(exchange.getResponseBody()); + // BODY PROCESSING + try (GeoJSONWriter geoJSONWriter = new GeoJSONWriter(out)) { geoJSONWriter.setPrettyPrinting(true); + geoJSONWriter.setEncodeFeatureBounds(true); - boolean gpx = false; + boolean gpx = true; SimpleFeatureType TYPE; try { if (gpx) @@ -203,7 +266,7 @@ public class WfsHttpHandler implements HttpHandler { SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(TYPE); GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(); - res.forEach((c) -> { + features.forEach((c) -> { Geometry the_geom; if (gpx) {// experimental Content area = c.getContent("gpx/area.gpx").orElse(null); @@ -250,8 +313,83 @@ public class WfsHttpHandler implements HttpHandler { throw new UncheckedIOException(e); } }); - geoJSONWriter.close(); } + } + + protected void encodeCollectionAsGML(Stream features, OutputStream out) throws IOException { + String entityType = "entity"; + URL schemaLocation = getClass().getResource("/org/argeo/app/api/entity.xsd"); + String namespace = "http://www.argeo.org/ns/entity"; + + GML gml = new GML(Version.WFS1_1); + gml.setCoordinateReferenceSystem(DefaultGeographicCRS.WGS84); + gml.setNamespace("local", namespace); + + SimpleFeatureType featureType = gml.decodeSimpleFeatureType(schemaLocation, + new NameImpl(namespace, entityType + "Feature")); + +// CoordinateReferenceSystem crs=DefaultGeographicCRS.WGS84; +// QName featureName = new QName(namespace,"apafFieldFeature"); +// GMLConfiguration configuration = new GMLConfiguration(); +// FeatureType parsed = GTXML.parseFeatureType(configuration, featureName, crs); +// SimpleFeatureType featureType = DataUtilities.simple(parsed); + + SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(featureType); + + DefaultFeatureCollection featureCollection = new DefaultFeatureCollection(); + + features.forEach((c) -> { +// boolean gpx = false; + Geometry the_geom = null; + Polygon the_area = null; +// if (gpx) { + Content area = c.getContent("gpx/area.gpx").orElse(null); + if (area != null) { + + try (InputStream in = area.open(InputStream.class)) { + the_area = GpxUtils.parseGpxTrackTo(in, Polygon.class); + } catch (IOException e) { + throw new UncheckedIOException("Cannot parse " + c, e); + } + } +// } else { + if (c.hasContentClass(EntityType.geopoint)) { + 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 = JTS.GEOMETRY_FACTORY.createPoint(coordinate); + } + +// } + if (the_geom != null) + featureBuilder.set(new NameImpl(namespace, "geopoint"), the_geom); + if (the_area != null) + featureBuilder.set(new NameImpl(namespace, "area"), the_area); + + List attrDescs = featureType.getAttributeDescriptors(); + for (AttributeDescriptor attrDesc : attrDescs) { + if (attrDesc instanceof GeometryAttribute) + continue; + Name name = attrDesc.getName(); + QName qName = new QName(name.getNamespaceURI(), name.getLocalPart()); + String value = c.attr(qName); + if (value == null) { + value = c.attr(name.getLocalPart()); + } + if (value != null) { + featureBuilder.set(name, value); + } + } + + String uuid = c.attr(LdapAttr.entryUUID); + + SimpleFeature feature = featureBuilder.buildFeature(uuid); + featureCollection.add(feature); + + }); + gml.encode(out, featureCollection); + out.close(); }