From eb9b6e9f20f06b15e8e616cbbc8d20162b9e1c50 Mon Sep 17 00:00:00 2001 From: Lorenzo Natali Date: Tue, 9 May 2017 17:02:35 +0200 Subject: [PATCH] Print support for relative URLs (#1797) Actually print plugin fails when you have a WMS like "/geoserver". This fixes this behavior using the origin of the browser. --- docma-config.json | 1 + web/client/utils/PrintUtils.js | 76 ++++++++++++++++++- web/client/utils/__tests__/PrintUtils-test.js | 72 +++++++++++++++++- 3 files changed, 145 insertions(+), 4 deletions(-) diff --git a/docma-config.json b/docma-config.json index 3044ab3b48..4bc183313d 100644 --- a/docma-config.json +++ b/docma-config.json @@ -136,6 +136,7 @@ "web/client/utils/index.jsdoc", "web/client/utils/CoordinatesUtils.js", "web/client/utils/PluginsUtils.js", + "web/client/utils/PrintUtils.js", "web/client/utils/ShareUtils.js" ], "jsapi": "web/client/jsapi/MapStore2.js", diff --git a/web/client/utils/PrintUtils.js b/web/client/utils/PrintUtils.js index 4ae3ac64a7..64e213841c 100644 --- a/web/client/utils/PrintUtils.js +++ b/web/client/utils/PrintUtils.js @@ -1,4 +1,4 @@ -/** +/* * Copyright 2016, GeoSolutions Sas. * All rights reserved. * @@ -21,15 +21,55 @@ const assign = require('object-assign'); const getGeomType = function(layer) { return (layer.features && layer.features[0]) ? layer.features[0].geometry.type : undefined; }; - +/** + * Utilities for Print + * @memberof utils + */ const PrintUtils = { + /** + * Given a static resource, returns the resource's absolute + * URL. Supports file paths with or without origin/protocol. + * @param {string} uri the uri to transform + * @param {string} [origin=window.location.origin] the origin to use + */ + toAbsoluteURL: (uri, origin) => { + // Handle absolute URLs (with protocol-relative prefix) + // Example: //domain.com/file.png + if (uri.search(/^\/\//) !== -1) { + return window.location.protocol + uri; + } + + // Handle absolute URLs (with explicit origin) + // Example: http://domain.com/file.png + if (uri.search(/:\/\//) !== -1) { + return uri; + } + + // Handle absolute URLs (without explicit origin) + // Example: /file.png + if (uri.search(/^\//) !== -1) { + return (origin || window.location.origin) + uri; + } + }, + /** + * Tranform the original URL configuration of the layer into a URL + * usable for the print service. + * @param {string|array} input Original URL + * @return {string} the URL modified as GeoServer requires + */ normalizeUrl: (input) => { let result = isArray(input) ? input[0] : input; if (result.indexOf('?') !== -1) { result = result.substring(0, result.indexOf('?')); } - return result; + return PrintUtils.toAbsoluteURL(result); }, + /** + * Find the layout name for the given options. + * The convention is: `PAGE_FORMAT + ("_2_pages_legend"|"_2_pages_legend"|"") + ("_landscape"|"")`` + * @param {object} spec the spec with the options + * @return {string} the layout name. + */ getLayoutName: (spec) => { let layoutName = [spec.sheet]; if (spec.includeLegend) { @@ -44,15 +84,33 @@ const PrintUtils = { } return layoutName.join('_'); }, + /** + * Gets the print scales allowed from the capabilities of the print service. + * @param {capabilities} capabilities the capabilities of the print service + * @return {array} the scales array + */ getPrintScales: (capabilities) => { return capabilities.scales.slice(0).reverse().map((scale) => parseFloat(scale.value)) || []; }, + /** + * Guest the nearest zoom level in the allowed scales + * @param {number} zoom the zoom level + * @param {array} scales the allowed scales + * @param {array} [mapScales=defaultScales] the map scales + * @return {number} the index that best approximates the current map scale + */ getNearestZoom: (zoom, scales, mapScales = defaultScales) => { const mapScale = mapScales[zoom]; return scales.reduce((previous, current, index) => { return current < mapScale ? previous : index; }, 0); }, + /** + * Get the mapSize for print preview, parsing the layout and limiting the width. + * @param {object} layout the layout object + * @param {number} maxWidth the max width for the mapSize + * @return {object} width and height of a map limited by the maxWidth and with the same ratio of the layout + */ getMapSize: (layout, maxWidth) => { if (layout) { const width = layout.rotation ? layout.map.height : layout.map.width; @@ -67,6 +125,11 @@ const PrintUtils = { height: 100 }; }, + /** + * Creates the mapfish print specification from the current configuration + * @param {object} spec the current configuration + * @return {object} the mapfish print configuration to send to the server + */ getMapfishPrintSpecification: (spec) => { const projectedCenter = CoordinatesUtils.reproject(spec.center, 'EPSG:4326', spec.projection); return { @@ -92,6 +155,13 @@ const PrintUtils = { "legends": PrintUtils.getMapfishLayersSpecification(spec.layers, spec, 'legend') }; }, + /** + * Generate the layers (or legend) specification for print. + * @param {array} layers the layers configurations + * @param {spec} spec the print configurations + * @param {string} purpose allowed values: `map|legend`. Tells which spec to generate. + * @return {array} the configuration array for layers (or legend) to send to the print service. + */ getMapfishLayersSpecification: (layers, spec, purpose) => { return layers.filter((layer) => PrintUtils.specCreators[layer.type] && PrintUtils.specCreators[layer.type][purpose]) .map((layer) => PrintUtils.specCreators[layer.type][purpose](layer, spec)); diff --git a/web/client/utils/__tests__/PrintUtils-test.js b/web/client/utils/__tests__/PrintUtils-test.js index 6b070e44db..42f7acdec4 100644 --- a/web/client/utils/__tests__/PrintUtils-test.js +++ b/web/client/utils/__tests__/PrintUtils-test.js @@ -1,4 +1,4 @@ -/** +/* * Copyright 2016, GeoSolutions Sas. * All rights reserved. * @@ -83,6 +83,50 @@ const mapFishVectorLayer = { ] } }; + +const testSpec = { + "antiAliasing": true, + "iconSize": 24, + "legendDpi": 96, + "fontFamily": "Verdana", + "fontSize": 8, + "bold": false, + "italic": false, + "resolution": "96", + "name": "", + "description": "", + "sheet": "A2", + "includeLegend": true, + "twoPages": true, + "center": { + "x": 8.930511, + "y": 44.417107, + "crs": "EPSG:4326" + }, + "zoom": 11, + "scaleZoom": 3, + "scale": 50000, + "layers": [ + { + "group": "background", + "source": "osm", + "name": "mapnik", + "title": "Open Street Map", + "type": "osm", + "visibility": true, + "singleTile": false, + "dimensions": [], + "id": "mapnik__0", + "loading": false, + "loadingError": false + } + ], + "projection": "EPSG:900913", + "size": { + "height": 462, + "width": 368 + } +}; describe('PrintUtils', () => { it('custom params are applied to wms layers', () => { @@ -99,6 +143,11 @@ describe('PrintUtils', () => { expect(specs.length).toBe(1); expect(specs[0].geoJson.features[0].geometry.coordinates[0], mapFishVectorLayer).toBe(mapFishVectorLayer.geoJson.features[0].geometry.coordinates[0]); }); + it('vector layer generation for legend', () => { + const specs = PrintUtils.getMapfishLayersSpecification([layer], {projection: "EPSG:3857"}, 'legend'); + expect(specs).toExist(); + expect(specs.length).toBe(1); + }); it('vector layer default point style', () => { const style = PrintUtils.getOlDefaultStyle({features: [{geometry: {type: "Point"}}]}); expect(style).toExist(); @@ -120,4 +169,25 @@ describe('PrintUtils', () => { expect(style).toExist(); expect(style.strokeWidth).toBe(3); }); + it('toAbsoluteUrl', () => { + const url = PrintUtils.toAbsoluteURL("/geoserver", "http://localhost:8080"); + expect(url).toExist(); + expect(url).toBe("http://localhost:8080/geoserver"); + expect(PrintUtils.toAbsoluteURL("//someurl/geoserver").indexOf("http")).toBe(0); + }); + it('getMapSize', () => { + expect(PrintUtils.getMapSize()).toExist(); // check defaults + expect(PrintUtils.getMapSize({map: {width: 200, height: 200}}, 150).height).toBe(150); + expect(PrintUtils.getMapSize({rotation: true, map: {width: 200, height: 100}}, 200).height).toBe(400); + }); + it('getNearestZoom', () => { + const scales = [1000, 1000, 1000000, 10000000]; + expect(PrintUtils.getNearestZoom(0, scales)).toBe(0); + }); + it('getMapfishPrintSpecification', () => { + const printSpec = PrintUtils.getMapfishPrintSpecification(testSpec); + expect(printSpec).toExist(); + expect(printSpec.dpi).toBe(96); + expect(printSpec.layers.length).toBe(1); + }); });