5

I'm trying to implement a WFS client in Java using the Geotools. But everytime I tried to read the actual features I get following Error

java.lang.ClassCastException: java.util.HashMap cannot be cast to org.opengis.feature.simple.SimpleFeature

It seems it is nested somewhere in

(PullParserFeatureReader.java:84) Requests to other WFS services work. So it seems to be something specific to this service, which I can't figure out.

My code looks like this

    Map connectionParameters = new HashMap();
    connectionParameters.put("WFSDataStoreFactory:GET_CAPABILITIES_URL", "https://geodaten.metropoleruhr.de/poi/poi?" );
    connectionParameters.put("WSFDataStoreFactory:WFS_STRATEGY", "mapserver");
    connectionParameters.put("WFSDataStoreFactory:MAXFEATURES", "1");

    WFSDataStoreFactory dsf = new WFSDataStoreFactory();
    WFSDataStore data = dsf.createDataStore( connectionParameters );

    String typeNames[] = data.getTypeNames();
    String typeName = typeNames[0];

    try {
        SimpleFeatureSource source = data.getFeatureSource(typeName);
        SimpleFeatureCollection fc = source.getFeatures();

        while(fc.features().hasNext()){
            SimpleFeature sf = fc.features().next();
            System.out.println(sf.getAttribute("name"));
        }
    } catch (IOException ex) {
        ex.printStackTrace();
    }

1 Answer 1

4

At present, you are reliant on the version negotiation and are accepting the default output format. It may be better to force those to say 1.1.0 and "text/xml; subtype=gml/2.1.2" to limit the variables.

If that doesn't fix the issue then from the GeoTools FAQ:

Q: For WFS why does Parser return a Map? This shows up as the following error:

ClassCastException “java.util.HashMap cannot be cast to FeatureCollection”. To understand this error please remember that the GML returned by a WFS GetFeatures request is a normal XML file, with a reference to an XML schema at the top.

For a WFS GetFeature response the schema reference is usually a DescribeFeatureType call that returns an XML Schema.

If this schema is incorrectly configured (common with MapServer) or cannot be reached (common with restricted environments) our Parser will give up guessing what is a Feature and just return the values in a HashMap.

Note If you are using the GML utility class it will perform a bit of analysis and create an ad-hoc FeatureType in order to return you Features. looking at the HashMaps returned building a feature type that matches that kind of contents building features that match that FeatureType This is similar to the approach taken by OGR; OGR figures out where the “geometry” is; and then goes up two levels and assumes those things are features. Both of these approaches are strictly a work around for a common problem of misconfigured WFS servers. Here is how to review the configuration of your WFS Server:

If you are having a problem with a HashMap being returned when you expect a Feature, you should check GetCapabilities responses for remote server e.g. with browser:

http://{URL}?SERVICE=WFS&VERSION=1.1.0&REQUEST=GetCapabilities After that, ensure that elements contain urls that actually work

In particular check that DescribeFeatureType responds with the expected XML Schema

Now the server returns a valid getcapabilities doc which is good and it suggests we can try describeFeatureTypes - that seem to provide valid schema. But I'm not sure which feature type you are actually trying to download. Everything seemed to work for ms:poi_polizei which I picked at random.

Update

Using the following code I get no issues for any of the features in that server:

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.message.BasicNameValuePair;
import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.DataUtilities;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.Query;
import org.geotools.data.Transaction;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.data.wfs.WFSDataStoreFactory;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.FeatureCollection;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.filter.FilterFactory2;
import org.opengis.filter.identity.FeatureId;
import org.opengis.filter.sort.SortBy;
import org.opengis.filter.sort.SortOrder;

public class FetchWFS {


  FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
  private URL baseURL;
  private DataStore datastore;
  private boolean auth = false;

  public static void main(String[] args) throws Exception {
    FetchWFS test = new FetchWFS("http://geodaten.metropoleruhr.de/poi/poi");

    String[] names = test.getNames();
    for (String name : names) {
      System.out.print(name);
      SimpleFeatureCollection f = test.getData(name);
      System.out.println(" "+f.size()+" features");
      System.out.println("\t"+DataUtilities.first(f));
      System.out.println();
    }

  }

  public FetchWFS(String url) throws IOException, URISyntaxException {
    this(url, "", "");
  }

  public FetchWFS(String url, String user, String passwd) throws IOException, URISyntaxException {
    if (!user.isEmpty()) {
      auth = true;
    }
    baseURL = new URL(url);
    List<NameValuePair> nvp = URLEncodedUtils.parse(baseURL.toURI(), "UTF-8");
    NameValuePair service = new BasicNameValuePair("service", "wfs");
    NameValuePair request = new BasicNameValuePair("request", "getCapabilities");
    NameValuePair version = new BasicNameValuePair("version", "1.1.0");
    HashMap<String, NameValuePair> parts = new HashMap<>();
    parts.put(service.getName(), service);
    parts.put(request.getName(), request);
    parts.put(version.getName(), version);
    for (NameValuePair part : nvp) {

      if (part.getName().equalsIgnoreCase("SERVICE")) {
        // We don't care what they think this should be
      } else if (part.getName().equalsIgnoreCase("REQUEST")) {
        // This must be getCapabuilities so we ignore them
      } else if (part.getName().equalsIgnoreCase("VERSION")) {
        System.out.println("Changing version to " + part.getValue());
        parts.put(version.getName(), part);
      } else {
        parts.put(part.getName(), part);
      }
    }

    URIBuilder builder = new URIBuilder();
    builder.setScheme(baseURL.getProtocol());
    builder.setHost(baseURL.getHost());
    builder.setPort(baseURL.getPort());
    builder.setPath(baseURL.getPath());
    List<NameValuePair> p = new ArrayList<>();
    p.addAll(parts.values());
    builder.setParameters(p);
    // builder.addParameter("viewparams", "q:\"mySolrQuery\"");
    URI uri = builder.build();
    System.out.println(uri);
    baseURL = uri.toURL();
    // fetch the DataStore
    Map<String, Object> params = new HashMap<>();
    params.put(WFSDataStoreFactory.URL.key, baseURL);
    if (auth) {
      params.put(WFSDataStoreFactory.USERNAME.key, user);
      params.put(WFSDataStoreFactory.PASSWORD.key, passwd);
    }
    params.put(WFSDataStoreFactory.WFS_STRATEGY.key, "mapserver");
    datastore = DataStoreFinder.getDataStore(params);
  }

  public SimpleFeatureType getSchema(String name) throws IOException {
    return datastore.getSchema(name);
  }

  public String[] getNames() throws IOException {
    return datastore.getTypeNames();
  }

  public SimpleFeatureCollection getData(String name) throws IOException {
    SimpleFeatureSource source = datastore.getFeatureSource(name);
    return source.getFeatures();
  }
}
3
  • Thanks a lot! That worked for me! Thoudh I'd like to use v2.0.0 of the WFS. Changing the version results in another issue. Oct 25, 2017 at 13:17
  • I just spotted the v2.0 issue - I'll look into it at the weekend. You can filter the returned feature collection using a Query - SimpleFeatureSource source = datastore.getFeatureSource(name); Query query = new Query(); /*query.setSortBy(new SortBy[] { ff.sort("name", SortOrder.ASCENDING) }); query.setStartIndex(start); query.setMaxFeatures(size);*/ query.setFilter(ECQL.toFilter("name='Erlebnisbad Maximare'")); return source.getFeatures(query);
    – Ian Turton
    Oct 25, 2017 at 13:47
  • Thanks so much. I'll be on holiday next week. So no rush. Figured out with the filter already. Thanks once again! Oct 25, 2017 at 14:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.