-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Basic time filtering and composition of filters in AND when multiple …
…are found
- Loading branch information
Showing
9 changed files
with
294 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 14 additions & 0 deletions
14
src/community/wfs3/src/main/java/org/geoserver/wfs3/GetFeatureType.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,14 @@ | |||
/* (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 net.opengis.wfs20.impl.GetFeatureTypeImpl; | |||
|
|||
/** | |||
* This class extends WFS 2.0 GetFeatureType just to allow having a custom KVP reader for it | |||
*/ | |||
public class GetFeatureType extends GetFeatureTypeImpl { | |||
|
|||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
161 changes: 161 additions & 0 deletions
161
src/community/wfs3/src/main/java/org/geoserver/wfs3/kvp/GetFeatureKvpRequestReader.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,161 @@ | |||
/* (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 org.eclipse.emf.ecore.EObject; | |||
import org.geoserver.catalog.Catalog; | |||
import org.geoserver.catalog.FeatureTypeInfo; | |||
import org.geoserver.catalog.NamespaceInfo; | |||
import org.geoserver.config.GeoServer; | |||
import org.geoserver.wfs3.GetFeatureType; | |||
import org.geotools.util.DateRange; | |||
import org.opengis.feature.type.FeatureType; | |||
import org.opengis.filter.Filter; | |||
import org.opengis.filter.FilterFactory; | |||
import org.opengis.filter.expression.Literal; | |||
import org.opengis.filter.expression.PropertyName; | |||
import org.opengis.filter.identity.FeatureId; | |||
import org.opengis.filter.spatial.BBOX; | |||
import org.opengis.geometry.Envelope; | |||
|
|||
import javax.xml.namespace.QName; | |||
import java.io.IOException; | |||
import java.util.ArrayList; | |||
import java.util.Collections; | |||
import java.util.Date; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import java.util.stream.Collectors; | |||
|
|||
public class GetFeatureKvpRequestReader extends org.geoserver.wfs.kvp.GetFeatureKvpRequestReader { | |||
|
|||
public GetFeatureKvpRequestReader(GeoServer geoServer, FilterFactory filterFactory) { | |||
super(GetFeatureType.class, null, geoServer, filterFactory); | |||
} | |||
|
|||
@Override | |||
public Object read(Object request, Map kvp, Map rawKvp) throws Exception { | |||
GetFeatureType gf = (GetFeatureType) super.read(request, kvp, rawKvp); | |||
Filter filter = getFullFilter(kvp); | |||
querySet(gf, "filter", Collections.singletonList(filter)); | |||
return gf; | |||
} | |||
|
|||
@Override | |||
public Object createRequest() { | |||
return new org.geoserver.wfs3.GetFeatureType(); | |||
} | |||
|
|||
/** | |||
* Finds all filter expressions and combines them in "AND" | |||
* | |||
* @param kvp | |||
* @return | |||
*/ | |||
private Filter getFullFilter(Map kvp) throws IOException { | |||
List<Filter> filters = new ArrayList<>(); | |||
// check the various filters, considering that only one feature type at a time can be | |||
// used in WFS3 | |||
if (kvp.containsKey("filter")) { | |||
List<Filter> list = (List) kvp.get("filter"); | |||
filters.add(list.get(0)); | |||
} | |||
if (kvp.containsKey("cql_filter")) { | |||
List<Filter> list = (List) kvp.get("cql_filter"); | |||
filters.add(list.get(0)); | |||
} | |||
if (kvp.containsKey("featureId") || kvp.containsKey("resourceId")) { | |||
List<String> featureIdList = (List) kvp.get("featureId"); | |||
Set<FeatureId> ids = | |||
featureIdList | |||
.stream() | |||
.map(id -> filterFactory.featureId(id)) | |||
.collect(Collectors.toSet()); | |||
filters.add(filterFactory.id(ids)); | |||
} | |||
if (kvp.containsKey("bbox")) { | |||
Envelope bbox = (Envelope) kvp.get("bbox"); | |||
BBOX bboxFilter = bboxFilter((com.vividsolutions.jts.geom.Envelope) bbox); | |||
filters.add(bboxFilter); | |||
} | |||
if (kvp.containsKey("time")) { | |||
Object timeSpecification = kvp.get("time"); | |||
QName typeName = (QName) ((List) ((List) kvp.get("typeName")).get(0)).get(0); | |||
List<String> timeProperties = getTimeProperties(typeName); | |||
Filter filter = buildTimeFilter(timeSpecification, timeProperties); | |||
filters.add(filter); | |||
} | |||
return mergeFiltersAnd(filters); | |||
} | |||
|
|||
private Filter buildTimeFilter(Object timeSpec, List<String> timeProperties) { | |||
List<Filter> filters = new ArrayList<>(); | |||
for (String timeProperty : timeProperties) { | |||
PropertyName property = filterFactory.property(timeProperty); | |||
Filter filter; | |||
if (timeSpec instanceof Date) { | |||
filter = filterFactory.equals(property, filterFactory.literal(timeSpec)); | |||
} else if (timeSpec instanceof DateRange) { | |||
Literal before = filterFactory.literal(((DateRange) timeSpec).getMinValue()); | |||
Literal after = filterFactory.literal(((DateRange) timeSpec).getMaxValue()); | |||
filter = filterFactory.between(property, before, after); | |||
} else { | |||
throw new IllegalArgumentException("Cannot build time filter out of " + timeSpec); | |||
} | |||
|
|||
filters.add(filter); | |||
} | |||
|
|||
return mergeFiltersOr(filters); | |||
} | |||
|
|||
private Filter mergeFiltersAnd(List<Filter> filters) { | |||
if (filters.isEmpty()) { | |||
return Filter.INCLUDE; | |||
} else if (filters.size() == 1) { | |||
return filters.get(0); | |||
} else { | |||
return filterFactory.and(filters); | |||
} | |||
} | |||
|
|||
private Filter mergeFiltersOr(List<Filter> filters) { | |||
if (filters.isEmpty()) { | |||
return Filter.EXCLUDE; | |||
} else if (filters.size() == 1) { | |||
return filters.get(0); | |||
} else { | |||
return filterFactory.or(filters); | |||
} | |||
} | |||
|
|||
private List<String> getTimeProperties(QName typeName) throws IOException { | |||
Catalog catalog = geoServer.getCatalog(); | |||
NamespaceInfo ns = catalog.getNamespaceByURI(typeName.getNamespaceURI()); | |||
FeatureTypeInfo ft = catalog.getFeatureTypeByName(ns, typeName.getLocalPart()); | |||
if (ft == null) { | |||
return Collections.emptyList(); | |||
} | |||
FeatureType schema = ft.getFeatureType(); | |||
return schema.getDescriptors() | |||
.stream() | |||
.filter(pd -> Date.class.isAssignableFrom(pd.getType().getBinding())) | |||
.map(pd -> pd.getName().getLocalPart()) | |||
.collect(Collectors.toList()); | |||
} | |||
|
|||
/** | |||
* In WFS3 it's possible to have multiple filter KVPs, and they have to be combined in AND | |||
* | |||
* @param kvp | |||
* @param keys | |||
* @param request | |||
*/ | |||
@Override | |||
protected void ensureMutuallyExclusive(Map kvp, String[] keys, EObject request) { | |||
// no op, we actually want to handle multiple filters | |||
} | |||
} |
44 changes: 44 additions & 0 deletions
44
src/community/wfs3/src/main/java/org/geoserver/wfs3/kvp/TimeKvpParser.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,44 @@ | |||
package org.geoserver.wfs3.kvp; | |||
|
|||
import org.geoserver.ows.KvpParser; | |||
import org.geoserver.ows.kvp.TimeParser; | |||
import org.geoserver.platform.ServiceException; | |||
import org.geotools.util.Version; | |||
|
|||
import java.text.ParseException; | |||
import java.util.Collection; | |||
import java.util.List; | |||
|
|||
/** | |||
* WFS specific version of time parsing, turns a time spec into a single time or date range | |||
*/ | |||
public class TimeKvpParser extends KvpParser { | |||
|
|||
TimeParser parser = new TimeParser(); | |||
|
|||
/** | |||
* Creates the parser specifying the name of the key to latch to. | |||
* | |||
* @param key The key whose associated value to parse. | |||
*/ | |||
public TimeKvpParser() { | |||
super("time", Object.class); | |||
setVersion(new Version("3.0.0")); | |||
setService("WFS"); | |||
} | |||
|
|||
@SuppressWarnings({ "unchecked", "rawtypes" }) | |||
public Object parse(String value) throws ParseException { | |||
Collection times = parser.parse(value); | |||
if (times.isEmpty() || times.size() > 1) { | |||
throw new ServiceException( | |||
"Invalid time specification, must be a single time, or a time range", | |||
ServiceException.INVALID_PARAMETER_VALUE, | |||
"time"); | |||
} | |||
|
|||
return times.iterator().next(); | |||
} | |||
|
|||
|
|||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.