[Geoserver-users] WFS GetFeature with a query limit

hello all,

i'm seeing different behavior in the response to a WFS GetFeature
request when i specify a query limit v/s when i don't but only with
WFS 1.1.0; i.e. 1.0.0 returns the same (correct) result in both
cases. i'm including the test case i used to demonstrate the issue.
the code uses the GeoTools libraries version 2.5.3 and communicates
with a GeoServer 1.6.4b.

my questions which i'd appreciate an answer or guidance from a
GeoServer developer are:

* is this a known issue or i'm doing the wrong thing? if it's the
  latter a push in the right direction is much appreciated.

* i thought of using the buffer-limit attribute with a WFS data
  store in the hope to limit the output and allow the client side
  code to close the store before getting _all_ the features this
  also does not seem to fix the issue. again is it a known issue
  or should i file a bug report.

* in the log/trace file with WFS 1.1 i see the following two
  statements:

    Supported filter: Filter.INCLUDE
    Unsupported filter: Filter.INCLUDE

  is it safe to ignore those statements?

here is the JUnit test case code:

// default package

import java.io.IOException;
import java.io.Serializable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import junit.framework.TestCase;

import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.DefaultQuery;
import org.geotools.data.FeatureSource;
import org.geotools.data.wfs.WFSDataStore;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;

public class TestIncludeFilter extends TestCase {
   private static final String BASE_URL =
         "http://localhost:8080/geoserver/wfs?SERVICE=WFS&REQUEST=GetCapabilities";
   private static final String CAPABILITIES_1_0 = BASE_URL + "&VERSION=1.0.0";
   private static final String CAPABILITIES_1_1 = BASE_URL + "&VERSION=1.1.0";
   private static final String LAYER_NAME = "topp:states";
   private static final String ATTRIBUTE_NAMES = new String {
         "STATE_NAME", "STATE_FIPS", "SUB_REGION" };
   private static final int QUERY_LIMIT = 15;

   // default constructor

   public void testVersion1_0() throws IOException {
      // Step 1 - connection parameters
      final Map<String, Serializable> connectionParams =
            new HashMap<String, Serializable>();
      connectionParams.put(
            "WFSDataStoreFactory:GET_CAPABILITIES_URL", CAPABILITIES_1_0);
      _test(connectionParams, 0);
      _test(connectionParams, QUERY_LIMIT);
   }

   public void testVersion1_1_POST() throws IOException {
      // Step 1 - connection parameters
      final Map<String, Serializable> connectionParams =
            new HashMap<String, Serializable>();
      connectionParams.put(
            "WFSDataStoreFactory:GET_CAPABILITIES_URL", CAPABILITIES_1_1);
// connectionParams.put(
// "WFSDataStoreFactory:BUFFER_SIZE", 5);
      connectionParams.put("WFSDataStoreFactory:PROTOCOL", Boolean.TRUE);
      _test(connectionParams, 0);
      _test(connectionParams, QUERY_LIMIT);
   }

   public void
   _test(final Map<String, Serializable> connectionParams, final int limit)
   throws IOException {
      // Step 2 - connection
      final WFSDataStore ds =
            (WFSDataStore) DataStoreFinder.getDataStore(connectionParams);
      assertNotNull("Data store MUST NOT be null", ds);

      // Step 3 - discovery
      final List<String> typeNames = Arrays.asList(ds.getTypeNames());
      assertNotNull("Type names MUST NOT be null", typeNames);
      assertFalse("Type names MUST NOT be empty", typeNames.isEmpty());
      assertTrue(
            "Type names MUST contain [" + LAYER_NAME + "]",
            typeNames.contains(LAYER_NAME));
      final SimpleFeatureType schema = ds.getSchema(LAYER_NAME);
      assertNotNull("Feature type (schema) MUST NOT be null", schema);
      final List<AttributeDescriptor> attributes =
            schema.getAttributeDescriptors();
      assertNotNull("Attributes list MUST NOT be null", attributes);
      assertTrue(
            "Attributes list MUST contain at least 3 elements",
            attributes.size() > 2);
      for (final String atName : ATTRIBUTE_NAMES)
         assertTrue(
               "Attribute named [" + atName + "] MUST be found",
               contains(attributes, atName));

      // Step 4 - query
      final int result = process(ds, limit);
      System.out.println("*** result = " + result);
      assertTrue("MUST be able to find at least one feature", result > 0);
   }

   private boolean
   contains(final List<AttributeDescriptor> attributes, final String atName) {
      boolean result = false;
      for (final AttributeDescriptor attribute : attributes)
         if (attribute.getLocalName().equals(atName)) {
            result = true;
            break;
         }
      return result;
   }

   private int process(final DataStore dataStore, final int limit)
   throws IOException {
      final DefaultQuery q = new DefaultQuery(LAYER_NAME);
      q.setPropertyNames(ATTRIBUTE_NAMES);
      if (limit > 0) q.setMaxFeatures(QUERY_LIMIT);

      final FeatureSource<SimpleFeatureType, SimpleFeature> fs =
            dataStore.getFeatureSource(LAYER_NAME);
      final FeatureCollection<SimpleFeatureType, SimpleFeature> fc =
            fs.getFeatures(q);
      FeatureIterator<SimpleFeature> fit = null;
      int result = 0;
      try {
         for (fit = fc.features(); fit.hasNext():wink: {
            fit.next();
            result++;
         }
      } finally {
         if (fit != null) fc.close(fit);
      }
      System.out.println("*** INCLUDE filter, for "
            + ATTRIBUTE_NAMES.length + " attributes, with "
            + (limit == 0 ? "NO limit" : "a limit of (" + limit + ")")
            + ", found " + result + " feature(s)");
      return result;
   }
}

TIA + cheers;
rsn

Hi Raif,

I ran the test case you supplied (thanks btw, it was nicely laid out :)) and was able to reproduce the problem.

This is an issue with the wfs 1.1 datastore in geotools. In particular line 367 of WFS_1_1_0_DataStore:

The code as written:

int maxFeatures = Math.min(maxFeaturesHardLimit.intValue(),
       query.getMaxFeatures());

However, maxFeaturesHardLimit == 0 means it is unset. Which means whenever the client does not set a hard limit, and specifies a query limit it will always be zero. A simple fix:

int maxFeatures = maxFeaturesHardLimit.intValue() > 0 ?
     Math.min(maxFeaturesHardLimit.intValue(), query.getMaxFeatures()) :

     query.getMaxFeatures()

Feel free to open a jira issue in geotools for this. Gabriel, if you are ok with the fix I will happily commit.

-Justin

Raif S. Naffah wrote:

hello all,

i'm seeing different behavior in the response to a WFS GetFeature
request when i specify a query limit v/s when i don't but only with
WFS 1.1.0; i.e. 1.0.0 returns the same (correct) result in both
cases. i'm including the test case i used to demonstrate the issue.
the code uses the GeoTools libraries version 2.5.3 and communicates
with a GeoServer 1.6.4b.

my questions which i'd appreciate an answer or guidance from a
GeoServer developer are:

* is this a known issue or i'm doing the wrong thing? if it's the
  latter a push in the right direction is much appreciated.

* i thought of using the buffer-limit attribute with a WFS data
  store in the hope to limit the output and allow the client side
  code to close the store before getting _all_ the features this
  also does not seem to fix the issue. again is it a known issue
  or should i file a bug report.

* in the log/trace file with WFS 1.1 i see the following two
  statements:

    Supported filter: Filter.INCLUDE
    Unsupported filter: Filter.INCLUDE

  is it safe to ignore those statements?

here is the JUnit test case code:

// default package

import java.io.IOException;
import java.io.Serializable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import junit.framework.TestCase;

import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.DefaultQuery;
import org.geotools.data.FeatureSource;
import org.geotools.data.wfs.WFSDataStore;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;

public class TestIncludeFilter extends TestCase {
   private static final String BASE_URL =
         "http://localhost:8080/geoserver/wfs?SERVICE=WFS&REQUEST=GetCapabilities&quot;;
   private static final String CAPABILITIES_1_0 = BASE_URL + "&VERSION=1.0.0";
   private static final String CAPABILITIES_1_1 = BASE_URL + "&VERSION=1.1.0";
   private static final String LAYER_NAME = "topp:states";
   private static final String ATTRIBUTE_NAMES = new String {
         "STATE_NAME", "STATE_FIPS", "SUB_REGION" };
   private static final int QUERY_LIMIT = 15;

   // default constructor

   public void testVersion1_0() throws IOException {
      // Step 1 - connection parameters
      final Map<String, Serializable> connectionParams =
            new HashMap<String, Serializable>();
      connectionParams.put(
            "WFSDataStoreFactory:GET_CAPABILITIES_URL", CAPABILITIES_1_0);
      _test(connectionParams, 0);
      _test(connectionParams, QUERY_LIMIT);
   }

   public void testVersion1_1_POST() throws IOException {
      // Step 1 - connection parameters
      final Map<String, Serializable> connectionParams =
            new HashMap<String, Serializable>();
      connectionParams.put(
            "WFSDataStoreFactory:GET_CAPABILITIES_URL", CAPABILITIES_1_1);
// connectionParams.put(
// "WFSDataStoreFactory:BUFFER_SIZE", 5);
      connectionParams.put("WFSDataStoreFactory:PROTOCOL", Boolean.TRUE);
      _test(connectionParams, 0);
      _test(connectionParams, QUERY_LIMIT);
   }

   public void
   _test(final Map<String, Serializable> connectionParams, final int limit)
   throws IOException {
      // Step 2 - connection
      final WFSDataStore ds =
            (WFSDataStore) DataStoreFinder.getDataStore(connectionParams);
      assertNotNull("Data store MUST NOT be null", ds);

      // Step 3 - discovery
      final List<String> typeNames = Arrays.asList(ds.getTypeNames());
      assertNotNull("Type names MUST NOT be null", typeNames);
      assertFalse("Type names MUST NOT be empty", typeNames.isEmpty());
      assertTrue(
            "Type names MUST contain [" + LAYER_NAME + "]",
            typeNames.contains(LAYER_NAME));
      final SimpleFeatureType schema = ds.getSchema(LAYER_NAME);
      assertNotNull("Feature type (schema) MUST NOT be null", schema);
      final List<AttributeDescriptor> attributes =
            schema.getAttributeDescriptors();
      assertNotNull("Attributes list MUST NOT be null", attributes);
      assertTrue(
            "Attributes list MUST contain at least 3 elements",
            attributes.size() > 2);
      for (final String atName : ATTRIBUTE_NAMES)
         assertTrue(
               "Attribute named [" + atName + "] MUST be found",
               contains(attributes, atName));

      // Step 4 - query
      final int result = process(ds, limit);
      System.out.println("*** result = " + result);
      assertTrue("MUST be able to find at least one feature", result > 0);
   }

   private boolean
   contains(final List<AttributeDescriptor> attributes, final String atName) {
      boolean result = false;
      for (final AttributeDescriptor attribute : attributes)
         if (attribute.getLocalName().equals(atName)) {
            result = true;
            break;
         }
      return result;
   }

   private int process(final DataStore dataStore, final int limit)
   throws IOException {
      final DefaultQuery q = new DefaultQuery(LAYER_NAME);
      q.setPropertyNames(ATTRIBUTE_NAMES);
      if (limit > 0) q.setMaxFeatures(QUERY_LIMIT);

      final FeatureSource<SimpleFeatureType, SimpleFeature> fs =
            dataStore.getFeatureSource(LAYER_NAME);
      final FeatureCollection<SimpleFeatureType, SimpleFeature> fc =
            fs.getFeatures(q);
      FeatureIterator<SimpleFeature> fit = null;
      int result = 0;
      try {
         for (fit = fc.features(); fit.hasNext():wink: {
            fit.next();
            result++;
         }
      } finally {
         if (fit != null) fc.close(fit);
      }
      System.out.println("*** INCLUDE filter, for "
            + ATTRIBUTE_NAMES.length + " attributes, with "
            + (limit == 0 ? "NO limit" : "a limit of (" + limit + ")")
            + ", found " + result + " feature(s)");
      return result;
   }
}

TIA + cheers;
rsn

------------------------------------------------------------------------

------------------------------------------------------------------------------
Open Source Business Conference (OSBC), March 24-25, 2009, San Francisco, CA
-OSBC tackles the biggest issue in open source: Open Sourcing the Enterprise
-Strategies to boost innovation and cut costs with open source participation
-Receive a $600 discount off the registration fee with the source code: SFAD
http://p.sf.net/sfu/XcvMzF8H

------------------------------------------------------------------------

_______________________________________________
Geoserver-users mailing list
Geoserver-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/geoserver-users

--
Justin Deoliveira
OpenGeo - http://opengeo.org
Enterprise support for open source geospatial.

hello Justin,

thank you muchly for your prompt reply! i'll open a JIRA issue just to
be able to track the version of gt/geoserver which will contain the
fix.

On Thu, 19 Feb 2009 03:53:40 pm Justin Deoliveira wrote:

Hi Raif,

I ran the test case you supplied (thanks btw, it was nicely laid out
:)) and was able to reproduce the problem.

This is an issue with the wfs 1.1 datastore in geotools. In
particular line 367 of WFS_1_1_0_DataStore:

The code as written:

int maxFeatures = Math.min(maxFeaturesHardLimit.intValue(),
       query.getMaxFeatures());

However, maxFeaturesHardLimit == 0 means it is unset. Which means
whenever the client does not set a hard limit, and specifies a query
limit it will always be zero. A simple fix:

int maxFeatures = maxFeaturesHardLimit.intValue() > 0 ?
     Math.min(maxFeaturesHardLimit.intValue(),
query.getMaxFeatures()) :

     query.getMaxFeatures()

Feel free to open a jira issue in geotools for this. Gabriel, if you
are ok with the fix I will happily commit.

-Justin

Raif S. Naffah wrote:
> hello all,
>
> i'm seeing different behavior in the response to a WFS GetFeature
> request when i specify a query limit v/s when i don't but only with
> WFS 1.1.0; i.e. 1.0.0 returns the same (correct) result in both
> cases. i'm including the test case i used to demonstrate the
> issue. the code uses the GeoTools libraries version 2.5.3 and
> communicates with a GeoServer 1.6.4b.
>
> my questions which i'd appreciate an answer or guidance from a
> GeoServer developer are:
>
> * is this a known issue or i'm doing the wrong thing? if it's the
> latter a push in the right direction is much appreciated.
>
> * i thought of using the buffer-limit attribute with a WFS data
> store in the hope to limit the output and allow the client side
> code to close the store before getting _all_ the features this
> also does not seem to fix the issue. again is it a known issue
> or should i file a bug report.
>
> * in the log/trace file with WFS 1.1 i see the following two
> statements:
>
> Supported filter: Filter.INCLUDE
> Unsupported filter: Filter.INCLUDE
>
> is it safe to ignore those statements?
>
>
> here is the JUnit test case code:
>
> // default package
>
> import java.io.IOException;
> import java.io.Serializable;
> import java.util.Arrays;
> import java.util.HashMap;
> import java.util.List;
> import java.util.Map;
>
> import junit.framework.TestCase;
>
> import org.geotools.data.DataStore;
> import org.geotools.data.DataStoreFinder;
> import org.geotools.data.DefaultQuery;
> import org.geotools.data.FeatureSource;
> import org.geotools.data.wfs.WFSDataStore;
> import org.geotools.feature.FeatureCollection;
> import org.geotools.feature.FeatureIterator;
> import org.opengis.feature.simple.SimpleFeature;
> import org.opengis.feature.simple.SimpleFeatureType;
> import org.opengis.feature.type.AttributeDescriptor;
>
> public class TestIncludeFilter extends TestCase {
> private static final String BASE_URL =
>
> "http://localhost:8080/geoserver/wfs?SERVICE=WFS&REQUEST=GetCapabil
>ities"; private static final String CAPABILITIES_1_0 = BASE_URL +
> "&VERSION=1.0.0"; private static final String CAPABILITIES_1_1 =
> BASE_URL + "&VERSION=1.1.0"; private static final String LAYER_NAME
> = "topp:states"; private static final String ATTRIBUTE_NAMES =
> new String { "STATE_NAME", "STATE_FIPS", "SUB_REGION" };
> private static final int QUERY_LIMIT = 15;
>
>
> // default constructor
>
> public void testVersion1_0() throws IOException {
> // Step 1 - connection parameters
> final Map<String, Serializable> connectionParams =
> new HashMap<String, Serializable>();
> connectionParams.put(
> "WFSDataStoreFactory:GET_CAPABILITIES_URL",
> CAPABILITIES_1_0); _test(connectionParams, 0);
> _test(connectionParams, QUERY_LIMIT);
> }
>
> public void testVersion1_1_POST() throws IOException {
> // Step 1 - connection parameters
> final Map<String, Serializable> connectionParams =
> new HashMap<String, Serializable>();
> connectionParams.put(
> "WFSDataStoreFactory:GET_CAPABILITIES_URL",
> CAPABILITIES_1_1); // connectionParams.put(
> // "WFSDataStoreFactory:BUFFER_SIZE", 5);
> connectionParams.put("WFSDataStoreFactory:PROTOCOL",
> Boolean.TRUE); _test(connectionParams, 0);
> _test(connectionParams, QUERY_LIMIT);
> }
>
> public void
> _test(final Map<String, Serializable> connectionParams, final
> int limit) throws IOException {
> // Step 2 - connection
> final WFSDataStore ds =
> (WFSDataStore)
> DataStoreFinder.getDataStore(connectionParams); assertNotNull("Data
> store MUST NOT be null", ds);
>
> // Step 3 - discovery
> final List<String> typeNames =
> Arrays.asList(ds.getTypeNames()); assertNotNull("Type names MUST
> NOT be null", typeNames); assertFalse("Type names MUST NOT be
> empty", typeNames.isEmpty()); assertTrue(
> "Type names MUST contain [" + LAYER_NAME + "]",
> typeNames.contains(LAYER_NAME));
> final SimpleFeatureType schema = ds.getSchema(LAYER_NAME);
> assertNotNull("Feature type (schema) MUST NOT be null",
> schema); final List<AttributeDescriptor> attributes =
> schema.getAttributeDescriptors();
> assertNotNull("Attributes list MUST NOT be null",
> attributes); assertTrue(
> "Attributes list MUST contain at least 3 elements",
> attributes.size() > 2);
> for (final String atName : ATTRIBUTE_NAMES)
> assertTrue(
> "Attribute named [" + atName + "] MUST be found",
> contains(attributes, atName));
>
> // Step 4 - query
> final int result = process(ds, limit);
> System.out.println("*** result = " + result);
> assertTrue("MUST be able to find at least one feature",
> result > 0); }
>
> private boolean
> contains(final List<AttributeDescriptor> attributes, final
> String atName) { boolean result = false;
> for (final AttributeDescriptor attribute : attributes)
> if (attribute.getLocalName().equals(atName)) {
> result = true;
> break;
> }
> return result;
> }
>
> private int process(final DataStore dataStore, final int limit)
> throws IOException {
> final DefaultQuery q = new DefaultQuery(LAYER_NAME);
> q.setPropertyNames(ATTRIBUTE_NAMES);
> if (limit > 0) q.setMaxFeatures(QUERY_LIMIT);
>
> final FeatureSource<SimpleFeatureType, SimpleFeature> fs =
> dataStore.getFeatureSource(LAYER_NAME);
> final FeatureCollection<SimpleFeatureType, SimpleFeature> fc
> = fs.getFeatures(q);
> FeatureIterator<SimpleFeature> fit = null;
> int result = 0;
> try {
> for (fit = fc.features(); fit.hasNext():wink: {
> fit.next();
> result++;
> }
> } finally {
> if (fit != null) fc.close(fit);
> }
> System.out.println("*** INCLUDE filter, for "
> + ATTRIBUTE_NAMES.length + " attributes, with "
> + (limit == 0 ? "NO limit" : "a limit of (" + limit +
> ")") + ", found " + result + " feature(s)");
> return result;
> }
> }

cheers;
rsn

On Thu, 19 Feb 2009 04:04:03 pm Raif S. Naffah wrote:

hello Justin,

thank you muchly for your prompt reply! i'll open a JIRA issue just
to be able to track the version of gt/geoserver which will contain
the fix.

done: http://jira.codehaus.org/browse/GEOT-2345

On Thu, 19 Feb 2009 03:53:40 pm Justin Deoliveira wrote:
> Hi Raif,
>
> I ran the test case you supplied (thanks btw, it was nicely laid
> out
>
> :)) and was able to reproduce the problem.
>
> This is an issue with the wfs 1.1 datastore in geotools. In
> particular line 367 of WFS_1_1_0_DataStore:
>
> The code as written:
>
> int maxFeatures = Math.min(maxFeaturesHardLimit.intValue(),
> query.getMaxFeatures());
>
> However, maxFeaturesHardLimit == 0 means it is unset. Which means
> whenever the client does not set a hard limit, and specifies a
> query limit it will always be zero. A simple fix:
>
> int maxFeatures = maxFeaturesHardLimit.intValue() > 0 ?
> Math.min(maxFeaturesHardLimit.intValue(),
> query.getMaxFeatures()) :
>
> query.getMaxFeatures()
>
>
> Feel free to open a jira issue in geotools for this. Gabriel, if
> you are ok with the fix I will happily commit.
>
> -Justin
>
> Raif S. Naffah wrote:
> > hello all,
> >
> > i'm seeing different behavior in the response to a WFS GetFeature
> > request when i specify a query limit v/s when i don't but only
> > with WFS 1.1.0; i.e. 1.0.0 returns the same (correct) result in
> > both cases. i'm including the test case i used to demonstrate
> > the issue. the code uses the GeoTools libraries version 2.5.3 and
> > communicates with a GeoServer 1.6.4b.
> >
> > my questions which i'd appreciate an answer or guidance from a
> > GeoServer developer are:
> >
> > * is this a known issue or i'm doing the wrong thing? if it's
> > the latter a push in the right direction is much appreciated.
> >
> > * i thought of using the buffer-limit attribute with a WFS data
> > store in the hope to limit the output and allow the client side
> > code to close the store before getting _all_ the features this
> > also does not seem to fix the issue. again is it a known issue
> > or should i file a bug report.
> >
> > * in the log/trace file with WFS 1.1 i see the following two
> > statements:
> >
> > Supported filter: Filter.INCLUDE
> > Unsupported filter: Filter.INCLUDE
> >
> > is it safe to ignore those statements?
> >
> >
> > here is the JUnit test case code:
> >
> > // default package
> >
> > import java.io.IOException;
> > import java.io.Serializable;
> > import java.util.Arrays;
> > import java.util.HashMap;
> > import java.util.List;
> > import java.util.Map;
> >
> > import junit.framework.TestCase;
> >
> > import org.geotools.data.DataStore;
> > import org.geotools.data.DataStoreFinder;
> > import org.geotools.data.DefaultQuery;
> > import org.geotools.data.FeatureSource;
> > import org.geotools.data.wfs.WFSDataStore;
> > import org.geotools.feature.FeatureCollection;
> > import org.geotools.feature.FeatureIterator;
> > import org.opengis.feature.simple.SimpleFeature;
> > import org.opengis.feature.simple.SimpleFeatureType;
> > import org.opengis.feature.type.AttributeDescriptor;
> >
> > public class TestIncludeFilter extends TestCase {
> > private static final String BASE_URL =
> >
> > "http://localhost:8080/geoserver/wfs?SERVICE=WFS&REQUEST=GetCapab
> >il ities"; private static final String CAPABILITIES_1_0 = BASE_URL
> > + "&VERSION=1.0.0"; private static final String CAPABILITIES_1_1
> > = BASE_URL + "&VERSION=1.1.0"; private static final String
> > LAYER_NAME = "topp:states"; private static final String
> > ATTRIBUTE_NAMES = new String { "STATE_NAME", "STATE_FIPS",
> > "SUB_REGION" }; private static final int QUERY_LIMIT = 15;
> >
> >
> > // default constructor
> >
> > public void testVersion1_0() throws IOException {
> > // Step 1 - connection parameters
> > final Map<String, Serializable> connectionParams =
> > new HashMap<String, Serializable>();
> > connectionParams.put(
> > "WFSDataStoreFactory:GET_CAPABILITIES_URL",
> > CAPABILITIES_1_0); _test(connectionParams, 0);
> > _test(connectionParams, QUERY_LIMIT);
> > }
> >
> > public void testVersion1_1_POST() throws IOException {
> > // Step 1 - connection parameters
> > final Map<String, Serializable> connectionParams =
> > new HashMap<String, Serializable>();
> > connectionParams.put(
> > "WFSDataStoreFactory:GET_CAPABILITIES_URL",
> > CAPABILITIES_1_1); // connectionParams.put(
> > // "WFSDataStoreFactory:BUFFER_SIZE", 5);
> > connectionParams.put("WFSDataStoreFactory:PROTOCOL",
> > Boolean.TRUE); _test(connectionParams, 0);
> > _test(connectionParams, QUERY_LIMIT);
> > }
> >
> > public void
> > _test(final Map<String, Serializable> connectionParams, final
> > int limit) throws IOException {
> > // Step 2 - connection
> > final WFSDataStore ds =
> > (WFSDataStore)
> > DataStoreFinder.getDataStore(connectionParams);
> > assertNotNull("Data store MUST NOT be null", ds);
> >
> > // Step 3 - discovery
> > final List<String> typeNames =
> > Arrays.asList(ds.getTypeNames()); assertNotNull("Type names MUST
> > NOT be null", typeNames); assertFalse("Type names MUST NOT be
> > empty", typeNames.isEmpty()); assertTrue(
> > "Type names MUST contain [" + LAYER_NAME + "]",
> > typeNames.contains(LAYER_NAME));
> > final SimpleFeatureType schema = ds.getSchema(LAYER_NAME);
> > assertNotNull("Feature type (schema) MUST NOT be null",
> > schema); final List<AttributeDescriptor> attributes =
> > schema.getAttributeDescriptors();
> > assertNotNull("Attributes list MUST NOT be null",
> > attributes); assertTrue(
> > "Attributes list MUST contain at least 3 elements",
> > attributes.size() > 2);
> > for (final String atName : ATTRIBUTE_NAMES)
> > assertTrue(
> > "Attribute named [" + atName + "] MUST be found",
> > contains(attributes, atName));
> >
> > // Step 4 - query
> > final int result = process(ds, limit);
> > System.out.println("*** result = " + result);
> > assertTrue("MUST be able to find at least one feature",
> > result > 0); }
> >
> > private boolean
> > contains(final List<AttributeDescriptor> attributes, final
> > String atName) { boolean result = false;
> > for (final AttributeDescriptor attribute : attributes)
> > if (attribute.getLocalName().equals(atName)) {
> > result = true;
> > break;
> > }
> > return result;
> > }
> >
> > private int process(final DataStore dataStore, final int
> > limit) throws IOException {
> > final DefaultQuery q = new DefaultQuery(LAYER_NAME);
> > q.setPropertyNames(ATTRIBUTE_NAMES);
> > if (limit > 0) q.setMaxFeatures(QUERY_LIMIT);
> >
> > final FeatureSource<SimpleFeatureType, SimpleFeature> fs =
> > dataStore.getFeatureSource(LAYER_NAME);
> > final FeatureCollection<SimpleFeatureType, SimpleFeature>
> > fc = fs.getFeatures(q);
> > FeatureIterator<SimpleFeature> fit = null;
> > int result = 0;
> > try {
> > for (fit = fc.features(); fit.hasNext():wink: {
> > fit.next();
> > result++;
> > }
> > } finally {
> > if (fit != null) fc.close(fit);
> > }
> > System.out.println("*** INCLUDE filter, for "
> > + ATTRIBUTE_NAMES.length + " attributes, with "
> > + (limit == 0 ? "NO limit" : "a limit of (" + limit +
> > ")") + ", found " + result + " feature(s)");
> > return result;
> > }
> > }

cheers;
rsn

--
cheers;
rsn

oops,

the fix sounds alright, feel free to apply at your convenience

Gabriel

On Thursday 19 February 2009 02:53:40 Justin Deoliveira wrote:

Hi Raif,

I ran the test case you supplied (thanks btw, it was nicely laid out :))

and was able to reproduce the problem.

This is an issue with the wfs 1.1 datastore in geotools. In particular

line 367 of WFS_1_1_0_DataStore:

The code as written:

int maxFeatures = Math.min(maxFeaturesHardLimit.intValue(),

query.getMaxFeatures());

However, maxFeaturesHardLimit == 0 means it is unset. Which means

whenever the client does not set a hard limit, and specifies a query

limit it will always be zero. A simple fix:

int maxFeatures = maxFeaturesHardLimit.intValue() > 0 ?

Math.min(maxFeaturesHardLimit.intValue(), query.getMaxFeatures()) :

query.getMaxFeatures()

Feel free to open a jira issue in geotools for this. Gabriel, if you are

ok with the fix I will happily commit.

-Justin

Raif S. Naffah wrote:

hello all,

i’m seeing different behavior in the response to a WFS GetFeature

request when i specify a query limit v/s when i don’t but only with

WFS 1.1.0; i.e. 1.0.0 returns the same (correct) result in both

cases. i’m including the test case i used to demonstrate the issue.

the code uses the GeoTools libraries version 2.5.3 and communicates

with a GeoServer 1.6.4b.

my questions which i’d appreciate an answer or guidance from a

GeoServer developer are:

  • is this a known issue or i’m doing the wrong thing? if it’s the

latter a push in the right direction is much appreciated.

  • i thought of using the buffer-limit attribute with a WFS data

store in the hope to limit the output and allow the client side

code to close the store before getting all the features this

also does not seem to fix the issue. again is it a known issue

or should i file a bug report.

  • in the log/trace file with WFS 1.1 i see the following two

statements:

Supported filter: Filter.INCLUDE

Unsupported filter: Filter.INCLUDE

is it safe to ignore those statements?

here is the JUnit test case code:

// default package

import java.io.IOException;

import java.io.Serializable;

import java.util.Arrays;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

import junit.framework.TestCase;

import org.geotools.data.DataStore;

import org.geotools.data.DataStoreFinder;

import org.geotools.data.DefaultQuery;

import org.geotools.data.FeatureSource;

import org.geotools.data.wfs.WFSDataStore;

import org.geotools.feature.FeatureCollection;

import org.geotools.feature.FeatureIterator;

import org.opengis.feature.simple.SimpleFeature;

import org.opengis.feature.simple.SimpleFeatureType;

import org.opengis.feature.type.AttributeDescriptor;

public class TestIncludeFilter extends TestCase {

private static final String BASE_URL =

http://localhost:8080/geoserver/wfs?SERVICE=WFS&REQUEST=GetCapabilities

; private static final String CAPABILITIES_1_0 = BASE_URL +

“&VERSION=1.0.0”; private static final String CAPABILITIES_1_1 = BASE_URL

  • “&VERSION=1.1.0”; private static final String LAYER_NAME =

“topp:states”;

private static final String ATTRIBUTE_NAMES = new String {

“STATE_NAME”, “STATE_FIPS”, “SUB_REGION” };

private static final int QUERY_LIMIT = 15;

// default constructor

public void testVersion1_0() throws IOException {

// Step 1 - connection parameters

final Map<String, Serializable> connectionParams =

new HashMap<String, Serializable>();

connectionParams.put(

“WFSDataStoreFactory:GET_CAPABILITIES_URL”,

CAPABILITIES_1_0); _test(connectionParams, 0);

_test(connectionParams, QUERY_LIMIT);

}

public void testVersion1_1_POST() throws IOException {

// Step 1 - connection parameters

final Map<String, Serializable> connectionParams =

new HashMap<String, Serializable>();

connectionParams.put(

“WFSDataStoreFactory:GET_CAPABILITIES_URL”,

CAPABILITIES_1_1); // connectionParams.put(

// “WFSDataStoreFactory:BUFFER_SIZE”, 5);

connectionParams.put(“WFSDataStoreFactory:PROTOCOL”, Boolean.TRUE);

_test(connectionParams, 0);

_test(connectionParams, QUERY_LIMIT);

}

public void

_test(final Map<String, Serializable> connectionParams, final int

limit) throws IOException {

// Step 2 - connection

final WFSDataStore ds =

(WFSDataStore)

DataStoreFinder.getDataStore(connectionParams); assertNotNull("Data store

MUST NOT be null", ds);

// Step 3 - discovery

final List typeNames = Arrays.asList(ds.getTypeNames());

assertNotNull(“Type names MUST NOT be null”, typeNames);

assertFalse(“Type names MUST NOT be empty”, typeNames.isEmpty());

assertTrue(

“Type names MUST contain [” + LAYER_NAME + “]”,

typeNames.contains(LAYER_NAME));

final SimpleFeatureType schema = ds.getSchema(LAYER_NAME);

assertNotNull(“Feature type (schema) MUST NOT be null”, schema);

final List attributes =

schema.getAttributeDescriptors();

assertNotNull(“Attributes list MUST NOT be null”, attributes);

assertTrue(

“Attributes list MUST contain at least 3 elements”,

attributes.size() > 2);

for (final String atName : ATTRIBUTE_NAMES)

assertTrue(

“Attribute named [” + atName + “] MUST be found”,

contains(attributes, atName));

// Step 4 - query

final int result = process(ds, limit);

System.out.println("*** result = " + result);

assertTrue(“MUST be able to find at least one feature”, result >

0); }

private boolean

contains(final List attributes, final String

atName) { boolean result = false;

for (final AttributeDescriptor attribute : attributes)

if (attribute.getLocalName().equals(atName)) {

result = true;

break;

}

return result;

}

private int process(final DataStore dataStore, final int limit)

throws IOException {

final DefaultQuery q = new DefaultQuery(LAYER_NAME);

q.setPropertyNames(ATTRIBUTE_NAMES);

if (limit > 0) q.setMaxFeatures(QUERY_LIMIT);

final FeatureSource<SimpleFeatureType, SimpleFeature> fs =

dataStore.getFeatureSource(LAYER_NAME);

final FeatureCollection<SimpleFeatureType, SimpleFeature> fc =

fs.getFeatures(q);

FeatureIterator fit = null;

int result = 0;

try {

for (fit = fc.features(); fit.hasNext():wink: {

fit.next();

result++;

}

} finally {

if (fit != null) fc.close(fit);

}

System.out.println("*** INCLUDE filter, for "

  • ATTRIBUTE_NAMES.length + " attributes, with "
  • (limit == 0 ? “NO limit” : “a limit of (” + limit + “)”)
  • “, found " + result + " feature(s)”);

return result;

}

}

TIA + cheers;

rsn



----- Open Source Business Conference (OSBC), March 24-25, 2009, San

Francisco, CA -OSBC tackles the biggest issue in open source: Open

Sourcing the Enterprise -Strategies to boost innovation and cut costs

with open source participation -Receive a $600 discount off the

registration fee with the source code: SFAD http://p.sf.net/sfu/XcvMzF8H



Geoserver-users mailing list

Geoserver-users@lists.sourceforge.net

https://lists.sourceforge.net/lists/listinfo/geoserver-users

Gabriel Roldan

OpenGeo - http://www.opengeo.org