Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backport release_3_9] [Bugfix] Consider the opacity of groups for printing #5519

Merged
merged 1 commit into from
Mar 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions assets/src/components/Print.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ export default class Print extends HTMLElement {

// Add visible layers in defined order
const orderedVisibleLayers = {};
mainLizmap.state.rootMapGroup.findMapLayers().forEach(layer => {
mainLizmap.state.rootMapGroup.findExplodedMapLayers().forEach(layer => {
if (layer.visibility) {
orderedVisibleLayers[layer.layerOrder] = layer;
}
Expand All @@ -267,9 +267,9 @@ export default class Print extends HTMLElement {

// Handle qgis layer opacity otherwise client value override it
if (layer.layerConfig?.opacity) {
opacityLayers.push(parseInt(255 * layer.opacity * layer.layerConfig.opacity));
opacityLayers.push(parseInt(255 * layer.calculateTotalOpacity() * layer.layerConfig.opacity));
} else {
opacityLayers.push(parseInt(255 * layer.opacity));
opacityLayers.push(parseInt(255 * layer.calculateTotalOpacity()));
}
if ('FILTERTOKEN' in layerWmsParams) {
filter.push(layerWmsParams['FILTERTOKEN']);
Expand Down
13 changes: 13 additions & 0 deletions assets/src/modules/state/Layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,19 @@ export class LayerItemState extends EventDispatcher {
}
return this._visibility;
}

/**
* Calculate total opacity by including also all parent groups opacity values
* @returns {number} the total opacity
*/
calculateTotalOpacity(){
let opacity = this.opacity;
if(this._parentGroup !== null){
opacity = opacity*this._parentGroup.calculateTotalOpacity();
}

return Math.round(opacity * 100) / 100;
}
}

/**
Expand Down
24 changes: 24 additions & 0 deletions assets/src/modules/state/MapLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,30 @@ export class MapGroupState extends MapItemState {
return layersCount;
}

/**
* Find all layers by exploding the layers within every "group as layer" groups
* @returns {LayerLayerState[]} The layer states of listed layers
*/
findExplodedMapLayers(){
let layers = [];
for(const item of this.getChildren()) {
if (item instanceof MapLayerState) {
const itemState = item.itemState;
if(itemState instanceof LayerGroupState && itemState.groupAsLayer){
//count the layers inside the group
layers = layers.concat(itemState.findLayers());
}
else if(itemState instanceof LayerLayerState){
layers.push(item.itemState);
}
} else if (item instanceof MapGroupState) {
layers = layers.concat(item.findExplodedMapLayers());
}
}

return layers;
}

/**
* Find layer items
* @returns {MapLayerState[]} The layer names of all map layers
Expand Down
83 changes: 83 additions & 0 deletions tests/end2end/playwright/pages/printpage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// @ts-check
import { expect } from '@playwright/test';
import { ProjectPage } from './project';

/**
* Playwright Page
* @typedef {import('@playwright/test').Page} Page
*/

/**
* Playwright Page
* @typedef {import('@playwright/test').Locator} Locator
*/

export class PrintPage extends ProjectPage {
// Metadata
/**
* The print panel
* @type {Locator}
*/
printPanel;

/**
* The print button on menu
* @type {Locator}
*/
printSwitcherButton;

/**
* The print scale combobox
* @type {Locator}
*/
printScale;

/**
* The launch print button on print panel
* @type {Locator}
*/
launchPrintButton;

/**
* Constructor for a QGIS project page
* @param {Page} page The playwright page
* @param {string} project The project name
* @param {string} repository The repository name, default to testsrepository
*/
constructor(page, project, repository = 'testsrepository') {
super(page, project, repository);

this.printPanel = page.locator('#print');
this.printSwitcherButton = page.locator('#button-print');
this.launchPrintButton = page.locator('#print-launch');
this.printScaleCombobox = page.locator('#print-scale');
}

/**
* openPrintPanel function
* opens the print mini-dock panel
*/
async openPrintPanel() {
await this.page.locator('#button-print').click();
}

/**
* setPrintScale function
* Set the print scale
* @param {string} scale The scale
*/
async setPrintScale(scale) {
await this.printScaleCombobox.selectOption(scale);
}

/**
* launchPrint function
* Launch print
*/
async launchPrint() {
// Launch print
await this.launchPrintButton.click();
// check message
await expect(this.page.locator('div.alert')).toHaveCount(1);
}
}
21 changes: 21 additions & 0 deletions tests/end2end/playwright/pages/project.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,27 @@ export class ProjectPage extends BasePage {
await this.page.locator('#attribute-layer-list-table').locator(`button[value=${layer}]`).click();
}

/**
* openLayerInfo function
* Open the info layer panel for the given layer
* @param {string} layer Name of the layer
*/
async openLayerInfo(layer) {
await this.page.getByTestId(layer).locator('.node').first().hover();
await this.page.getByTestId(layer).locator('.layer-actions').first().locator('i.icon-info-sign').click();
}

/**
* setLayerOpacity function
* Open the info layer panel for the given layer
* @param {string} layer Name of the layer
* @param {string} opacity Layer opacity, possible values '0','20','40','60','80','100'
*/
async setLayerOpacity(layer, opacity = '100') {
await this.openLayerInfo(layer);
await this.page.getByRole('link', { name: opacity }).click();
}

/**
* editingSubmitForm function
* Submit the form
Expand Down
148 changes: 148 additions & 0 deletions tests/end2end/playwright/print.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @ts-check

Check failure on line 1 in tests/end2end/playwright/print.spec.js

View workflow job for this annotation

GitHub Actions / E2E QGIS 3.40 PG 17-3 PHP 8.3

Failed Test: Print requests

Print requests: Error: Print requests → wrong length list : Got : 14 items With : SERVICE, REQUEST, VERSION, FORMAT, TRANSPARENT, CRS, DPI, TEMPLATE, map0:EXTENT, map0:SCALE, map0:LAYERS, map0:STYLES, map0:OPACITIES, simple_label To have: 15 items Debug : SERVICE, REQUEST, VERSION, FORMAT, TRANSPARENT, CRS, DPI, TEMPLATE, map0:EXTENT, map0:SCALE, map0:LAYERS, map0:STYLES, map0:OPACITIES, simple_label Debug count : 14 items expect(received).toHaveLength(expected) Expected length: 15 Received length: 14 Received array: ["SERVICE", "REQUEST", "VERSION", "FORMAT", "TRANSPARENT", "CRS", "DPI", "TEMPLATE", "map0:EXTENT", "map0:SCALE", …] - Error: Print requests → wrong length list : Got : 14 items With : SERVICE, REQUEST, VERSION, FORMAT, TRANSPARENT, CRS, DPI, TEMPLATE, map0:EXTENT, map0:SCALE, map0:LAYERS, map0:STYLES, map0:OPACITIES, simple_label To have: 15 items Debug : SERVICE, REQUEST, VERSION, FORMAT, TRANSPARENT, CRS, DPI, TEMPLATE, map0:EXTENT, map0:SCALE, map0:LAYERS, map0:STYLES, map0:OPACITIES, simple_label Debug count : 14 items expect(received).toHaveLength(expected) Expected length: 15 Received length: 14 Received array: ["SERVICE", "REQUEST", "VERSION", "FORMAT", "TRANSPARENT", "CRS", "DPI", "TEMPLATE", "map0:EXTENT", "map0:SCALE", …] at expectToHaveLengthCompare (/home/runner/work/lizmap-web-client/lizmap-web-client/tests/end2end/playwright/globals.js:238:7) at /home/runner/work/lizmap-web-client/lizmap-web-client/tests/end2end/playwright/print.spec.js:89:15

Check failure on line 1 in tests/end2end/playwright/print.spec.js

View workflow job for this annotation

GitHub Actions / E2E QGIS 3.40 PG 17-3 PHP 8.3

Failed Test: Print requests with selection

Print requests with selection: Error: Print requests with selection → wrong length list : Got : 15 items With : SERVICE, REQUEST, VERSION, FORMAT, TRANSPARENT, CRS, DPI, TEMPLATE, map0:EXTENT, map0:SCALE, map0:LAYERS, map0:STYLES, map0:OPACITIES, SELECTIONTOKEN, simple_label To have: 16 items Debug : SERVICE, REQUEST, VERSION, FORMAT, TRANSPARENT, CRS, DPI, TEMPLATE, map0:EXTENT, map0:SCALE, map0:LAYERS, map0:STYLES, map0:OPACITIES, simple_label, SELECTIONTOKEN Debug count : 15 items expect(received).toHaveLength(expected) Expected length: 16 Received length: 15 Received array: ["SERVICE", "REQUEST", "VERSION", "FORMAT", "TRANSPARENT", "CRS", "DPI", "TEMPLATE", "map0:EXTENT", "map0:SCALE", …] - Error: Print requests with selection → wrong length list : Got : 15 items With : SERVICE, REQUEST, VERSION, FORMAT, TRANSPARENT, CRS, DPI, TEMPLATE, map0:EXTENT, map0:SCALE, map0:LAYERS, map0:STYLES, map0:OPACITIES, SELECTIONTOKEN, simple_label To have: 16 items Debug : SERVICE, REQUEST, VERSION, FORMAT, TRANSPARENT, CRS, DPI, TEMPLATE, map0:EXTENT, map0:SCALE, map0:LAYERS, map0:STYLES, map0:OPACITIES, simple_label, SELECTIONTOKEN Debug count : 15 items expect(received).toHaveLength(expected) Expected length: 16 Received length: 15 Received array: ["SERVICE", "REQUEST", "VERSION", "FORMAT", "TRANSPARENT", "CRS", "DPI", "TEMPLATE", "map0:EXTENT", "map0:SCALE", …] at expectToHaveLengthCompare (/home/runner/work/lizmap-web-client/lizmap-web-client/tests/end2end/playwright/globals.js:238:7) at /home/runner/work/lizmap-web-client/lizmap-web-client/tests/end2end/playwright/print.spec.js:284:15

Check failure on line 1 in tests/end2end/playwright/print.spec.js

View workflow job for this annotation

GitHub Actions / E2E QGIS 3.40 PG 17-3 PHP 8.3

Failed Test: Print requests with filter

Print requests with filter: Error: Print requests with filter → wrong length list : Got : 15 items With : SERVICE, REQUEST, VERSION, FORMAT, TRANSPARENT, CRS, DPI, TEMPLATE, map0:EXTENT, map0:SCALE, map0:LAYERS, map0:STYLES, map0:OPACITIES, FILTERTOKEN, simple_label To have: 16 items Debug : SERVICE, REQUEST, VERSION, FORMAT, TRANSPARENT, CRS, DPI, TEMPLATE, map0:EXTENT, map0:SCALE, map0:LAYERS, map0:STYLES, map0:OPACITIES, simple_label, FILTERTOKEN Debug count : 15 items expect(received).toHaveLength(expected) Expected length: 16 Received length: 15 Received array: ["SERVICE", "REQUEST", "VERSION", "FORMAT", "TRANSPARENT", "CRS", "DPI", "TEMPLATE", "map0:EXTENT", "map0:SCALE", …] - Error: Print requests with filter → wrong length list : Got : 15 items With : SERVICE, REQUEST, VERSION, FORMAT, TRANSPARENT, CRS, DPI, TEMPLATE, map0:EXTENT, map0:SCALE, map0:LAYERS, map0:STYLES, map0:OPACITIES, FILTERTOKEN, simple_label To have: 16 items Debug : SERVICE, REQUEST, VERSION, FORMAT, TRANSPARENT, CRS, DPI, TEMPLATE, map0:EXTENT, map0:SCALE, map0:LAYERS, map0:STYLES, map0:OPACITIES, simple_label, FILTERTOKEN Debug count : 15 items expect(received).toHaveLength(expected) Expected length: 16 Received length: 15 Received array: ["SERVICE", "REQUEST", "VERSION", "FORMAT", "TRANSPARENT", "CRS", "DPI", "TEMPLATE", "map0:EXTENT", "map0:SCALE", …] at expectToHaveLengthCompare (/home/runner/work/lizmap-web-client/lizmap-web-client/tests/end2end/playwright/globals.js:238:7) at /home/runner/work/lizmap-web-client/lizmap-web-client/tests/end2end/playwright/print.spec.js:335:15

Check failure on line 1 in tests/end2end/playwright/print.spec.js

View workflow job for this annotation

GitHub Actions / E2E QGIS 3.40 PG 17-3 PHP 8.3

Failed Test: Print requests

Print requests: Error: Print requests 1 → wrong length list : Got : 14 items With : SERVICE, REQUEST, VERSION, FORMAT, TRANSPARENT, CRS, DPI, TEMPLATE, map0:EXTENT, map0:SCALE, map0:LAYERS, map0:STYLES, map0:OPACITIES, simple_label To have: 15 items Debug : SERVICE, REQUEST, VERSION, FORMAT, TRANSPARENT, CRS, DPI, TEMPLATE, map0:EXTENT, map0:SCALE, map0:LAYERS, map0:STYLES, map0:OPACITIES, simple_label Debug count : 14 items expect(received).toHaveLength(expected) Expected length: 15 Received length: 14 Received array: ["SERVICE", "REQUEST", "VERSION", "FORMAT", "TRANSPARENT", "CRS", "DPI", "TEMPLATE", "map0:EXTENT", "map0:SCALE", …] - Error: Print requests 1 → wrong length list : Got : 14 items With : SERVICE, REQUEST, VERSION, FORMAT, TRANSPARENT, CRS, DPI, TEMPLATE, map0:EXTENT, map0:SCALE, map0:LAYERS, map0:STYLES, map0:OPACITIES, simple_label To have: 15 items Debug : SERVICE, REQUEST, VERSION, FORMAT, TRANSPARENT, CRS, DPI, TEMPLATE, map0:EXTENT, map0:SCALE, map0:LAYERS, map0:STYLES, map0:OPACITIES, simple_label Debug count : 14 items expect(received).toHaveLength(expected) Expected length: 15 Received length: 14 Received array: ["SERVICE", "REQUEST", "VERSION", "FORMAT", "TRANSPARENT", "CRS", "DPI", "TEMPLATE", "map0:EXTENT", "map0:SCALE", …] at expectToHaveLengthCompare (/home/runner/work/lizmap-web-client/lizmap-web-client/tests/end2end/playwright/globals.js:238:7) at /home/runner/work/lizmap-web-client/lizmap-web-client/tests/end2end/playwright/print.spec.js:726:15
import { test, expect } from '@playwright/test';
import {PrintPage} from "./pages/printpage";
import { gotoMap, expectParametersToContain, getAuthStorageStatePath, expectToHaveLengthCompare } from './globals';

test.describe('Print', () => {
Expand Down Expand Up @@ -335,6 +336,153 @@
});
});

test.describe(
'Print opacities',
{
tag: ['@readonly'],
},() =>
{

test('Group as layer', async ({ page }) => {

const printPage = new PrintPage(page, 'group_as_layer_opacity');
await printPage.open();
await printPage.openPrintPanel();
await printPage.setPrintScale('50000');

// set opacity of `Group_opacity` (group as layer) layer to 60%
await printPage.setLayerOpacity('Group_opacity','60');

// opacity notes:
// the `raster` layer already has 80% opacity (inherited from QGIS project)
// setting the opacity to 60% on the `Group_opacity` causes:
// - `raster` layer to have a final total opacity of 48% (0.8*0.6)
// -> OPACITIES PARAM = 255*0.48 ~= 122
// - `single_wms_polygons` layer to have a final total opacity of 60% (1*0.6)
// -> OPACITIES PARAM = 255*0.6 = 153
// other layers have opacity 100% (255)

const mapLayers = [
'OpenStreetMap',
'raster',
'single_wms_polygons',
'single_wms_polygons_group_as_layer',
'single_wms_points_group',
]

const expectedParameters = {
'SERVICE': 'WMS',
'REQUEST': 'GetPrint',
'VERSION': '1.3.0',
'FORMAT': 'pdf',
'TRANSPARENT': 'true',
'CRS': 'EPSG:3857',
'DPI': '100',
'TEMPLATE': 'test',
'map0:EXTENT': /425189.\d+,5401412.\d+,439539.\d+,5411262.\d+/,
'map0:SCALE': '50000',
'map0:LAYERS': mapLayers.join(','),
'map0:STYLES': 'default,default,default,default,default',
'map0:OPACITIES': '255,122,153,255,255',
}
// Test `test` template
let getPrintPromise = page.waitForRequest(
request =>
request.method() === 'POST' &&
request.postData()?.includes('GetPrint') === true
);

// Launch print
await printPage.launchPrint();

// check request
let getPrintRequest = await getPrintPromise;

// check response parameters
let name = "Group as layer opacity requests";
let getPrintParams = await expectParametersToContain(
name, getPrintRequest.postData() ?? '', expectedParameters);

await expectToHaveLengthCompare(
name,
Array.from(getPrintParams.keys()),
13,
Object.keys(expectedParameters)
);
})

test('Layers in group with opacity', async ({ page }) => {
const printPage = new PrintPage(page, 'group_as_layer_opacity');
await printPage.open();
await printPage.openPrintPanel();
await printPage.setPrintScale('50000');

// set opacity of `Group_a` group opacity to 60%
await printPage.setLayerOpacity('Group_a','60');
// set opacity of `single_wms_points_group` (belonging to `Group_a`) layer to 40%
await printPage.setLayerOpacity('single_wms_points_group','40');
// set opacity of `single_wms_polygons_group_as_layer` (belonging to `Group_a`) layer to 80%
await printPage.setLayerOpacity('single_wms_polygons_group_as_layer','80');

// opacity notes:
// setting the opacity to 60% on `Group_a` causes:
// - `single_wms_points_group` layer to have a final total opacity of 24% (0.4*0.6)
// -> OPACITIES PARAM = 255*0.24 ~= 61
// - `single_wms_polygons_group_as_layer` layer to have a final total opacity of 48% (0.8*0.6)
// -> OPACITIES PARAM = 255*0.48 ~= 122
// other layers have opacity 100%, except for `raster` layer which has a 80% opacity by default,
// resulting in a OPACITIES PARAM = 255*0.8 = 204

const mapLayers = [
'OpenStreetMap',
'raster',
'single_wms_polygons',
'single_wms_polygons_group_as_layer',
'single_wms_points_group',
]

const expectedParameters = {
'SERVICE': 'WMS',
'REQUEST': 'GetPrint',
'VERSION': '1.3.0',
'FORMAT': 'pdf',
'TRANSPARENT': 'true',
'CRS': 'EPSG:3857',
'DPI': '100',
'TEMPLATE': 'test',
'map0:EXTENT': /425189.\d+,5401412.\d+,439539.\d+,5411262.\d+/,
'map0:SCALE': '50000',
'map0:LAYERS': mapLayers.join(','),
'map0:STYLES': 'default,default,default,default,default',
'map0:OPACITIES': '255,204,255,122,61',
}
// Test `test` template
let getPrintPromise = page.waitForRequest(
request =>
request.method() === 'POST' &&
request.postData()?.includes('GetPrint') === true
);

// Launch print
await printPage.launchPrint();

// check request
let getPrintRequest = await getPrintPromise;

// check response parameters
let name = "Layers in group with opacity request";
let getPrintParams = await expectParametersToContain(
name, getPrintRequest.postData() ?? '', expectedParameters);
await expectToHaveLengthCompare(
name,
Array.from(getPrintParams.keys()),
13,
Object.keys(expectedParameters)
);
})
}
);

test.describe('Print in popup', () => {
test.beforeEach(async ({ page }) => {
const url = '/index.php/view/map/?repository=testsrepository&project=print';
Expand Down
Loading
Loading