Select to view content in your preferred language

LegendUrl not set when loading WMS with sublayers

246
5
Jump to solution
3 weeks ago
SebastianKrings
Frequent Contributor

Hi,

in my Arc JS App I use the legend widget.

private _legendWidget?: Legend;

    @ViewChild('legendDiv', { static: false }) private _legendDiv?: ElementRef;

    async ngAfterViewInit(): Promise<void> {
        const view = await this._mvp.getMapViewAsync();
        this._legendWidget = new Legend({ view, container: this._legendDiv?.nativeElement });
    }

 

When I add a WMS Layer with sublayers I recognize that these are not shown in the legend widget.
E.G. from the following WMS I try using the sublayer with name "dwd:Ewam_reg025_fd_sl_SHTS"

https://maps.dwd.de/geoserver/ows?service=WMS&version=1.3.0&request=GetCapabilities

 

There I can see

<Style>
	<Name>ewam_reg025_fd_sl_shts</Name>
	<Title>Wellenhöhe</Title>
	<Abstract>style for wind waves and sewll isoflaechen</Abstract>
	<LegendURL width="91" height="252">
		<Format>image/png</Format>
		<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="https://maps.dwd.de/geoserver/ows?service=WMS&version=1.3.0&request=GetLegendGraphic&format=image%2Fpng&width=20&height=20&layer=dwd%3AEwam_reg025_fd_sl_SHTS"/>
	</LegendURL>
</Style>

 

So it seems all necessary information are in place but somehow the legend url is not taken by arcgis.
When debugging I can see at the layer object that legendEnabled is set to true while legendUrl is null.

When I manually insert the legend URL into the property of the layer, the legend widget does show the legend correctly.

This is how we load the layer:

private getWmsLayerProperties(wmsLayerConfig: WmsLayerConfig) {
	const properties: __esri.WMSSublayerProperties = {};
	properties.name = "dwd:Ewam_reg025_fd_sl_SHTS";

	return {
		id: wmsLayerConfig.id,
		url: wmsLayerConfig.url,
		title: wmsLayerConfig.title,
		opacity: wmsLayerConfig.opacity,
		refreshInterval: wmsLayerConfig.refreshInterval,
		sublayers: [properties],
		visible: wmsLayerConfig.visible ?? false
	}
}

private loadLayer() {
	const layer = new WMSLayer(this.getWmsLayerProperties(config));
	layer.load();
}

 

From my understanding the layer.load does some magic including to load the defined sublayers.
But unfortunately by this the legendUrl is not taken and keeps beeing null.

 

Of course I could add the legendUrl by hand but we are talking about much layers and those the user may indiviudally add to the map. So I need to find a way to either get ArcGIS do that job for me or else to dynamically load/find the legend url of the given sub layer.

Obviously I would prefer the first. Thanks.

0 Kudos
1 Solution

Accepted Solutions
AnneFitz
Esri Regular Contributor

When setting the `sublayers` property directly in the WMSLayer's constructor, this will override the properties returned from the service's GetCapabilities response, which is why the LegendURL gets stripped.

To maintain all the sublayer's properties from the service, we would recommend setting the sublayers property after the layer has loaded. For example: 

const layer = new WMSLayer({
  url: // url to the service
});
await layer.load();
// filter by a given sublayer
const sublayer = layer.findSublayerByName("My Sublayer");
if (sublayer) {
  layer.sublayers = [sublayer];
}
map.add(layer);

You'll see this pattern promoted throughout the SDK documentation, for instance in the API reference and sample.

View solution in original post

0 Kudos
5 Replies
SebastianKrings
Frequent Contributor

We did a debugging in the dev console of the browser.

In ArcGIS Online we used the default add layer function, to add the layer and the specific sublayer

We found in addfromurl.js line 553 this code:

 

   else if ("Style" !== Q.nodeName || I.legendUrl)
                            "Layer" === Q.nodeName && (Q = n(Q, J, M)) && (Q.parentLayerId = I.id,
                            I.sublayers || (I.sublayers = []),
                            I.sublayers.push(Q));
                        else if (Q = t("LegendURL", Q))
                            if (Q = t("OnlineResource", Q))
                                I.legendUrl = Q.getAttribute("xlink:href")
                    }

 

which i called within a.parsecapabilities method.

So esri has routines to solve the legendUrl from sublayer by itself. Seems this is not included in the layer.load() function.

0 Kudos
SebastianKrings
Frequent Contributor

just found another interesting thing

at runtime the layer contains a resourceInfo property which contain all 198 sublayers including their respective legendUrl.

This brought me to the idea of not defining the desired sublayername in the properties at first. With the aim to get all layers with their legendUrl. After this I may filter all unwanted layers out.
The good news it really happens like that, all sublayers are loaded automatically with their legendUrl.
The bad new is, I do not know how to properly remove all 197 unwanted layers.
While layer.allSublayers shows a flattened list layer.sublayers shows a hierarchical list.

Do I only edit the layer.sublayers collection and put my desired sublayer as single/flat item into this collection without any parents (I dont want them)?
Do I also need to update the layer.allSublayers collection or will it somehow automatically update itself?
Neither?

Or do I keep my old way defining the sublayer name in the properties and after layer.load() update the sublayer with the legendUrl from the layer.resourceInfo?
But dont I need to re-run layer.load after updating the legendUrl or will the legendWidget recognize this?
I would prefer the most clean and safe way to get rid of that.

0 Kudos
AnneFitz
Esri Regular Contributor

When setting the `sublayers` property directly in the WMSLayer's constructor, this will override the properties returned from the service's GetCapabilities response, which is why the LegendURL gets stripped.

To maintain all the sublayer's properties from the service, we would recommend setting the sublayers property after the layer has loaded. For example: 

const layer = new WMSLayer({
  url: // url to the service
});
await layer.load();
// filter by a given sublayer
const sublayer = layer.findSublayerByName("My Sublayer");
if (sublayer) {
  layer.sublayers = [sublayer];
}
map.add(layer);

You'll see this pattern promoted throughout the SDK documentation, for instance in the API reference and sample.

0 Kudos
SebastianKrings
Frequent Contributor

The way using findSublayerByName is great, it saves me from doing a recursive search on my own.

May the sample be a bit outdated but layer.sublayers is a collection and therefore not assignable with an array as suggested by you and used in the sample.

My code now is:

layer.when(() => {
            if (wantedNames?.length > 0) {
                const wantedSublayers: __esri.WMSSublayer[] = [];
                wantedNames.forEach(name => {
                    const subLayer = layer.findSublayerByName(name);
                    if (subLayer) {
                        wantedSublayers.push(subLayer);
                    }
                });
                
                layer.sublayers.removeAll();
                layer.sublayers.addMany(wantedSublayers);
            }
            
        });

 

0 Kudos
AnneFitz
Esri Regular Contributor

The sublayers property supports autocasting, so it should still work if you set it from an array. As of version 4.32, autocasting is now supported in TypeScript applications.

0 Kudos