UNPKG

@geogirafe/lib-geoportal

Version:

GeoGirafe is a flexible application to build online geoportals.

245 lines (244 loc) 9.56 kB
import LayerWms from '../../../models/layers/layerwms.js'; import LayerWmts from '../../../models/layers/layerwmts.js'; import GeoConsts from '../../../tools/geoconsts.js'; import { isLayerVisible } from './printUtils.js'; import LegendHelper from '../../../tools/legendhelper.js'; import LayerWmsExternal from '../../../models/layers/layerwmsexternal.js'; /** * Class representing an encoder for creating a legend in the MFP (MapFishPrint) format. * Iterates on the layer tree layers to collect layers in the right order and with all metadata. */ export class MFPLegendEncoder { options; defaultOptions = { useExtent: true, label: {}, params: {}, showGroupsTitle: true }; /** * Sets the options for encoding legend. */ setOptions(options) { this.options = { ...this.defaultOptions, ...options }; } /** * Encodes the legend based on the provided options. * @returns The encoded legend or null if no legend classes were found. */ encodeLegend(options) { this.setOptions(options); const layerLegend = this.encodeLayersLegend(options.state.layers.layersList); const legend = { classes: layerLegend }; return legend.classes.length > 0 ? legend : null; } /** * Encodes the legends of every given base layers (recursively). * @returns The encoded legends as an array of MFPLegendClass objects. */ encodeLayersLegend(layers) { const groupClasses = []; [...layers].forEach((layer) => { const item = this.encodeLayerLegendClasses(layer); if (item) { this.addClassItemToArray(groupClasses, item); } }); return groupClasses; } /** * Encodes the legend classes for a given layer, based on its type or instance. * @returns The encoded legend classes for the layer, or null if the layer is not supported. */ encodeLayerLegendClasses(layer) { if (layer.children) { return this.encodeLayerGroupLegendClasses(layer); } if (layer instanceof LayerWms || layer instanceof LayerWmsExternal) { return this.encodeLayerWmsLegendClasses(layer); } if (layer instanceof LayerWmts) { return this.encodeLayerWmtsLegendClasses(layer); } return null; } /** * @returns The encoded legend class for the layer group. */ encodeLayerGroupLegendClasses(layerGroup) { const legendGroupItem = {}; if (this.options?.showGroupsTitle) { legendGroupItem.name = this.options?.i18nManager.getTranslation(layerGroup.name); } const groupClasses = []; legendGroupItem.classes = groupClasses; layerGroup.children.forEach((childLayer) => { const child = this.encodeLayerLegendClasses(childLayer); if (child) { this.addClassItemToArray(groupClasses, child); } }); return this.tryToSimplifyLegendGroup(legendGroupItem); } /** * @returns The encoded legend classes for a given WMTS * layer, or null if the layer is not visible or inactive. */ encodeLayerWmtsLegendClasses(layerWmts) { if (!isLayerVisible(layerWmts, this.options?.printResolution) || layerWmts.inactive) { return null; } let icon_dpi = this.getMetadataLegendImage(layerWmts); if (!icon_dpi && layerWmts._olayer) { const url = LegendHelper.getWMTSLegendURL(layerWmts._olayer); if (url) { icon_dpi = { url: url, dpi: GeoConsts.SCREEN_DOTS_PER_INCH }; } } if (icon_dpi) { return { name: this.options?.i18nManager.getTranslation(layerWmts.name), icons: [icon_dpi.url] }; } return null; } /** * @returns The encoded legend classes for a WMS * layer, or null if the layer is not visible or inactive. */ encodeLayerWmsLegendClasses(layerWms) { if (!isLayerVisible(layerWms, this.options?.printResolution) || layerWms.inactive) { return null; } const ogcServer = layerWms.ogcServer; const serverType = ogcServer?.type ?? ''; const dpi = this.options?.dpi ?? GeoConsts.SCREEN_DOTS_PER_INCH; // Case node as a legend image const icon_dpi = this.getMetadataLegendImage(layerWms); if (icon_dpi) { return this.getLegendClassForWMS(layerWms.name, icon_dpi, serverType); } // Case node has no legend image => Get the url for each WMS layer. const legendLayerClasses = []; const legendGroupItem = { classes: legendLayerClasses }; if (this.options?.showGroupsTitle) { legendGroupItem.name = this.options?.i18nManager.getTranslation(layerWms.name); } const layerNames = layerWms.layers?.split(',') ?? []; layerNames.forEach((name) => { const url = LegendHelper.getWMSLegendURL(layerWms.ogcServer, name, { dpi, serverType, scale: this.options?.scale, bbox: this.options?.extent, srs: this.options?.mapManager.getMap().getView().getProjection().getCode(), additionalQueryString: this.options?.params ? this.options?.params[serverType] : undefined }); if (!url) { console.error('Missing legend url for wms: ', layerWms.name); return; } const icon_dpi = { url: url, dpi: serverType === 'qgisserver' ? dpi : GeoConsts.SCREEN_DOTS_PER_INCH }; legendLayerClasses.push(this.getLegendClassForWMS(name, icon_dpi, serverType)); }); // Case of wms layer with only one layer, use the node name and remove the useless class level. if (legendLayerClasses.length === 1) { const firstLegendLayer = legendLayerClasses[0]; delete firstLegendLayer.classes; firstLegendLayer.name = this.options?.i18nManager.getTranslation(layerWms.name); return firstLegendLayer; } return this.tryToSimplifyLegendGroup(legendGroupItem); } /** * @returns a MFPLegendClass object representing the legend class for a WMS layer name. */ getLegendClassForWMS(layerName, icon_dpi, serverType) { const legendItem = { name: this.mustHideLabel(serverType) ? '' : this.options?.i18nManager.getTranslation(layerName), icons: [icon_dpi.url] }; if (icon_dpi.dpi !== GeoConsts.SCREEN_DOTS_PER_INCH) { Object.assign(legendItem, { dpi: icon_dpi.dpi }); } return legendItem; } /** * Retrieves the metadata legend image or hiDPILegendImages for the given layer. * @returns The metadata legend image URL and DPI, or null if not found. */ getMetadataLegendImage(layer) { let dpi = this.options?.dpi ?? -1; if (dpi === -1) { dpi = GeoConsts.SCREEN_DOTS_PER_INCH; } let found_dpi = dpi; let legendImage = layer.legendImage; const hiDPILegendImages = layer.hiDPILegendImages; let dist = Number.MAX_VALUE; if (legendImage) { dist = Math.abs(Math.log(GeoConsts.SCREEN_DOTS_PER_INCH / dpi)); found_dpi = GeoConsts.SCREEN_DOTS_PER_INCH; } if (hiDPILegendImages) { for (const str_dpi in hiDPILegendImages) { const new_dpi = parseFloat(str_dpi); const new_dist = Math.abs(Math.log(new_dpi / dpi)); if (new_dist < dist) { dist = new_dist; found_dpi = new_dpi; legendImage = hiDPILegendImages[str_dpi]; } } } if (legendImage) { return { url: legendImage, dpi: found_dpi }; } return null; } /** * Determines whether the label for a specific server type must be hidden. * @returns True if the label must be hidden, false otherwise. * @protected */ mustHideLabel(serverType) { const label = this.options?.label; return !!label && label[serverType] === false; } /** * Add a classItem to a classes array if the classItem to add is not null. * If the classItem have embedded classes, these classes must have classItem. Otherwise, the given * classItem will be not added. * @protected */ addClassItemToArray(classes, classItem) { if (classItem && (classItem.classes ? classItem.classes.length > 0 : true)) { classes.push(classItem); } } /** * If a Legend item have only one child and the child name is identical to its name, then return * only the children (cut one level). Shrink also if both names are null or undefined. * Otherwise, return the given legend item. * @returns The same legend item or a shrunk one. * @protected */ tryToSimplifyLegendGroup(legendGroupItem) { if (legendGroupItem.classes?.length === 1 && legendGroupItem.classes[0].name === legendGroupItem.name) { return legendGroupItem.classes[0]; } return legendGroupItem; } }