Runtime type checking contradicts compile time type declarations

820
5
07-12-2021 11:57 PM
DaveWhitla
New Contributor III

In trying to circumvent another problem I tried creating a FeatureLayer via its FeatureLayer(FeatureTable) constructor. FeatureCollectionTable is a subclass of FeatureTable and yet passing one as the argument to the above constructor results in an exception being thrown. Why?

"Cannot create a feature layer from a feature collection table. Add the feature collection table to a feature collection."

0 Kudos
5 Replies
MarkBaird
Esri Regular Contributor

I could explain the errors you are getting here, but I do think its worth you explaining what you are trying to do in your app and the data sources you are working with.  Once I've understood what you are trying to do and what data you are working with I can give you pointers to documentation or samples which demonstrate this.  

 

0 Kudos
DaveWhitla
New Contributor III

Hi Mark,

I believe I understand the error. My issue with it is that an implementation should not impose runtime type restrictions on a method argument that satisfies the declared type of the method parameter.

Either the method should take some more restrictive subclass of FeatureTable (or another marker interface) as its parameter, or FeatureCollectionTable should not extend FeatureTable.

That said I think I have found another way around my problem. My objective was to set LabelDefinitions on a FeatureCollectionLayer and up to that point I had hit a few dead ends and was trying to find an alternative way of constructing a FeatureLayer. Upon finding that particular method to be another dead end I moved on to finding some way of accessing the FeatureLayer(s) created within the FeatureCollectionLayer. I ran into another issue there documented in another post today but through trial and error probing the available methods on FeatureCollectionLayer I have found that the following, while it feels a bit clunky, seems to consistently work:

 

FeatureCollection featureCollection = new FeatureCollection();
featureCollection.getTables().add(table);
layer = new FeatureCollectionLayer(featureCollection);

layer.getSubLayerContents().addListChangedListener(event -> {
  if (event.getAction() == ListChangedEvent.Action.ADDED) {
    for (LayerContent layerContent : event.getItems()) {
      if (layerContent instanceof FeatureLayer featureLayer) {
        featureLayer.setLabelsEnabled(true);
        featureLayer.getLabelDefinitions().add(getLabelDefinition());  
      }
    }
  }
});

 

 

0 Kudos
MarkBaird
Esri Regular Contributor

I suspect the issue you have been seeing is that your application is trying to access resources before they are loaded.  In the Runtime SDK we encourage asynchronous development techniques to ensure that you don't write any code which potentially blocks the UI thread of your app.  I'd have a look at this page on loadable resources.

 

In your code above I suspect something in your app has triggered a load event because the ListChangesListener has triggered.  This may have been caused by you adding the layer to the map, or you called LoadAsync on the layer.

Anyway I've added a snipped of code to set the labelling property within a doneLoadListener.  If you call this outside of the listener it throws an error.

 

      featureCollectionLayer.loadAsync(); // needed if you didn't add it to a map.
      featureCollectionLayer.addDoneLoadingListener(()-> {
        System.out.println("loaded");
        // if this is called without the layer loaded it will fail.
        featureCollectionLayer.getLayers().get(0).setLabelsEnabled(false);
      });

 

This might be same issue with your other post. 

Let me know if this helps.

0 Kudos
DaveWhitla
New Contributor III

This isn’t the issue I’m afraid. The bug in your code is definitely timing sensitive as I could only reproduce it sometimes. Under no circumstances should Iterator.hasNext() return true and the next call to Iterator.next() throw an index out of bounds exception.

0 Kudos
DaveWhitla
New Contributor III

Thanks for the code suggestion. I already solved the issue using the code I provided above. I am not loading anything asynchronously - the table is populated locally.

Does simply adding a layer to the map's operationalLayers list trigger doneLoadingListeners if the layer is backed by a FeatureCollectionTable rather than a service call? If I had known that I might have tried it first, but honestly I think that listening for subLayerContents changes better reflects my aim as it will catch subsequent layer additions - if that is possible.

0 Kudos