Skip to content

Commit

Permalink
Allowing dateline crossing requests
Browse files Browse the repository at this point in the history
  • Loading branch information
aaime committed Aug 22, 2018
1 parent 381514d commit 5a0831c
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 5 deletions.
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -8,26 +8,30 @@
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.xml.namespace.QName; import javax.xml.namespace.QName;
import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EObject;
import org.geoserver.catalog.Catalog; import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.FeatureTypeInfo; import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.catalog.NamespaceInfo; import org.geoserver.catalog.NamespaceInfo;
import org.geoserver.config.GeoServer; import org.geoserver.config.GeoServer;
import org.geoserver.platform.ServiceException;
import org.geoserver.wfs.request.Query;
import org.geoserver.wfs3.GetFeatureType; import org.geoserver.wfs3.GetFeatureType;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.util.DateRange; import org.geotools.util.DateRange;
import org.locationtech.jts.geom.Envelope;
import org.opengis.feature.type.FeatureType; import org.opengis.feature.type.FeatureType;
import org.opengis.filter.Filter; import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory; import org.opengis.filter.FilterFactory;
import org.opengis.filter.expression.Literal; import org.opengis.filter.expression.Literal;
import org.opengis.filter.expression.PropertyName; import org.opengis.filter.expression.PropertyName;
import org.opengis.filter.identity.FeatureId; import org.opengis.filter.identity.FeatureId;
import org.opengis.filter.spatial.BBOX;
import org.opengis.geometry.Envelope;


public class GetFeatureKvpRequestReader extends org.geoserver.wfs.kvp.GetFeatureKvpRequestReader { public class GetFeatureKvpRequestReader extends org.geoserver.wfs.kvp.GetFeatureKvpRequestReader {


Expand Down Expand Up @@ -81,8 +85,7 @@ private Filter getFullFilter(Map kvp) throws IOException {
filters.add(filterFactory.id(ids)); filters.add(filterFactory.id(ids));
} }
if (kvp.containsKey("bbox")) { if (kvp.containsKey("bbox")) {
Envelope bbox = (Envelope) kvp.get("bbox"); Filter bboxFilter = bboxFilter(kvp.get("bbox"));
BBOX bboxFilter = bboxFilter((org.locationtech.jts.geom.Envelope) bbox);
filters.add(bboxFilter); filters.add(bboxFilter);
} }
if (kvp.containsKey("time")) { if (kvp.containsKey("time")) {
Expand Down Expand Up @@ -162,4 +165,50 @@ private List<String> getTimeProperties(QName typeName) throws IOException {
protected void ensureMutuallyExclusive(Map kvp, String[] keys, EObject request) { protected void ensureMutuallyExclusive(Map kvp, String[] keys, EObject request) {
// no op, we actually want to handle multiple filters // no op, we actually want to handle multiple filters
} }

protected void handleBBOX(Map kvp, EObject eObject) throws Exception {
// set filter from bbox
Filter bboxFilter = bboxFilter(kvp.get("bbox"));

List<Query> queries = getQueries(eObject);
List filters = new ArrayList();

for (Iterator<Query> it = queries.iterator(); it.hasNext(); ) {
Query q = it.next();

List typeName = q.getTypeNames();
Filter filter;
if (typeName.size() > 1) {
// TODO: not sure what to do here, just going to and them up
List and = new ArrayList(typeName.size());

for (Iterator t = typeName.iterator(); t.hasNext(); ) {
and.add(bboxFilter);
}

filter = filterFactory.and(and);
} else {
filter = bboxFilter;
}

filters.add(filter);
}

querySet(eObject, "filter", filters);
}

private Filter bboxFilter(Object bbox) {
if (bbox instanceof Envelope) {
return super.bboxFilter((Envelope) bbox);
} else if (bbox instanceof ReferencedEnvelope[]) {
ReferencedEnvelope[] envelopes = (ReferencedEnvelope[]) bbox;
List<Filter> filters =
Stream.of(envelopes)
.map(e -> (Filter) super.bboxFilter(e))
.collect(Collectors.toList());
return filterFactory.or(filters);
}
throw new ServiceException(
"Internal error, did not expect to find this value for the bbox: " + bbox);
}
} }
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,65 @@
/* (c) 2017 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.geoserver.wfs.kvp.BBoxKvpParser;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.NoSuchAuthorityCodeException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;

/**
* A parser that allows dateline crossing envelopes. TODO: Same as the OpenSearch one, the class
* will eventually have to be either moved to core or merged with its based class.
*
* @author Andrea Aime
*/
public class WFS3BBoxKvpParser extends BBoxKvpParser {

@Override
protected Object buildEnvelope(
int countco,
double minx,
double miny,
double minz,
double maxx,
double maxy,
double maxz,
String srs)
throws NoSuchAuthorityCodeException, FactoryException {
if (countco > 4) {
throw new IllegalArgumentException(
"Too many coordinates, openSearch cannot handle non flat envelopes yet");
}

CoordinateReferenceSystem crs = srs == null ? null : CRS.decode(srs, true);
if (crs == null) {
crs = DefaultGeographicCRS.WGS84;
}
minx = rollLongitude(minx);
maxx = rollLongitude(maxx);

if (minx > maxx) {
// dateline crossing case
return new ReferencedEnvelope[] {
new ReferencedEnvelope(minx, 180, miny, maxy, crs),
new ReferencedEnvelope(-180, maxx, miny, maxy, crs),
};
} else {
return new ReferencedEnvelope(minx, maxx, miny, maxy, crs);
}
}

protected static double rollLongitude(final double x) {
double mod = (x + 180) % 360;
if (mod == 0) {
return x > 0 ? 180 : -180;
} else {
return mod - 180;
}
}
}
4 changes: 4 additions & 0 deletions src/community/wfs3/src/main/resources/applicationContext.xml
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@


<!-- KVP parsing --> <!-- KVP parsing -->
<bean id="wfs3TimeKvpParser" class="org.geoserver.wfs3.kvp.TimeKvpParser"/> <bean id="wfs3TimeKvpParser" class="org.geoserver.wfs3.kvp.TimeKvpParser"/>
<bean id="wfs3BBOXKvpParser" class="org.geoserver.wfs3.kvp.WFS3BBoxKvpParser">
<property name="service" value="WFS"/>
<property name="version" value="3.0.0"/>
</bean>
<bean id="apiKvpRequestReader" class="org.geoserver.wfs3.kvp.APIRequestKVPReader"/> <bean id="apiKvpRequestReader" class="org.geoserver.wfs3.kvp.APIRequestKVPReader"/>
<bean id="contentKvpRequestReader" class="org.geoserver.wfs3.kvp.LandingPageRequestKVPReader"/> <bean id="contentKvpRequestReader" class="org.geoserver.wfs3.kvp.LandingPageRequestKVPReader"/>
<bean id="collectionKvpRequestReader" class="org.geoserver.wfs3.kvp.CollectionRequestKVPReader"/> <bean id="collectionKvpRequestReader" class="org.geoserver.wfs3.kvp.CollectionRequestKVPReader"/>
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -80,6 +80,20 @@ public void testBBoxFilter() throws Exception {
1, json.read("features[?(@.id == 'PrimitiveGeoFeature.f002')]", List.class).size()); 1, json.read("features[?(@.id == 'PrimitiveGeoFeature.f002')]", List.class).size());
} }


@Test
public void testBBoxDatelineCrossingFilter() throws Exception {
String roadSegments = getEncodedName(MockData.PRIMITIVEGEOFEATURE);
DocumentContext json =
getAsJSONPath("wfs3/collections/" + roadSegments + "/items?bbox=170,0,60,3", 200);
assertEquals("FeatureCollection", json.read("type", String.class));
// should return only f002 and f003
assertEquals(2, (int) json.read("features.length()", Integer.class));
assertEquals(
1, json.read("features[?(@.id == 'PrimitiveGeoFeature.f001')]", List.class).size());
assertEquals(
1, json.read("features[?(@.id == 'PrimitiveGeoFeature.f002')]", List.class).size());
}

@Test @Test
public void testTimeFilter() throws Exception { public void testTimeFilter() throws Exception {
String roadSegments = getEncodedName(MockData.PRIMITIVEGEOFEATURE); String roadSegments = getEncodedName(MockData.PRIMITIVEGEOFEATURE);
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public Object read(Object request, Map kvp, Map rawKvp) throws Exception {
if (typeNames == null) { if (typeNames == null) {
typeNames = (List) kvp.get("typeNames"); typeNames = (List) kvp.get("typeNames");
} }
List list = new ArrayList(); List<List<QName>> list = new ArrayList<>();


for (Iterator itr = typeNames.iterator(); itr.hasNext(); ) { for (Iterator itr = typeNames.iterator(); itr.hasNext(); ) {
Object obj = itr.next(); Object obj = itr.next();
Expand All @@ -135,6 +135,7 @@ public Object read(Object request, Map kvp, Map rawKvp) throws Exception {


kvp.put("typeName", list); kvp.put("typeName", list);
querySet(eObject, "typeName", list); querySet(eObject, "typeName", list);
typeNames = list;
} else { } else {
// check for featureId and infer typeName // check for featureId and infer typeName
// in WFS 2.0 it is resourceId // in WFS 2.0 it is resourceId
Expand Down

0 comments on commit 5a0831c

Please sign in to comment.