diff --git a/web/client/actions/__tests__/print-test.js b/web/client/actions/__tests__/print-test.js index e71105efc0..b2fab7a1ad 100644 --- a/web/client/actions/__tests__/print-test.js +++ b/web/client/actions/__tests__/print-test.js @@ -88,6 +88,21 @@ describe('Test correctness of the print actions', () => { expect(retVal.projection).toBe('EPSG:4326'); expect(retVal.currentLocale).toBe('en-US'); }); + it('configurePrintMap with useFixedScales', () => { + const retVal = configurePrintMap({x: 1, y: 1}, 5, 6, 2.0, [], 'EPSG:4326', 'en-US', true); + expect(retVal).toExist(); + expect(retVal.type).toBe(CONFIGURE_PRINT_MAP); + expect(retVal.center).toExist(); + expect(retVal.center.x).toBe(1); + expect(retVal.zoom).toBe(5); + expect(retVal.scaleZoom).toBe(6); + expect(retVal.scale).toBe(2.0); + expect(retVal.layers).toExist(); + expect(retVal.layers.length).toBe(0); + expect(retVal.projection).toBe('EPSG:4326'); + expect(retVal.currentLocale).toBe('en-US'); + expect(retVal.useFixedScales).toBe(true); + }); it('changePrintZoomLevel', () => { const retVal = changePrintZoomLevel(5, 10000); diff --git a/web/client/actions/print.js b/web/client/actions/print.js index 6c54fbb25b..6467727342 100644 --- a/web/client/actions/print.js +++ b/web/client/actions/print.js @@ -132,7 +132,7 @@ export function printTransformerAdded(name) { }; } -export function configurePrintMap(center, zoom, scaleZoom, scale, layers, projection, currentLocale) { +export function configurePrintMap(center, zoom, scaleZoom, scale, layers, projection, currentLocale, useFixedScales) { return { type: CONFIGURE_PRINT_MAP, center, @@ -141,7 +141,8 @@ export function configurePrintMap(center, zoom, scaleZoom, scale, layers, projec scale, layers, projection, - currentLocale + currentLocale, + useFixedScales }; } diff --git a/web/client/plugins/Print.jsx b/web/client/plugins/Print.jsx index 937c75c5c4..42ced98c43 100644 --- a/web/client/plugins/Print.jsx +++ b/web/client/plugins/Print.jsx @@ -29,9 +29,9 @@ import { layersSelector } from '../selectors/layers'; import { currentLocaleSelector } from '../selectors/locale'; import { mapSelector, scalesSelector } from '../selectors/map'; import { mapTypeSelector } from '../selectors/maptype'; -import { normalizeSRS, reprojectBbox, convertDegreesToRadian } from '../utils/CoordinatesUtils'; +import { normalizeSRS, convertDegreesToRadian } from '../utils/CoordinatesUtils'; import { getMessageById } from '../utils/LocaleUtils'; -import { defaultGetZoomForExtent, getResolutions, mapUpdated, dpi2dpu, DEFAULT_SCREEN_DPI } from '../utils/MapUtils'; +import { defaultGetZoomForExtent, getResolutions, mapUpdated, dpi2dpu, DEFAULT_SCREEN_DPI, getScales, reprojectZoom } from '../utils/MapUtils'; import { isInsideResolutionsLimits } from '../utils/LayersUtils'; import { has, includes } from 'lodash'; import {additionalLayersSelector} from "../selectors/additionallayers"; @@ -289,7 +289,8 @@ export default { overrideOptions: PropTypes.object, items: PropTypes.array, addPrintParameter: PropTypes.func, - printingService: PropTypes.object + printingService: PropTypes.object, + printMap: PropTypes.object }; static contextTypes = { @@ -340,7 +341,8 @@ export default { currentLocale: 'en-US', overrideOptions: {}, items: [], - printingService: getDefaultPrintingService() + printingService: getDefaultPrintingService(), + printMap: {} }; state = { @@ -569,34 +571,30 @@ export default { const { map: newMap, capabilities, - minZoom, configurePrintMap: configurePrintMapProp, useFixedScales, - getZoomForExtent, - maxZoom, currentLocale, - scales: scalesProp, - layers + layers, + printMap, + printSpec } = props || this.props; if (newMap && newMap.bbox && capabilities) { - const bbox = reprojectBbox([ - newMap.bbox.bounds.minx, - newMap.bbox.bounds.miny, - newMap.bbox.bounds.maxx, - newMap.bbox.bounds.maxy - ], newMap.bbox.crs, newMap.projection); - const mapSize = this.getMapSize(); + const selectedPrintProjection = (printSpec && printSpec?.params?.projection) || (printSpec && printSpec?.projection) || (printMap && printMap.projection) || 'EPSG:3857'; + const printSrs = normalizeSRS(selectedPrintProjection); + const mapProjection = newMap.projection; + const mapSrs = normalizeSRS(mapProjection); + const zoom = reprojectZoom(newMap.zoom, mapSrs, printSrs); + const scales = getPrintScales(capabilities); + const printMapScales = getScales(printSrs); + const scaleZoom = getNearestZoom(zoom, scales, printMapScales); if (useFixedScales) { - const mapZoom = getZoomForExtent(bbox, mapSize, minZoom, maxZoom); - const scales = getPrintScales(capabilities); - const scaleZoom = getNearestZoom(newMap.zoom, scales); const scale = scales[scaleZoom]; - configurePrintMapProp(newMap.center, mapZoom, scaleZoom, scale, - layers, newMap.projection, currentLocale); + configurePrintMapProp(newMap.center, zoom, scaleZoom, scale, + layers, newMap.projection, currentLocale, useFixedScales); } else { - const scale = scalesProp[newMap.zoom]; - configurePrintMapProp(newMap.center, newMap.zoom, newMap.zoom, scale, - layers, newMap.projection, currentLocale); + const scale = printMapScales[zoom]; + configurePrintMapProp(newMap.center, zoom, scaleZoom, scale, + layers, newMap.projection, currentLocale, useFixedScales); } } }; @@ -629,8 +627,9 @@ export default { scalesSelector, (state) => state.browser && (!state.browser.ie || state.browser.ie11), currentLocaleSelector, - mapTypeSelector - ], (open, capabilities, printSpec, pdfUrl, error, map, layers, additionalLayers, scales, usePreview, currentLocale, mapType) => ({ + mapTypeSelector, + (state) => state.print.map + ], (open, capabilities, printSpec, pdfUrl, error, map, layers, additionalLayers, scales, usePreview, currentLocale, mapType, printMap) => ({ open, capabilities, printSpec, @@ -650,7 +649,8 @@ export default { scales, usePreview, currentLocale, - mapType + mapType, + printMap })); const PrintPlugin = connect(selector, { diff --git a/web/client/plugins/__tests__/Print-test.jsx b/web/client/plugins/__tests__/Print-test.jsx index c410cc1e8e..440c72a321 100644 --- a/web/client/plugins/__tests__/Print-test.jsx +++ b/web/client/plugins/__tests__/Print-test.jsx @@ -202,6 +202,88 @@ describe('Print Plugin', () => { }); }); + it('test configuration with useFixedScales and enableScalebox = true', (done) => { + const printingService = { + getMapConfiguration() { + return { + layers: [], + center: { + x: 0, + y: 0, + crs: "EPSG:4326" + } + }; + }, + validate() { return {};} + }; + getPrintPlugin({ + state: {...initialState, + print: {...initialState.print, + capabilities: {...initialState.print.capabilities, + scales: [1000000, 500000, 100000].map(value => ({name: value, value}))} + }} + }).then(async({ Plugin }) => { + try { + await ReactDOM.render(, document.getElementById("container")); + const comp = document.getElementById("container"); + await ReactTestUtils.act(async() => comp); + expect(comp).toExist(); + const scaleBoxComp = document.querySelector("#mappreview-scalebox select"); + expect(scaleBoxComp).toExist(); + done(); + } catch (ex) { + done(ex); + } + }); + }); + it('test configuration with useFixedScales and enableScalebox = false', (done) => { + const printingService = { + getMapConfiguration() { + return { + layers: [], + center: { + x: 0, + y: 0, + crs: "EPSG:4326" + } + }; + }, + validate() { return {};} + }; + getPrintPlugin({ + state: {...initialState, + print: {...initialState.print, + capabilities: {...initialState.print.capabilities, + scales: [1000000, 500000, 100000].map(value => ({name: value, value}))} + }} + }).then(async({ Plugin }) => { + try { + await ReactDOM.render(, document.getElementById("container")); + const comp = document.getElementById("container"); + await ReactTestUtils.act(async() => comp); + expect(comp).toExist(); + const scaleBoxComp = document.querySelector("#mappreview-scalebox select"); + expect(scaleBoxComp).toNotExist(); + done(); + } catch (ex) { + done(ex); + } + }); + }); it('default configuration with not allowed layers', (done) => { getPrintPlugin({ layers: [{visibility: true, type: "bing"}] diff --git a/web/client/plugins/__tests__/print/Projection-test.jsx b/web/client/plugins/__tests__/print/Projection-test.jsx index 18d83c751f..eb2959a2cb 100644 --- a/web/client/plugins/__tests__/print/Projection-test.jsx +++ b/web/client/plugins/__tests__/print/Projection-test.jsx @@ -24,7 +24,8 @@ const initialState = { map: { scale: 1784, scaleZoom: 2, - projection: "EPSG:3857" + projection: "EPSG:3857", + zoom: 3 }, capabilities: { createURL: "http://fakeservice", @@ -119,11 +120,12 @@ describe('PrintProjection Plugin', () => { }); it('map transformer with user chosen crs', (done) => { - getPrintProjectionPlugin().then(({ Plugin, store }) => { + getPrintProjectionPlugin().then(async({ Plugin, store }) => { try { - ReactDOM.render(, document.getElementById("container")); + const comp = ReactDOM.render(, document.getElementById("container")); + await ReactTestUtils.act(async() => comp); const combo = getByXPath("//select"); - ReactTestUtils.Simulate.change(combo, { + await ReactTestUtils.Simulate.change(combo, { target: { value: "EPSG:4326" } @@ -139,11 +141,12 @@ describe('PrintProjection Plugin', () => { }); it('validator without allowPreview', (done) => { - getPrintProjectionPlugin().then(({ Plugin, store }) => { + getPrintProjectionPlugin().then(async({ Plugin, store }) => { try { - ReactDOM.render(, document.getElementById("container")); + const comp = ReactDOM.render(, document.getElementById("container")); + await ReactTestUtils.act(async() => comp); const combo = getByXPath("//select"); - ReactTestUtils.Simulate.change(combo, { + await ReactTestUtils.Simulate.change(combo, { target: { value: "EPSG:4326" } diff --git a/web/client/plugins/print/Projection.jsx b/web/client/plugins/print/Projection.jsx index 1e5a3f3487..03370c90d2 100644 --- a/web/client/plugins/print/Projection.jsx +++ b/web/client/plugins/print/Projection.jsx @@ -8,7 +8,7 @@ import { setPrintParameter } from "../../actions/print"; import printReducer from "../../reducers/print"; import Choice from "../../components/print/Choice"; import { getMessageById } from '../../utils/LocaleUtils'; -import {getScales, reprojectZoom} from "../../utils/MapUtils"; +import {getScales} from "../../utils/MapUtils"; import { getAvailableCRS, normalizeSRS } from '../../utils/CoordinatesUtils'; @@ -16,21 +16,19 @@ export const projectionSelector = (state) => state?.print?.spec?.params?.project function mapTransformer(state, map) { const projection = projectionSelector(state); - const mapProjection = mapProjectionSelector(state); const srs = normalizeSRS(projection); + const scales = getScales(srs); + const mapProjection = mapProjectionSelector(state); const mapSrs = normalizeSRS(mapProjection); if (srs !== mapSrs) { - const zoom = reprojectZoom(map.scaleZoom, mapSrs, srs); - const scales = getScales(srs); return { ...map, - zoom: zoom, - scaleZoom: map.scaleZoom, // the selected value from scale DD - scale: scales[zoom], + scale: scales[map.zoom], + zoom: map.zoom >= 1 ? map.zoom - 1 : map.zoom, // for print map preview projection: srs }; } - return map; + return {...map, scale: scales[map.zoom], zoom: map.zoom >= 1 ? map.zoom - 1 : map.zoom}; } const validator = (allowPreview) => (state) => { @@ -67,15 +65,17 @@ export const Projection = ({ addValidator("projection", "map-preview", validator(allowPreview)); } }, [allowPreview]); + function changeProjection(crs) { + onChangeParameter("params.projection", crs); + onRefresh(); + } useEffect(() => { if (enabled) { + changeProjection(projection); addMapTransformer("projection", mapTransformer); } }, []); - function changeProjection(crs) { - onChangeParameter("params.projection", crs); - onRefresh(); - } + return enabled ? ( <> { expect(state.map.layers.length).toBe(0); expect(state.map.projection).toBe('EPSG:4326'); }); + it('configure print map with useFixedScales = true', () => { + const state = print({capabilities: {}, spec: {}}, { + type: CONFIGURE_PRINT_MAP, + center: {x: 1, y: 1}, + zoom: 5, + scaleZoom: 6, + scale: 10000, + layers: [], + projection: 'EPSG:4326', + useFixedScales: true + }); + expect(state.map).toExist(); + expect(state.map.center).toExist(); + expect(state.map.center.x).toBe(1); + expect(state.map.zoom).toBe(5); + expect(state.map.scale).toBe(10000); + expect(state.map.layers.length).toBe(0); + expect(state.map.projection).toBe('EPSG:4326'); + expect(state.map.useFixedScales).toBe(true); + }); it('configure print map title', () => { const state = print({capabilities: {}, spec: {}}, { diff --git a/web/client/reducers/print.js b/web/client/reducers/print.js index c00fa47ace..a904d391a5 100644 --- a/web/client/reducers/print.js +++ b/web/client/reducers/print.js @@ -97,7 +97,8 @@ function print(state = {spec: initialSpec, capabilities: null, map: null, isLoad scale: action.scale, layers, size: action.size ?? state.map?.size, - projection: action.projection + projection: action.projection, + useFixedScales: action.useFixedScales }, error: null } @@ -108,7 +109,7 @@ function print(state = {spec: initialSpec, capabilities: null, map: null, isLoad return assign({}, state, { map: assign({}, state.map, { scaleZoom: action.zoom, - zoom: state.map.zoom + diff, + zoom: state.map.zoom + diff >= 0 ? state.map.zoom + diff : 0, scale: action.scale }) } diff --git a/web/client/utils/PrintUtils.js b/web/client/utils/PrintUtils.js index 0d0d9d10bb..ad56a0722d 100644 --- a/web/client/utils/PrintUtils.js +++ b/web/client/utils/PrintUtils.js @@ -9,7 +9,7 @@ import { reproject, getUnits, reprojectGeoJson, normalizeSRS } from './CoordinatesUtils'; import {addAuthenticationParameter} from './SecurityUtils'; -import { calculateExtent, getGoogleMercatorScales, getResolutionsForProjection, getScales, reprojectZoom } from './MapUtils'; +import { calculateExtent, getGoogleMercatorScales, getResolutionsForProjection, getScales } from './MapUtils'; import { optionsToVendorParams } from './VendorParamsUtils'; import { colorToHexStr } from './ColorUtils'; import { getLayerConfig } from './TileConfigProvider'; @@ -236,9 +236,10 @@ export const mapProjectionSelector = (state) => state?.print?.map?.projection ?? export const getMapfishPrintSpecification = (rawSpec, state) => { const {params, ...baseSpec} = rawSpec; const spec = {...baseSpec, ...params}; - const mapProjection = mapProjectionSelector(state); + const printMap = state?.print?.map; const projectedCenter = reproject(spec.center, 'EPSG:4326', spec.projection); - const projectedZoom = Math.round(reprojectZoom(spec.scaleZoom, mapProjection, spec.projection)); + // * use [spec.zoom] the actual zoom in case useFixedScale = false else use [spec.scaleZoom] the fixed zoom scale not actual + const projectedZoom = Math.round(printMap?.useFixedScales ? spec.scaleZoom : spec.zoom); const scales = spec.scales || getScales(spec.projection); const reprojectedScale = scales[projectedZoom] || defaultScales[projectedZoom]; diff --git a/web/client/utils/__tests__/PrintUtils-test.js b/web/client/utils/__tests__/PrintUtils-test.js index ad9d53d65d..b43b0ca26a 100644 --- a/web/client/utils/__tests__/PrintUtils-test.js +++ b/web/client/utils/__tests__/PrintUtils-test.js @@ -34,7 +34,7 @@ import { KVP1, REST1 } from '../../test-resources/layers/wmts'; import { poi as TMS110_1 } from '../../test-resources/layers/tms'; import { BasemapAT, NASAGIBS, NLS_CUSTOM_URL, LINZ_CUSTOM_URL } from '../../test-resources/layers/tileprovider'; import { setStore } from '../StateUtils'; -import { getGoogleMercatorScales } from '../MapUtils'; +import { getGoogleMercatorScales, getScales } from '../MapUtils'; const layer = { url: "http://mygeoserver", @@ -554,18 +554,51 @@ describe('PrintUtils', () => { ...testSpec, scaleZoom: 3, scales: [2000000, 1000000, 500000, 100000, 50000] + }, { + print: { + map: { + useFixedScales: true + } + } }); expect(printSpec).toExist(); expect(printSpec.pages[0].scale).toBe(100000); }); - it('getMapfishPrintSpecification with standard scales', () => { + it('getMapfishPrintSpecification with standard scales for print map with projection 3857 [google web mercator]', () => { const printSpec = getMapfishPrintSpecification({ ...testSpec, - scaleZoom: 3 + zoom: 3 }); expect(printSpec).toExist(); expect(printSpec.pages[0].scale).toBe(getGoogleMercatorScales(0, 21)[3]); }); + it('getMapfishPrintSpecification with fixed scales for print map with projection 4326', () => { + const projection = 'EPSG:4326'; + const printSpec = getMapfishPrintSpecification({ + ...testSpec, + projection, + scaleZoom: 3, + scales: [2000000, 1000000, 500000, 100000, 50000] + }, { + print: { + map: { + useFixedScales: true + } + } + }); + expect(printSpec).toExist(); + expect(printSpec.pages[0].scale).toBe(100000); + }); + it('getMapfishPrintSpecification with standard scales for print map with projection 4326', () => { + const projection = 'EPSG:4326'; + const printSpec = getMapfishPrintSpecification({ + ...testSpec, + zoom: 3, + projection + }); + expect(printSpec).toExist(); + expect(printSpec.pages[0].scale).toBe(getScales(projection)[3]); + }); it('from rgba to rgb', () => { const rgb = rgbaTorgb("rgba(255, 255, 255, 0.1)"); expect(rgb).toExist();