UNPKG

@geogirafe/lib-geoportal

Version:

GeoGirafe is a flexible application to build online geoportals.

159 lines (158 loc) 6.62 kB
import PrintMaskLayer from './printMaskLayer.js'; import GeoConsts from '../../../tools/geoconsts.js'; import { getOlayerByName } from '../../../tools/utils/olutils.js'; import { unByKey } from 'ol/Observable.js'; const PRINT_MASK_LAYER_NAME = 'PrintMask'; /** * Independent manager to display a mask layer adapted to the print. * Listen to print events to auto setup. */ class PrintMaskManager { stateManager; printMaskLayer = new PrintMaskLayer({ name: PRINT_MASK_LAYER_NAME }); map; eventsCallbacks = []; olEventsKeys = []; possibleScales = []; scaleManuallySelected = false; constructor(map, stateManager) { this.map = map; this.stateManager = stateManager; this.printMaskLayer.setGetScaleFn(this.getScaleFn.bind(this)); this.registerEvents(); } get state() { return this.stateManager.state; } destroy() { this.stateManager.unsubscribe(this.eventsCallbacks); this.eventsCallbacks.length = 0; unByKey(this.olEventsKeys); this.olEventsKeys.length = 0; this.state.print.maskVisible = false; this.onPrintMaskVisibleChanged(); } /** * Sets the possible scales to fit the mask/view to. */ setPossibleScales(scales) { this.possibleScales = [...scales].sort((v1, v2) => v2 - v1).reverse(); } /** * Sets the resolution of the map view to the optimal resolution for a given scale. */ zoomToScale(scale) { const mapSize = this.map.getSize() ?? [0, 0]; const printMapSize = this.state.print.pageSize ?? [0, 0]; const resolution = PrintMaskManager.getOptimalResolution(mapSize, printMapSize, scale); const constraintRes = this.map.getView().getConstraints().resolution(resolution, 1, mapSize, undefined); // Set the resolution (this emits the change:resolution event). this.map.getView().setResolution(constraintRes); // After the change:resolution event, force the map to render at the next key frame with // scaleManuallySelected to true. this.map.render(); this.scaleManuallySelected = true; } /** * Register the events for: * - printing pageSize and mask visibility changes. * - set scaleManuallySelected to false when the resolution changes. */ registerEvents() { this.eventsCallbacks.push(...[ this.stateManager.subscribe('print.pageSize', () => this.onPrintFormatChanged()), this.stateManager.subscribe('print.maskVisible', () => this.onPrintMaskVisibleChanged()) ]); this.olEventsKeys.push(this.map.getView().on('change:resolution', () => { this.scaleManuallySelected = false; })); } /** * Sets the print.scale value. * Calculate the optimal scale for printing based on the map's current frame state and the possible scales. * @returns the optimal scale. * @private */ getScaleFn(frameState) { // BGE debounce this could be great here const mapSize = frameState.size; const viewResolution = frameState.viewState.resolution; console.assert(this.state.print.pageSize !== null, 'Can not get optimal scale without print pageSize'); const pageSize = this.state.print.pageSize ?? [0, 0]; let optimalScale; if (this.scaleManuallySelected) { optimalScale = this.state.print.scale ?? 10000; } else { optimalScale = PrintMaskManager.getOptimalScale(mapSize, viewResolution, pageSize, this.possibleScales); } this.state.print.scale = optimalScale; return this.state.print.scale; } /** * Display the mask or hide it. * @private */ onPrintMaskVisibleChanged() { const isLayerInMap = getOlayerByName(this.map, PRINT_MASK_LAYER_NAME); if (this.state.print.maskVisible && !isLayerInMap) { this.map.addLayer(this.printMaskLayer); return; } if (!this.state.print.maskVisible && isLayerInMap) { this.map.removeLayer(this.printMaskLayer); } } /** * Update the size (width, height), in pixels of the mask. * @private */ onPrintFormatChanged() { if (!this.state.print.pageSize) { return; } this.printMaskLayer.updateSize(this.state.print.pageSize); this.renderMask(); } /** * Render the mask layer again. * @private */ renderMask() { if (this.state.print.maskVisible) { this.printMaskLayer.changed(); } } /** * Calculates the optimal scale for printing a map based on the map size, resolution, desired print size, and available map scales. * @param mapSize - The size of the map in meters. Expressed as an array [width, height]. * @param mapResolution - The resolution of the map in meters per pixel. * @param printMapSize - The desired size of the printed map in inches. Expressed as an array [width, height]. * @param mapScales - The available map scales. * @returns The optimal scale for printing the map or -1 if any. */ static getOptimalScale(mapSize, mapResolution, printMapSize, mapScales) { const mapWidth = mapSize[0] * mapResolution; const mapHeight = mapSize[1] * mapResolution; const scaleWidth = (mapWidth * GeoConsts.INCHES_PER_METER * GeoConsts.PRINT_DOTS_PER_INCH) / printMapSize[0]; const scaleHeight = (mapHeight * GeoConsts.INCHES_PER_METER * GeoConsts.PRINT_DOTS_PER_INCH) / printMapSize[1]; const scale = Math.min(scaleWidth, scaleHeight); return mapScales.reduce((optimal, currentScale) => { return scale > currentScale ? currentScale : optimal; }, -1); } /** * Calculates the optimal resolution for printing a map based on the provided parameters. * @param mapSize - The size of the map in meters. * @param printMapSize - The size of the map to be printed in dots. * @param scale - The scale at which the map should be printed. * @returns The optimal resolution for printing the map. */ static getOptimalResolution = (mapSize, printMapSize, scale) => { const dotsPerMeter = GeoConsts.PRINT_DOTS_PER_INCH * GeoConsts.INCHES_PER_METER; const resolutionX = (printMapSize[0] * scale) / (dotsPerMeter * mapSize[0]); const resolutionY = (printMapSize[1] * scale) / (dotsPerMeter * mapSize[1]); return Math.max(resolutionX, resolutionY); }; } export default PrintMaskManager;