Skip to content

Commit

Permalink
Improvements to WFS3 HTML output
Browse files Browse the repository at this point in the history
  • Loading branch information
aaime committed Jul 5, 2018
1 parent 829e82c commit fb2a110
Show file tree
Hide file tree
Showing 38 changed files with 648 additions and 286 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,17 @@
*/
package org.geoserver.wfs3;

import javax.xml.namespace.QName;

/** Request for the server contents */
public class CollectionRequest extends BaseRequest {}
public class CollectionRequest extends BaseRequest {
QName typeName;

public QName getTypeName() {
return typeName;
}

public void setTypeName(QName typeName) {
this.typeName = typeName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,5 @@
*/
package org.geoserver.wfs3;

import javax.xml.namespace.QName;

/** Request for the server contents */
public class CollectionsRequest extends BaseRequest {

QName typeName;

public QName getTypeName() {
return typeName;
}

public void setTypeName(QName typeName) {
this.typeName = typeName;
}
}
public class CollectionsRequest extends BaseRequest {}
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@
import org.geoserver.ows.Response;
import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.platform.ServiceException;
import org.geoserver.wfs.StoredQueryProvider;
import org.geoserver.wfs.WFSInfo;
import org.geoserver.wfs.WebFeatureService20;
import org.geoserver.wfs.request.FeatureCollectionResponse;
import org.geoserver.wfs.request.GetFeatureRequest;
import org.geoserver.wfs3.response.CollectionDocument;
import org.geoserver.wfs3.response.CollectionsDocument;
import org.geoserver.wfs3.response.ConformanceDocument;
import org.geoserver.wfs3.response.LandingPageDocument;
Expand Down Expand Up @@ -65,23 +68,25 @@ public LandingPageDocument landingPage(LandingPageRequest request) {

@Override
public CollectionsDocument collections(CollectionsRequest request) {
if (request.getTypeName() == null) {
// all collections
return new CollectionsDocument(request, getService(), getCatalog());
return new CollectionsDocument(request, geoServer);
}

@Override
public CollectionDocument collection(CollectionRequest request) {
// single collection
QName typeName = request.getTypeName();
NamespaceInfo ns = getCatalog().getNamespaceByURI(typeName.getNamespaceURI());
FeatureTypeInfo featureType =
getCatalog().getFeatureTypeByName(ns, typeName.getLocalPart());
if (featureType == null) {
throw new ServiceException(
"Unknown collection " + typeName,
ServiceException.INVALID_PARAMETER_VALUE,
"typeName");
} else {
// single collection
QName typeName = request.getTypeName();
NamespaceInfo ns = getCatalog().getNamespaceByURI(typeName.getNamespaceURI());
FeatureTypeInfo featureType =
getCatalog().getFeatureTypeByName(ns, typeName.getLocalPart());
if (featureType == null) {
throw new ServiceException(
"Unknown collection " + typeName,
ServiceException.INVALID_PARAMETER_VALUE,
"typeName");
} else {
return new CollectionsDocument(request, getService(), getCatalog(), featureType);
}
CollectionsDocument collections =
new CollectionsDocument(request, geoServer, featureType);
return collections.getCollections().next();
}
}

Expand Down Expand Up @@ -109,11 +114,18 @@ public FeatureCollectionResponse getFeature(org.geoserver.wfs3.GetFeatureType re
request.setStartIndex(BigInteger.ZERO);
}

// delegate execution to WFS 2.0
FeatureCollectionResponse response = wfs20.getFeature(request);
WFS3GetFeature gf = new WFS3GetFeature(getServiceInfo(), getCatalog());
gf.setFilterFactory(filterFactory);
gf.setStoredQueryProvider(getStoredQueryProvider());
FeatureCollectionResponse response = gf.run(new GetFeatureRequest.WFS20(request));

return response;
}

private StoredQueryProvider getStoredQueryProvider() {
return new StoredQueryProvider(getCatalog());
}

/**
* Returns a selection of supported formats for a given response object
*
Expand Down Expand Up @@ -145,4 +157,8 @@ public static List<String> getAvailableFormats(Class responseType) {
public OpenAPI api(APIRequest request) {
return new OpenAPIBuilder().build(request, getService());
}

public WFSInfo getServiceInfo() {
return geoServer.getService(WFSInfo.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ private RequestWrapper(HttpServletRequest wrapped) {
Pattern.compile("/collections/([^/]+)/?").matcher(path);
boolean matches = matcher.matches();
if (matches) {
request = "collections";
request = "collection";
String layerName = matcher.group(1);
setLayerName(layerName);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* (c) 2018 Open Source Geospatial Foundation - all rights reserved
* * This code is licensed under the GPL 2.0 license, available at the root
* * application directory.
*
*/

package org.geoserver.wfs3;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.ows.URLMangler.URLType;
import org.geoserver.ows.util.CaseInsensitiveMap;
import org.geoserver.ows.util.ResponseUtils;
import org.geoserver.wfs.WFSInfo;
import org.geoserver.wfs.request.FeatureCollectionResponse;
import org.geoserver.wfs.request.GetFeatureRequest;
import org.geotools.util.logging.Logging;

/** A subclass of GetFeature that builds proper WFS3 nex/prev links */
class WFS3GetFeature extends org.geoserver.wfs.GetFeature {

static final Logger LOGGER = Logging.getLogger(WFS3GetFeature.class);

public WFS3GetFeature(WFSInfo wfs, Catalog catalog) {
super(wfs, catalog);
}

@Override
protected void buildPrevNextLinks(
GetFeatureRequest request,
int offset,
int maxFeatures,
int count,
FeatureCollectionResponse result,
Map<String, String> kvp) {
// can we build the links?
String typename = kvp.get("TYPENAME");
if (typename == null) {
LOGGER.log(
Level.INFO,
"Cannot build prev/next links, the the target typename is not known");
return;
}
FeatureTypeInfo typeInfo = getCatalog().getFeatureTypeByName(typename);
if (typeInfo == null) {
if (typename == null) {
LOGGER.log(
Level.INFO,
"Cannot build prev/next links, the the target typename was not found: "
+ typename);
return;
}
}
String collectionName = NCNameResourceCodec.encode(typeInfo);
String itemsPath = "wfs3/collections/" + collectionName + "/items";

// clean up the KVP params, remove the ones that are not WFS3 specific
List<String> PARAMS_BLACKLIST =
Arrays.asList(
"SERVICE",
"VERSION",
"REQUEST",
"TYPENAME",
"COUNT",
"OUTPUTFORMAT",
"STARTINDEX",
"LIMIT");
kvp = new CaseInsensitiveMap(kvp);
for (String param : PARAMS_BLACKLIST) {
kvp.remove(param);
}
// remove the SRSNAME if its value is 4326
if ("EPSG:4326".equals(kvp.get("SRSNAME"))) {
kvp.remove("SRSNAME");
}

// in WFS3 params are normally lowercase (and are case sensitive)...
// TODO: we might need a list of parameters and their "normalized case" for WFS3, we'll
// wait for the filtering/crs extensions to show up before deciding exactly what exactly to
// do
kvp =
kvp.entrySet()
.stream()
.collect(
Collectors.toMap(
entry -> entry.getKey().toLowerCase(),
entry -> entry.getValue()));

// build prev link if needed
if (offset > 0) {
// previous offset calculated as the current offset - maxFeatures, or 0 if this is a
// negative value, while previous count should be current offset - previousOffset
int prevOffset = Math.max(offset - maxFeatures, 0);
kvp.put("startIndex", String.valueOf(prevOffset));
kvp.put("limit", String.valueOf(offset - prevOffset));
result.setPrevious(buildURL(request, itemsPath, kvp));
}

// build next link if needed
if (count > 0 && offset > -1 && maxFeatures <= count) {
kvp.put("startIndex", String.valueOf(offset > 0 ? offset + count : count));
kvp.put("limit", String.valueOf(maxFeatures));
result.setNext(buildURL(request, itemsPath, kvp));
}
}

private String buildURL(GetFeatureRequest request, String itemsPath, Map<String, String> kvp) {
return ResponseUtils.buildURL(request.getBaseUrl(), itemsPath, kvp, URLType.SERVICE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,18 @@ public interface WebFeatureService30 {
* Returns a description of the collection(s)
*
* @param request A {@link CollectionRequest}
* @return A {@link CollectionDocument} or a {@link CollectionsDocument} depending on the
* request
* @return A {@link CollectionsDocument} depending on the request
*/
CollectionsDocument collections(CollectionsRequest request);

/**
* Returns a description of a single collection
*
* @param request A {@link CollectionRequest}
* @return A {@link CollectionDocument}
*/
CollectionDocument collection(CollectionRequest request);

/**
* The OpenAPI description of the service
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/* (c) 2018 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.wfs3.kvp;

import java.util.ArrayList;
import java.util.Map;
import javax.xml.namespace.QName;
import org.geoserver.wfs3.CollectionRequest;

/** Parses a "collection" request */
public class CollectionRequestKVPReader extends BaseKvpRequestReader {

public CollectionRequestKVPReader() {
super(CollectionRequest.class);
}

@Override
public Object read(Object request, Map kvp, Map rawKvp) throws Exception {
CollectionRequest cr = (CollectionRequest) super.read(request, kvp, rawKvp);
Object objTypeName = kvp.get("TYPENAME");
if (objTypeName instanceof ArrayList) {
QName name = (QName) ((ArrayList) objTypeName).get(0);
cr.setTypeName(name);
}

return cr;
}
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,22 @@
/*
* (c) 2018 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*
*/

/* (c) 2018 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.wfs3.kvp;

import java.util.ArrayList;
import java.util.Map;
import javax.xml.namespace.QName;
import org.geoserver.wfs3.CollectionsRequest;

/** Parses a "content" request */
/** Parses a "collections" request */
public class CollectionsRequestKVPReader extends BaseKvpRequestReader {

public CollectionsRequestKVPReader() {
super(CollectionsRequest.class);
}

@Override
public Object read(Object request, Map kvp, Map rawKvp) throws Exception {
CollectionsRequest cr = (CollectionsRequest) super.read(request, kvp, rawKvp);
Object objTypeName = kvp.get("TYPENAME");
if (objTypeName instanceof ArrayList) {
QName name = (QName) ((ArrayList) objTypeName).get(0);
cr.setTypeName(name);
}

return cr;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public abstract class AbstractHTMLResponse extends Response {

private static Configuration templateConfig = new Configuration();

private final GeoServer geoServer;
protected final GeoServer geoServer;
private FreemarkerTemplateSupport templateSupport;

public AbstractHTMLResponse(
Expand Down

0 comments on commit fb2a110

Please sign in to comment.