Wrong symbology in 4.17 Legend widget

1504
6
10-21-2020 01:09 PM
JoelBennett
MVP Regular Contributor

I've observed the Legend widget displaying the wrong symbology for some layers; in particular, the Legend will take the symbology for "layer b" and display it for "layer a".  I'm fairly certain this is the same problem mentioned in this thread, which was observed in 4.16.  Below is a description of the cause and a proposed 4.17 solution for those who need a fix "now".

The ActiveLayerInfo class has an internal dictionary for caching legend information retrieved from the server. Keys for this dictionary are generated for each service based upon whether or not the service supports dynamic layers. If dynamic layers are supported, the key is the long JSON string passed as the "dynamicLayers" parameter in the legend request. If dynamic layers are not supported, the key for the layer is the string "default".

This cache is shared across all instances of ActiveLayerInfo. The intent here is good, in that it can eliminate redundant requests to the server for information that the application has already retrieved. However, the key generation is highly susceptible to collisions.

For example, it is possible for multiple layers that do not support dynamic layers to share the same key "default". Since there is only one entry in the dictionary possible for this key, it will always store the most recently retrieved legend response. As a result, when layer symbology is rendered in the legend, a layer can incorrectly display the symbology for a completely different layer.

A fix for this is to generate better keys that have fewer collisions. If you're using a locally hosted copy of the API, you can do this yourself.  In my solution, I've prepended the layer's URL to the key. In this case you will need to do two find-and-replace operations in the file esri/widgets/Legend.js.  Note, this solution is for version 4.17, and may not work with other versions.

Part 1: search for (in function _getLegendLayers):

var b=(a=a&&a.hasDynamicLayers?a.dynamicLayers:null)||"default",e=I&&I[b];

Replace with:

var b=this.layer.url+((a=a&&a.hasDynamicLayers?a.dynamicLayers:null)||"default"),e=I&&I[b];

Part 2: search for (in function _generateLegendElementsForSublayers):

p=f.hasDynamicLayers&&f.dynamicLayers||"default"

Replace with:

p=this.layer.url+(f.hasDynamicLayers&&f.dynamicLayers||"default")

It should be further noted that my application uses multiple instances of the Legend widget simultaneously, but if I understand this problem correctly, it could still manifest itself when using a single Legend instance.

0 Kudos
6 Replies
NilsBabel1
Occasional Contributor

I'm having this exact same problem.  Your reasoning sounds correct.  However, I'm using the API through webpack and node.js.  It's minimized so I can't find the getLegendLayers function.  Have you contacted anyone at esri about this?  This sounds like a pretty big bug.  I have a lot of different map services in my application.  I'm using the layerlist widget with the legend embedded.  I'm getting all kinds of unexpected results with layers drawing different legends.  Any update on your solution?  Esri, any comment on this bug?

Thanks!

0 Kudos
by Anonymous User
Not applicable

There is a bug logged for this issue in the ArcGIS API for JavaScript 4.15-4.17. It has been fixed for the API version 4.18 of which you can currently test the pre-release by updating the version to 4.18 in your application's html.

0 Kudos
JoelBennett
MVP Regular Contributor

I have downloaded version 4.18, tested my application, and found that this issue has not been fixed. In my situation, I have the following:

  1. Layer A (a MapImageLayer with no dynamicLayers) is visible by default, and loaded into the map.
  2. Layer B (a TileLayer with no dynamicLayers) is not visible by default, and is also loaded into the map.
  3. A Legend widget is created for Layer A and correctly displays the symbology for Layer A.
  4. A Legend widget is created for Layer B, but displays nothing because the layer is not visible.
  5. Layer B is set to visible, and Legend B correctly displays the symbology for Layer B.
  6. After zooming in on the map, Legend A refreshes and now incorrectly displays the symbology for Layer B. (Legend B continues to display the symbology for Layer B, as it should.)

 

My workaround for 4.18 is the same as for 4.17, although the code to replace in esri/widgets/Legend.js is slightly different:

 

Part 1: search for (in function _getLegendLayers):

(a=a&&a.hasDynamicLayers?a.dynamicLayers:null)||"default",A=va&&va[q];

Replace with:

this.layer.url+((a=a&&a.hasDynamicLayers?a.dynamicLayers:null)||"default"),A=va&&va[q];

 


Part 2: search for (in function _generateLegendElementsForSublayers):

W=Q.hasDynamicLayers&&Q.dynamicLayers||"default";

Replace with:

W=this.layer.url+(Q.hasDynamicLayers&&Q.dynamicLayers||"default");

 

0 Kudos
MichailMarinakis1
Occasional Contributor II

Hi Joel, 

thanks a lot for the workaround! It works like charm! 

We have the same issue with a single Legend instance. The problem for us, is happening when you load multiple map image layers that include sublayers with same ids. For example Map image layer A with sublayer 1 (let's call it A1) and map image layer B with sublayer 1 (let's call it B1) . Depending the order they are loaded, sometimes the legend for A1 will have the values of B1 and sometimes the legend for B1 will have the values of A1 etc. 

It looks like there is no solution in 4.18. At least, there is nothing in the release notes about it. Hopefully it will be fixed in 4.19! 

0 Kudos
JoelBennett
MVP Regular Contributor

This was fixed in version 4.19.  However, the fix has caused new problems, as seen here.

0 Kudos
MichailMarinakis1
Occasional Contributor II

Thanks for the heads up! I will subscribe to the new post. 

0 Kudos