Make layer re-render?

873
2
10-28-2017 01:11 PM
TristanSebens
New Contributor II

I'm not sure if this is the most prudent way to go about this, so feel free to critique my code structure as well.

Okay, long story short, I have about a feature service that has tens of thousands of features. I only want the features contained within the current camera extent to be displayed, so:

  • I've created a feature layer from the REST service URL. I call this the 'internal' feature layer.
  • I've also created a corresponding 'wrapper' layer which 'wraps' around the internal feature layer.
  • This wrapper layer is what will be displayed on the map, and it's source will be set to the result of a query executed on the feature layer. It's sort of like the internal layer is a database table, and the wrapper layer is a view of that table.

So anytime the camera changes, I tell the wrapper layer to execute a query on its internal layer based on the current extent of the view. So far, so good. This all works in the code.

Here's the problem: The features aren't drawing. I've printed the JSON of the wrapper layer to the console, so I know that the source of the wrapper layer has been set to the result of the query correctly, but...

The features don't have symbols. 'symbol: null'

The internal layer has a Unique Value renderer that was built from the REST service, and when I create the wrapper layer I pass this render to the wrapper layers. I know that this renderer is present in the wrapper layer at the time of the query.

I can't figure out why the wrapper layer is not using it's renderer to generate symbols for the features, so I'm wondering if there is a function call that I need to make in order to make the layer redraw itself and use its renderer to generate the proper symbols.

Any thoughts or wisdom is appreciated.

0 Kudos
2 Replies
RobertScheitlin__GISP
MVP Emeritus

Tristan,

   Can you provide a sample of this?

TristanSebens
New Contributor II

Of course! I've attached the full source code to the initial post for reference, but here are the relevant lines:

- This is the function with which I create the wrapper layers, using the inner layers. In this function the inner layers have already been loaded.

 // Returns a layer promise which has all of the important characteristics of the passed layer, and which carries a reference to the original layer in the property 'subordinate'
function createWrapperLayer( featLayer ){
  console.log( 'Creating wrapper layer for ', featLayer.title );
  // Grab the features in the featLayer which are visible to the initial extent of the view. Assign those as the source.
  // Wait for the results of the query to return, then pass it into the next function to be used as the resultant layer's source.
  // Here we read out all of the important information from the original layer
  var subTitle = featLayer.title;
  var subID = featLayer.id;
  var subFields = featLayer.fields;
  var subObjID = featLayer.objectIdField;
  var subSpRef = featLayer.spatialReference;
  var subGeoType = featLayer.geometryType;
  var subRender = featLayer.renderer;
  // We have to wait for the query to finish execution to create the wrapper layer
  visibleFeats.then(
    function(visibleFeats) {
    // Now we create the actual layer
    visiLayer = new FeatureLayer({
      title: subTitle,
      id: subID,
      objectIdField: subObjID,
      source: findFeaturesInCameraExtent( featLayer ), // In order to execute this function, featLayers must already be loaded.
      fields: subFields,
      visible: true,
      spatialReference: subSpRef,
      geometryType: subGeoType,
      renderer: subRender
    });
    // Here we add a prototype attribute 'subordinate' to the visiLayer so that we can access the feature layer again later
    visiLayer.subordinate = featLayer;
    // And return it
    visiLayer.then(

      function( vLayer ) {
        console.log( vLayer );
      }

    );
    visiLayer.load();
    map.add( visiLayer );
    });
}

- This is the function which initiates the querying of the inner layers, and sets the source of the wrapper layer to the query's result(s)

// This is the function that will be called each time the camera changes. 

// It 

function updateVisibleFeatures() {
  map.layers.forEach(function(layer) {
    if ( layer.visible == true && layer.id != layerBounds.id ) { // Cycle through every layer except for the bounds  

      // layer.subordinate is a reference to the internal layer that the wrapper layer is wrapped around

      // it is the layer that is built from the REST service, and contains all available features.
      visibleFeats = findFeaturesInCameraExtent( layer.subordinate );
      visibleFeats.then(
        function( visibleFeats ) {
          layer.source = visibleFeats.features;
          console.log( layer );
        },

        // Function that is called in case the promise fails
        function( reason ) {
          console.log( 'Updating visible features failed: ', reason );
        });
    }
  });
}

And this is the subroutine that actually executes the query

// Function which, given a layer, will return the features of that layer that are contained within the camera extent.

// Function returns a promise, and the resultant featureSet should be accessed with the '.then()' function
function findFeaturesInCameraExtent( layer ) {
  var query = layer.createQuery(); // Create a blank query
  query.geometry = view.extent
  query.spatialRelationship = "contains";
  var results = layer.queryFeatures( query ) // Apply the spatial query to the layer, return a promise of the result
  // Returns a promise which, once resolved, will contain all of the features returned by the query
  return results;
}

0 Kudos