4.12 Scene Popup: Zoom To not working for features from e.g.Identify

543
1
Jump to solution
09-04-2019 02:13 AM
MichalGasparovic1
New Contributor III

Seems like there is a hitch somewhere within the Scene popup, or perhaps I'm missing something. Thanks for your help upfront.

Note: Worked ok in 4.11

I have a set of features coming from the identify task. These are the polygon graphics (but doesn't work with other geometry types as well) with proper geometry, spatial reference, symbology and attributes, the popup displays in correct location and the content gets generated ok, but the Zoom To is not working. it doesn't throw an error, it simply does nothing.

If I compare the popup features for which the popup works (e.g. from feature layer on click event), with the features I'm pushing for popup from identify, the difference is in layer and sourceLayer attributes are missing in the latter (because they come from the identify, these don't have the layer).

I can pinpoint the problem in catch of the createGoToCamera promise in the api but the arguments for that catch statement are null so not really contain any error information.

d.prototype.goToAnimated = function(a, c) {
   return h(this, void 0, void 0, function() {
      var e, d, g, k, l, p, m = this;
      return b(this, function(q) {
         this.cancelAnimatedGoTo();
         if (!this.ready)
              return [2, this.goToAnimatedBeforeReady(a, c)];
         e = this.view.state.cameraController;
         e instanceof K.PointToPointAnimationController && e.updateStateFromViewAnimation(),
         e.active && (d = e));
         null == d && (d = new K.PointToPointAnimationController(this.view.state,this.view.sceneIntersectionHelper));
         g = d.viewAnimation;
         k = this.createGoToCamera(a, c); // <- THIS IS THE PROBLEM PROMISE
...
...
...
...
...
 k.then(function(a) {
...

}).catch(function(a) {
                                    !v.isAbortError(a) && a instanceof f && a.message && T.error("#goTo()", "Failed to create camera from target, " + a.message[0].toLowerCase() + a.message.slice(1));
                                    if (p())
                                        throw m.view.state.cameraController.stopController(),
                                        a;
                                });‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

and my code:

this.esriLib.all(tasks).then(resultsArray => {
      const features = [];
      resultsArray.forEach((results, idx) => {
        if (results && results.results) {
          results.results.forEach((item) => {
            const feature = item.feature;
            if (!feature) {
              return;
            }
            // Set the symbol.
            feature.symbol = this.mapService.getIdentifyResultSymbol(feature.geometry);
            
            // Prepare the popup template
            const template = new this.esriLib.PopupTemplate({
              title: item.layerName,
              content: [{
                type: 'fields',
                fieldInfos: Object.keys(feature.attributes).map(k => {
                  return {
                    fieldName: k,
                    label: k
                  };
                })
              }]
            });
            feature.popupTemplate = template;
            features.push(feature);
          });
        }
      });

      if (features && features.length > 0) {
        this.mapView.popup.open({
          features: features,
          location: point
        });
      }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos
1 Solution

Accepted Solutions
MichalGasparovic1
New Contributor III

So, if everyone ever finds himself/herself in the similar scenario as above, this is how I resolved it

In summary:

Init

  • create the identify graphics layer
  • hook to whenLayerView for the above layer to get its view. store it somewhere, I usually hack it into the layer itself. (not sure why this is not a default behavior or esri layer)

During task execution

  • [task] do the identify
  • [layer] for each identified layer, get the info (fields and renderer, using the rest api). cache that info for future identify tasks so you don't have to do another request to the server) and loop the features:
  • [layer-feature] for each identified feature, transform the attributes names from aliases to system name fields (that is important for the next step). Also, make sure you transform ObjectId value to Number (otherwise the highlight wont' work!), not sure why identify is transforming all the values as strings.
  • [layer-feature] for each identified feature, try to obtain the symbol from the renderer. This is where you will need the system field names, as renderers don't work with the aliases (and identify is returning aliased field names) and assign it to the feature's symbol property. If no symbol has been found, establish some default symbols for your identified features.
    Each type of renderer has different methods to retrieve the symbol
    - SimpleRenderer => getSymbol
    - ClassBreaksRenderer => getClassBreaksInfo
    - UniqueValueRenderer => GetUniqueValueInfo
  • [layer-feature] for each identified feature, revert back the fields from system field names to field aliases (so the popup can display a proper aliases)
  • [layer-feature] for each identified feature, create and assign a template. Since 4.12 the popup won't generate the content for the '*' template (whyyy?), it's all in the graphics layers only or at feature level
    const template = new this.esriLib.PopupTemplate({
                title: item.layerName,
                content: [{
                  type: 'fields',
                  fieldInfos: Object.keys(feature.attributes).map(k => {
                    return {
                      fieldName: k,
                      label: k
                    };
                  })
                }]
              });‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
  • [layer-feature] for each identified feature, add the layer to the layer and sourceLayer property
    feature.popupTemplate = template;
    feature.layer = this.identifyLayer;
    feature.sourceLayer = this.identifyLayer;‍‍‍
  • [layer] add the features to the identify graphics layer (user addMany)
  • [layers] set the features to the mapview/sceneview popup.
    this.mapView.popup.open({
        features: features,
        location: evt.mapPoint
    });‍‍‍‍‍‍‍‍

Popup

Hook to the popup's visibility and features change events:

  • selectedFeature property change, monitor this event, and if the feature has the layer property, use that layer's view to highlight the particular feature. Clear any highlighted features beforehand.
        this.esriLib.watchUtils.watch(view.popup, 'selectedFeature', (feature) => {
          this.clearPopupHighlight(view.popup);
          if (feature && feature.layer && feature.layer.view && feature.layer.view.highlight) {
            view.popup.highlightHandle = feature.layer.view.highlight(feature);
          }      
        });
    ‍‍‍‍‍‍‍

  • visible property change, once it's not visible, clear any highlight and removeAll the features from the identify layer.

This is awful a lot of (async) work that needs to be done to display the identify results, display them using their own layer's renderers (real symbols), and enable popup to be able to zoom to and highlight the features.

Hope that helps.

View solution in original post

0 Kudos
1 Reply
MichalGasparovic1
New Contributor III

So, if everyone ever finds himself/herself in the similar scenario as above, this is how I resolved it

In summary:

Init

  • create the identify graphics layer
  • hook to whenLayerView for the above layer to get its view. store it somewhere, I usually hack it into the layer itself. (not sure why this is not a default behavior or esri layer)

During task execution

  • [task] do the identify
  • [layer] for each identified layer, get the info (fields and renderer, using the rest api). cache that info for future identify tasks so you don't have to do another request to the server) and loop the features:
  • [layer-feature] for each identified feature, transform the attributes names from aliases to system name fields (that is important for the next step). Also, make sure you transform ObjectId value to Number (otherwise the highlight wont' work!), not sure why identify is transforming all the values as strings.
  • [layer-feature] for each identified feature, try to obtain the symbol from the renderer. This is where you will need the system field names, as renderers don't work with the aliases (and identify is returning aliased field names) and assign it to the feature's symbol property. If no symbol has been found, establish some default symbols for your identified features.
    Each type of renderer has different methods to retrieve the symbol
    - SimpleRenderer => getSymbol
    - ClassBreaksRenderer => getClassBreaksInfo
    - UniqueValueRenderer => GetUniqueValueInfo
  • [layer-feature] for each identified feature, revert back the fields from system field names to field aliases (so the popup can display a proper aliases)
  • [layer-feature] for each identified feature, create and assign a template. Since 4.12 the popup won't generate the content for the '*' template (whyyy?), it's all in the graphics layers only or at feature level
    const template = new this.esriLib.PopupTemplate({
                title: item.layerName,
                content: [{
                  type: 'fields',
                  fieldInfos: Object.keys(feature.attributes).map(k => {
                    return {
                      fieldName: k,
                      label: k
                    };
                  })
                }]
              });‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
  • [layer-feature] for each identified feature, add the layer to the layer and sourceLayer property
    feature.popupTemplate = template;
    feature.layer = this.identifyLayer;
    feature.sourceLayer = this.identifyLayer;‍‍‍
  • [layer] add the features to the identify graphics layer (user addMany)
  • [layers] set the features to the mapview/sceneview popup.
    this.mapView.popup.open({
        features: features,
        location: evt.mapPoint
    });‍‍‍‍‍‍‍‍

Popup

Hook to the popup's visibility and features change events:

  • selectedFeature property change, monitor this event, and if the feature has the layer property, use that layer's view to highlight the particular feature. Clear any highlighted features beforehand.
        this.esriLib.watchUtils.watch(view.popup, 'selectedFeature', (feature) => {
          this.clearPopupHighlight(view.popup);
          if (feature && feature.layer && feature.layer.view && feature.layer.view.highlight) {
            view.popup.highlightHandle = feature.layer.view.highlight(feature);
          }      
        });
    ‍‍‍‍‍‍‍

  • visible property change, once it's not visible, clear any highlight and removeAll the features from the identify layer.

This is awful a lot of (async) work that needs to be done to display the identify results, display them using their own layer's renderers (real symbols), and enable popup to be able to zoom to and highlight the features.

Hope that helps.

0 Kudos