]> git.argeo.org Git - gpl/argeo-suite.git/blob - http/WfsHttpHandler.java
Prepare next development cycle
[gpl/argeo-suite.git] / http / WfsHttpHandler.java
1 package org.argeo.app.geo.http;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.io.UncheckedIOException;
6 import java.net.URL;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.stream.Stream;
10
11 import javax.xml.namespace.QName;
12
13 import org.argeo.api.acr.Content;
14 import org.argeo.api.acr.ContentSession;
15 import org.argeo.api.acr.NamespaceUtils;
16 import org.argeo.api.acr.ldap.LdapAttr;
17 import org.argeo.api.acr.spi.ProvidedRepository;
18 import org.argeo.app.api.EntityName;
19 import org.argeo.app.api.EntityType;
20 import org.argeo.app.api.WGS84PosName;
21 import org.argeo.app.geo.CqlUtils;
22 import org.argeo.app.geo.GeoTools;
23 import org.argeo.app.geo.GpxUtils;
24 import org.argeo.cms.http.HttpHeader;
25 import org.argeo.cms.http.server.HttpServerUtils;
26 import org.geotools.data.DataUtilities;
27 import org.geotools.data.geojson.GeoJSONWriter;
28 import org.geotools.feature.DefaultFeatureCollection;
29 import org.geotools.feature.NameImpl;
30 import org.geotools.feature.SchemaException;
31 import org.geotools.feature.simple.SimpleFeatureBuilder;
32 import org.geotools.geometry.jts.JTSFactoryFinder;
33 import org.geotools.referencing.crs.DefaultGeographicCRS;
34 import org.geotools.wfs.GML;
35 import org.geotools.wfs.GML.Version;
36 import org.locationtech.jts.geom.Coordinate;
37 import org.locationtech.jts.geom.Geometry;
38 import org.locationtech.jts.geom.GeometryFactory;
39 import org.locationtech.jts.geom.Polygon;
40 import org.opengis.feature.GeometryAttribute;
41 import org.opengis.feature.simple.SimpleFeature;
42 import org.opengis.feature.simple.SimpleFeatureType;
43 import org.opengis.feature.type.AttributeDescriptor;
44 import org.opengis.feature.type.Name;
45
46 import com.sun.net.httpserver.HttpExchange;
47 import com.sun.net.httpserver.HttpHandler;
48
49 /** A partially implemented WFS 2.0 server. */
50 public class WfsHttpHandler implements HttpHandler {
51 private ProvidedRepository contentRepository;
52
53 // HTTP parameters
54 final static String OUTPUT_FORMAT = "outputFormat";
55 final static String TYPE_NAMES = "typeNames";
56 final static String CQL_FILTER = "cql_filter";
57
58 @Override
59 public void handle(HttpExchange exchange) throws IOException {
60 String path = HttpServerUtils.subPath(exchange);
61 ContentSession session = HttpServerUtils.getContentSession(contentRepository, exchange);
62 // Content content = session.get(path);
63
64 Map<String, List<String>> parameters = HttpServerUtils.parseParameters(exchange);
65 String cql = parameters.containsKey(CQL_FILTER) ? parameters.get(CQL_FILTER).get(0) : null;
66 String typeNamesStr = parameters.containsKey(TYPE_NAMES) ? parameters.get(TYPE_NAMES).get(0) : null;
67 String outputFormat = parameters.containsKey(OUTPUT_FORMAT) ? parameters.get(OUTPUT_FORMAT).get(0) : null;
68 if (outputFormat == null) {
69 outputFormat = "application/json";
70 }
71
72 switch (outputFormat) {
73 case "application/json" -> {
74 exchange.getResponseHeaders().set(HttpHeader.CONTENT_TYPE.getHeaderName(), "application/json");
75 }
76 case "GML3" -> {
77 // exchange.getResponseHeaders().set(HttpHeader.CONTENT_TYPE.getHeaderName(), "application/gml+xml");
78 exchange.getResponseHeaders().set(HttpHeader.CONTENT_TYPE.getHeaderName(), "application/xml");
79 }
80
81 default -> throw new IllegalArgumentException("Unexpected value: " + outputFormat);
82 }
83
84 QName[] typeNames;
85 if (typeNamesStr != null) {
86 String[] arr = typeNamesStr.split(",");
87 typeNames = new QName[arr.length];
88 for (int i = 0; i < arr.length; i++) {
89 typeNames[i] = NamespaceUtils.parsePrefixedName(arr[i]);
90 }
91 } else {
92 typeNames = new QName[] { EntityType.local.qName() };
93 }
94
95 Stream<Content> res = session.search((search) -> {
96 if (cql != null) {
97 CqlUtils.filter(search.from(path), cql);
98 } else {
99 search.from(path).where((and) -> {
100 });
101 }
102 search.getWhere().any((f) -> {
103 for (QName typeName : typeNames)
104 f.isContentClass(typeName);
105 });
106 });
107
108 exchange.sendResponseHeaders(200, 0);
109
110 if ("GML3".equals(outputFormat)) {
111 String entityType = "apafField";
112 URL schemaLocation = new URL("http://localhost:7070/pkg/eu.netiket.on.apaf/apaf.xsd");
113 String namespace = "http://apaf.on.netiket.eu/ns/apaf";
114
115 GML gml = new GML(Version.WFS1_1);
116 gml.setCoordinateReferenceSystem(DefaultGeographicCRS.WGS84);
117 gml.setNamespace("local", namespace);
118
119 SimpleFeatureType featureType = gml.decodeSimpleFeatureType(schemaLocation,
120 new NameImpl(namespace, entityType + "Feature"));
121
122 // CoordinateReferenceSystem crs=DefaultGeographicCRS.WGS84;
123 // QName featureName = new QName(namespace,"apafFieldFeature");
124 // GMLConfiguration configuration = new GMLConfiguration();
125 // FeatureType parsed = GTXML.parseFeatureType(configuration, featureName, crs);
126 // SimpleFeatureType featureType = DataUtilities.simple(parsed);
127
128 SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(featureType);
129
130 DefaultFeatureCollection featureCollection = new DefaultFeatureCollection();
131
132 res.forEach((c) -> {
133 // boolean gpx = false;
134 Geometry the_geom = null;
135 Polygon the_area = null;
136 // if (gpx) {
137 Content area = c.getContent("gpx/area.gpx").orElse(null);
138 if (area != null) {
139
140 try (InputStream in = area.open(InputStream.class)) {
141 the_area = GpxUtils.parseGpxTrackTo(in, Polygon.class);
142 } catch (IOException e) {
143 throw new UncheckedIOException("Cannot parse " + c, e);
144 }
145 }
146 // } else {
147 if (c.hasContentClass(EntityType.geopoint)) {
148 double latitude = c.get(WGS84PosName.lat, Double.class).get();
149 double longitude = c.get(WGS84PosName.lng, Double.class).get();
150
151 Coordinate coordinate = new Coordinate(longitude, latitude);
152 the_geom = GeoTools.GEOMETRY_FACTORY.createPoint(coordinate);
153 }
154
155 // }
156 if (the_geom != null)
157 featureBuilder.set(new NameImpl(namespace, "geopoint"), the_geom);
158 if (the_area != null)
159 featureBuilder.set(new NameImpl(namespace, "area"), the_area);
160
161 List<AttributeDescriptor> attrDescs = featureType.getAttributeDescriptors();
162 for (AttributeDescriptor attrDesc : attrDescs) {
163 if (attrDesc instanceof GeometryAttribute)
164 continue;
165 Name name = attrDesc.getName();
166 QName qName = new QName(name.getNamespaceURI(), name.getLocalPart());
167 String value = c.attr(qName);
168 if (value == null) {
169 value = c.attr(name.getLocalPart());
170 }
171 if (value != null) {
172 featureBuilder.set(name, value);
173 }
174 }
175
176 String uuid = c.attr(LdapAttr.entryUUID);
177
178 SimpleFeature feature = featureBuilder.buildFeature(uuid);
179 featureCollection.add(feature);
180
181 });
182 gml.encode(exchange.getResponseBody(), featureCollection);
183 exchange.getResponseBody().close();
184
185 } else if ("application/json".equals(outputFormat)) {
186
187 // BODY PROCESSING
188 GeoJSONWriter geoJSONWriter = new GeoJSONWriter(exchange.getResponseBody());
189 geoJSONWriter.setPrettyPrinting(true);
190
191 boolean gpx = false;
192 SimpleFeatureType TYPE;
193 try {
194 if (gpx)
195 TYPE = DataUtilities.createType("Content",
196 "the_geom:Polygon:srid=4326,path:String,type:String,name:String");
197 else
198 TYPE = DataUtilities.createType("Content",
199 "the_geom:Point:srid=4326,path:String,type:String,name:String");
200 } catch (SchemaException e) {
201 throw new RuntimeException(e);
202 }
203 SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(TYPE);
204 GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
205
206 res.forEach((c) -> {
207 Geometry the_geom;
208 if (gpx) {// experimental
209 Content area = c.getContent("gpx/area.gpx").orElse(null);
210 if (area == null)
211 return;
212 try (InputStream in = area.open(InputStream.class)) {
213 SimpleFeature feature = GpxUtils.parseGpxToPolygon(in);
214 the_geom = (Geometry) feature.getDefaultGeometry();
215 } catch (IOException e) {
216 throw new UncheckedIOException("Cannot parse " + c, e);
217 }
218 } else {
219 if (!c.hasContentClass(EntityType.geopoint))
220 return;
221
222 double latitude = c.get(WGS84PosName.lat, Double.class).get();
223 double longitude = c.get(WGS84PosName.lng, Double.class).get();
224
225 Coordinate coordinate = new Coordinate(longitude, latitude);
226 the_geom = geometryFactory.createPoint(coordinate);
227
228 }
229
230 featureBuilder.add(the_geom);
231 String pth = c.getPath();
232 featureBuilder.add(pth);
233 if (c.hasContentClass(EntityType.local)) {
234 String type = c.attr(EntityName.type);
235 featureBuilder.add(type);
236 } else {
237 List<QName> contentClasses = c.getContentClasses();
238 if (!contentClasses.isEmpty()) {
239 featureBuilder.add(NamespaceUtils.toPrefixedName(contentClasses.get(0)));
240 }
241 }
242 featureBuilder.add(NamespaceUtils.toPrefixedName(c.getName()));
243
244 String uuid = c.attr(LdapAttr.entryUUID);
245
246 SimpleFeature feature = featureBuilder.buildFeature(uuid);
247 try {
248 geoJSONWriter.write(feature);
249 } catch (IOException e) {
250 throw new UncheckedIOException(e);
251 }
252 });
253 geoJSONWriter.close();
254 }
255
256 }
257
258 public void setContentRepository(ProvidedRepository contentRepository) {
259 this.contentRepository = contentRepository;
260 }
261 }