Skip to content

Conversation

@meyerlor
Copy link
Contributor

@meyerlor meyerlor commented Sep 30, 2025

I hope to help and fix the missing legends for external WMS services:

#5934

External WMS layers were showing empty legend icons because QGIS Server
returns empty icon fields in JSON GetLegendGraphic responses.

  • Added getLegendGraphicPNG() method to WMS class
  • Modified updateLayerTreeLayersSymbology() to detect external WMS layers
  • External WMS layers now fetch PNG legend and convert to base64
  • Normal layers continue using JSON format (no breaking changes)

I'm not sure if that is enough to get a proper legend from the WMS, i was not able to run proper tests, i just wanted to lay the groundwork to get WMS legends working (i hope it does not interfere with the recent changes which sped up the loading time so much! 😃 )

External WMS layers were showing empty legend icons because QGIS Server
   returns empty icon fields in JSON GetLegendGraphic responses.

   - Added getLegendGraphicPNG() method to WMS class
   - Modified updateLayerTreeLayersSymbology() to detect external WMS layers
   - External WMS layers now fetch PNG legend and convert to base64
   - Normal layers continue using JSON format (no breaking changes)
@github-actions github-actions bot added this to the 3.11.0 milestone Sep 30, 2025
@rldhont
Copy link
Collaborator

rldhont commented Oct 6, 2025

Thanks @meyerlor

Can you provide a printscreen with your fix ? I'm not sure that the GetLegendGraphic will be well displayed as an icon.

@meyerlor
Copy link
Contributor Author

meyerlor commented Oct 8, 2025

I'm away from a computer the next three weeks, will get back at it as soon I'm back!

@meyerlor meyerlor force-pushed the external-wms-legend-icon branch from ac3f5a4 to 1e8f8ba Compare November 9, 2025 09:46
Fixed the external WMS layer detection to use the correct property path
and handle both boolean and string values from the backend.

Changes:
- Updated path from itemState.externalWmsToggle to
  itemState.layerConfig.externalWmsToggle
- Added support for string value 'True' in addition to boolean true
- Cleaned up unnecessary comments for better code clarity

This ensures external WMS layers are properly detected and their
legends are fetched in PNG format instead of JSON.
Previously, externalWmsToggle was only set for layers with EPSG:3857.
This change enables it for ALL external WMS layers regardless of CRS.

Changes:
- Set externalWmsToggle = 'True' for all external WMS layers
- Keep EPSG:3857 specific handling for future use
- Allows legend display for external WMS in any projection

This works together with the JavaScript improvements to properly
display legends for all external WMS layers.
Updated the property path to access externalWmsToggle correctly
through the internal object structure.

Changes:
- Access via _mapItemState._layerItemState._layerTreeItemCfg._layerCfg
- Check _externalWmsToggle (private property with underscore)
- Maintains support for both boolean true and string 'True'

This ensures external WMS layers are properly detected and their
legends are displayed correctly.
Added LAYERTITLE: 'FALSE' parameter for external WMS layers to
suppress the layer title in the legend graphic response.

Changes:
- Moved wmsParams definition into separate if/else blocks
- External WMS: wmsParams includes LAYERTITLE: 'FALSE'
- Normal layers: wmsParams without LAYERTITLE (unchanged behavior)

This improves the legend display by removing unnecessary titles
from external WMS layer legends.
@meyerlor
Copy link
Contributor Author

meyerlor commented Nov 14, 2025

I worked on it, now it's displaying for external wms the legend as PNG:
grafik

The first layer Pachtflächen is a rule based styled layer, here still the json is retrieved.

Restored all original comments that were removed in previous commits:
- Added back comment about empty tree layers check
- Restored comment about fetching PNG and converting to base64
- Restored inline comment about removing data:image/png;base64 prefix
- Restored comment about fallback to default icon
- Restored comments about symbology type property checks
- Restored detailed error handling comments for POST method timeout

Also removed German comment and replaced with original structure:
- Removed: 'POST method code bleibt unverändert...'

This improves code readability and maintains original documentation.
@rldhont rldhont added legend/layer tree Tool which displays the layer tree with legends backport release_3_9 backport release_3_10 labels Nov 26, 2025
Copy link
Collaborator

@rldhont rldhont left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @meyerlor,

First of all, thanks for your pull request. I suggest some improvements.

The main for me is to do not load the legend image and simulate a symbology but to enhance the Treeview component to display the legend as image when it is needed.

You will have to enhance the this._layerTemplate in assets/src/components/Treeview.js

Comment on lines +36 to +38
const layerCfg = treeLayer._mapItemState?._layerItemState?._layerTreeItemCfg?._layerCfg;
const isExternalWMS = layerCfg?._externalWmsToggle === true
|| layerCfg?._externalWmsToggle === 'True';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const layerCfg = treeLayer._mapItemState?._layerItemState?._layerTreeItemCfg?._layerCfg;
const isExternalWMS = layerCfg?._externalWmsToggle === true
|| layerCfg?._externalWmsToggle === 'True';
const isExternalWMS = treeLayer.layerConfig.externalWmsToggle;

}).catch((error) => {
console.error(error);
});
} else {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
} else {
if (!isExternalWMS) {

Request JSON only for non external WMS

Comment on lines +40 to +70
if (isExternalWMS) {
// For external WMS layers, get PNG legend directly
const wmsParams = {
LAYER: treeLayer.wmsName,
STYLES: treeLayer.wmsSelectedStyleName,
LAYERTITLE: 'FALSE',
};
try {
const pngUrl = wms.getLegendGraphicPNG(wmsParams);
// Fetch the PNG and convert to base64
const response = await fetch(pngUrl);
const blob = await response.blob();
const reader = new FileReader();

await new Promise((resolve, reject) => {
reader.onloadend = () => {
const base64data = reader.result.split(',')[1]; // Remove data:image/png;base64, prefix
treeLayer.symbology = {
type: 'layer',
name: treeLayer.wmsName,
title: treeLayer.name,
icon: base64data
};
resolve();
};
reader.onerror = reject;
reader.readAsDataURL(blob);
});
} catch (error) {
console.error('Error loading external WMS legend:', error);
// Fallback to default icon will be handled by symbology state
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (isExternalWMS) {
// For external WMS layers, get PNG legend directly
const wmsParams = {
LAYER: treeLayer.wmsName,
STYLES: treeLayer.wmsSelectedStyleName,
LAYERTITLE: 'FALSE',
};
try {
const pngUrl = wms.getLegendGraphicPNG(wmsParams);
// Fetch the PNG and convert to base64
const response = await fetch(pngUrl);
const blob = await response.blob();
const reader = new FileReader();
await new Promise((resolve, reject) => {
reader.onloadend = () => {
const base64data = reader.result.split(',')[1]; // Remove data:image/png;base64, prefix
treeLayer.symbology = {
type: 'layer',
name: treeLayer.wmsName,
title: treeLayer.name,
icon: base64data
};
resolve();
};
reader.onerror = reject;
reader.readAsDataURL(blob);
});
} catch (error) {
console.error('Error loading external WMS legend:', error);
// Fallback to default icon will be handled by symbology state

Do not request the image and simulate a child icon. I prefer to enhance the treeview component to display the legend image has a child.

Comment on lines +2048 to +2050
// Set externalWmsToggle for ALL external WMS layers (not just EPSG:3857)
$obj->externalWmsToggle = 'True';
$obj->externalAccess = $layerDatasource;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Set externalWmsToggle for ALL external WMS layers (not just EPSG:3857)
$obj->externalWmsToggle = 'True';
$obj->externalAccess = $layerDatasource;
if (array_key_exists('type', $layerDatasource)
&& $layerDatasource['type'] == 'wms') {
// Set externalWmsToggle for ALL external WMS layers (not just EPSG:3857)
$obj->externalWmsToggle = 'True';
$obj->externalAccess = $layerDatasource;
}

Activate external only for WMS layers.

We should verify that the project's projection is recognized by the source service or that the OpenLayers layer created reuses the CRS of the source layer.

&& $layerDatasource['crs'] == 'EPSG:3857') {
$obj->externalWmsToggle = 'True';
$obj->externalAccess = $layerDatasource;
// Additional external access for EPSG:3857 layers
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keeps it for other $layerDatasource by using an else if or an || in the previous if

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport release_3_9 backport release_3_10 legend/layer tree Tool which displays the layer tree with legends

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants