1 package org
.argeo
.app
.geo
.http
;
3 import java
.io
.IOException
;
4 import java
.io
.InputStream
;
5 import java
.io
.UncheckedIOException
;
9 import java
.util
.stream
.Stream
;
11 import javax
.xml
.namespace
.QName
;
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
;
46 import com
.sun
.net
.httpserver
.HttpExchange
;
47 import com
.sun
.net
.httpserver
.HttpHandler
;
49 /** A partially implemented WFS 2.0 server. */
50 public class WfsHttpHandler
implements HttpHandler
{
51 private ProvidedRepository contentRepository
;
54 final static String OUTPUT_FORMAT
= "outputFormat";
55 final static String TYPE_NAMES
= "typeNames";
56 final static String CQL_FILTER
= "cql_filter";
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);
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";
72 switch (outputFormat
) {
73 case "application/json" -> {
74 exchange
.getResponseHeaders().set(HttpHeader
.CONTENT_TYPE
.getHeaderName(), "application/json");
77 // exchange.getResponseHeaders().set(HttpHeader.CONTENT_TYPE.getHeaderName(), "application/gml+xml");
78 exchange
.getResponseHeaders().set(HttpHeader
.CONTENT_TYPE
.getHeaderName(), "application/xml");
81 default -> throw new IllegalArgumentException("Unexpected value: " + outputFormat
);
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
]);
92 typeNames
= new QName
[] { EntityType
.local
.qName() };
95 Stream
<Content
> res
= session
.search((search
) -> {
97 CqlUtils
.filter(search
.from(path
), cql
);
99 search
.from(path
).where((and
) -> {
102 search
.getWhere().any((f
) -> {
103 for (QName typeName
: typeNames
)
104 f
.isContentClass(typeName
);
108 exchange
.sendResponseHeaders(200, 0);
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";
115 GML gml
= new GML(Version
.WFS1_1
);
116 gml
.setCoordinateReferenceSystem(DefaultGeographicCRS
.WGS84
);
117 gml
.setNamespace("local", namespace
);
119 SimpleFeatureType featureType
= gml
.decodeSimpleFeatureType(schemaLocation
,
120 new NameImpl(namespace
, entityType
+ "Feature"));
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);
128 SimpleFeatureBuilder featureBuilder
= new SimpleFeatureBuilder(featureType
);
130 DefaultFeatureCollection featureCollection
= new DefaultFeatureCollection();
133 // boolean gpx = false;
134 Geometry the_geom
= null;
135 Polygon the_area
= null;
137 Content area
= c
.getContent("gpx/area.gpx").orElse(null);
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
);
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();
151 Coordinate coordinate
= new Coordinate(longitude
, latitude
);
152 the_geom
= GeoTools
.GEOMETRY_FACTORY
.createPoint(coordinate
);
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
);
161 List
<AttributeDescriptor
> attrDescs
= featureType
.getAttributeDescriptors();
162 for (AttributeDescriptor attrDesc
: attrDescs
) {
163 if (attrDesc
instanceof GeometryAttribute
)
165 Name name
= attrDesc
.getName();
166 QName qName
= new QName(name
.getNamespaceURI(), name
.getLocalPart());
167 String value
= c
.attr(qName
);
169 value
= c
.attr(name
.getLocalPart());
172 featureBuilder
.set(name
, value
);
176 String uuid
= c
.attr(LdapAttr
.entryUUID
);
178 SimpleFeature feature
= featureBuilder
.buildFeature(uuid
);
179 featureCollection
.add(feature
);
182 gml
.encode(exchange
.getResponseBody(), featureCollection
);
183 exchange
.getResponseBody().close();
185 } else if ("application/json".equals(outputFormat
)) {
188 GeoJSONWriter geoJSONWriter
= new GeoJSONWriter(exchange
.getResponseBody());
189 geoJSONWriter
.setPrettyPrinting(true);
192 SimpleFeatureType TYPE
;
195 TYPE
= DataUtilities
.createType("Content",
196 "the_geom:Polygon:srid=4326,path:String,type:String,name:String");
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
);
203 SimpleFeatureBuilder featureBuilder
= new SimpleFeatureBuilder(TYPE
);
204 GeometryFactory geometryFactory
= JTSFactoryFinder
.getGeometryFactory();
208 if (gpx
) {// experimental
209 Content area
= c
.getContent("gpx/area.gpx").orElse(null);
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
);
219 if (!c
.hasContentClass(EntityType
.geopoint
))
222 double latitude
= c
.get(WGS84PosName
.lat
, Double
.class).get();
223 double longitude
= c
.get(WGS84PosName
.lng
, Double
.class).get();
225 Coordinate coordinate
= new Coordinate(longitude
, latitude
);
226 the_geom
= geometryFactory
.createPoint(coordinate
);
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
);
237 List
<QName
> contentClasses
= c
.getContentClasses();
238 if (!contentClasses
.isEmpty()) {
239 featureBuilder
.add(NamespaceUtils
.toPrefixedName(contentClasses
.get(0)));
242 featureBuilder
.add(NamespaceUtils
.toPrefixedName(c
.getName()));
244 String uuid
= c
.attr(LdapAttr
.entryUUID
);
246 SimpleFeature feature
= featureBuilder
.buildFeature(uuid
);
248 geoJSONWriter
.write(feature
);
249 } catch (IOException e
) {
250 throw new UncheckedIOException(e
);
253 geoJSONWriter
.close();
258 public void setContentRepository(ProvidedRepository contentRepository
) {
259 this.contentRepository
= contentRepository
;