From 3a5c8d637cc3069987b061f67c91108bd8feaf78 Mon Sep 17 00:00:00 2001 From: Vincent Lecrubier Date: Tue, 21 Jan 2020 20:50:33 +0000 Subject: [PATCH 1/3] Zoomify: Separate the service pixel ratio and the device pixel ratio --- examples/zoomify.html | 1 - examples/zoomify.js | 103 +++++++++------------------------------ src/ol/source/Zoomify.js | 6 ++- 3 files changed, 27 insertions(+), 83 deletions(-) diff --git a/examples/zoomify.html b/examples/zoomify.html index d5b18997ca..fb7503d075 100644 --- a/examples/zoomify.html +++ b/examples/zoomify.html @@ -10,7 +10,6 @@ tags: "zoomify, deep zoom, IIP, pixel, projection"
diff --git a/examples/zoomify.js b/examples/zoomify.js index 19c469dfdf..0da330b013 100644 --- a/examples/zoomify.js +++ b/examples/zoomify.js @@ -3,17 +3,13 @@ import View from '../src/ol/View.js'; import TileLayer from '../src/ol/layer/Tile.js'; import Zoomify from '../src/ol/source/Zoomify.js'; -const imgWidth = 9911; -const imgHeight = 6100; +const imgWidth = 4000; +const imgHeight = 3000; -const zoomifyUrl = 'http://vips.vtech.fr/cgi-bin/iipsrv.fcgi?zoomify=' + - '/mnt/MD1/AD00/plan_CHU-4HD-01/FOND.TIF/'; -const iipUrl = 'http://vips.vtech.fr/cgi-bin/iipsrv.fcgi?FIF=' + '/mnt/MD1/AD00/plan_CHU-4HD-01/FOND.TIF' + '&JTL={z},{tileIndex}'; +const zoomifyUrl = 'https://ol-zoomify.surge.sh/zoomify/'; const layer = new TileLayer({ source: new Zoomify({ - tileSize: 256, - tilePixelRatio: 1, url: zoomifyUrl, size: [imgWidth, imgHeight], crossOrigin: 'anonymous' @@ -22,17 +18,15 @@ const layer = new TileLayer({ const extent = [0, -imgHeight, imgWidth, 0]; -const resolutions = layer.getSource().getTileGrid().getResolutions(); - const map = new Map({ layers: [layer], target: 'map', view: new View({ // adjust zoom levels to those provided by the source - minResolution: resolutions[resolutions.length - 1], - maxResolution: resolutions[0], + resolutions: layer.getSource().getTileGrid().getResolutions(), // constrain the center: center cannot be set outside this extent - extent: extent + extent: extent, + constrainOnlyCenter: true }) }); map.getView().fit(extent); @@ -40,74 +34,23 @@ map.getView().fit(extent); const control = document.getElementById('zoomifyProtocol'); control.addEventListener('change', function(event) { const value = event.currentTarget.value; - if (value === 'iip') { - const extent = [0, -imgHeight, imgWidth, 0]; - layer.setSource( - new Zoomify({ - tileSize: 256, - tilePixelRatio: 1, - url: iipUrl, - size: [imgWidth, imgHeight], - crossOrigin: 'anonymous' - }) - ); - const resolutions = layer.getSource().getTileGrid().getResolutions(); - map.setView( - new View({ - // adjust zoom levels to those provided by the source - minResolution: resolutions[resolutions.length - 1], - maxResolution: resolutions[0], - // constrain the center: center cannot be set outside this extent - extent: extent - }) - ); - map.getView().fit(extent); - } else if (value === 'zoomify') { - const extent = [0, -imgHeight, imgWidth, 0]; - layer.setSource( - new Zoomify({ - tileSize: 256, - tilePixelRatio: 1, - url: zoomifyUrl, - size: [imgWidth, imgHeight], - crossOrigin: 'anonymous' - }) - ); - const resolutions = layer.getSource().getTileGrid().getResolutions(); - map.setView( - new View({ - // adjust zoom levels to those provided by the source - minResolution: resolutions[resolutions.length - 1], - maxResolution: resolutions[0], - // constrain the center: center cannot be set outside this extent - extent: extent - }) - ); - map.getView().fit(extent); + if (value === 'zoomify') { + layer.setSource(new Zoomify({ + url: zoomifyUrl, + size: [imgWidth, imgHeight], + crossOrigin: 'anonymous' + })); } else if (value === 'zoomifyretina') { - const pixelRatio = 4; - // Be careful! Image extent will be modified by pixel ratio - const extent = [0, -imgHeight / pixelRatio, imgWidth / pixelRatio, 0]; - layer.setSource( - new Zoomify({ - tileSize: 256 / pixelRatio, - tilePixelRatio: pixelRatio, - url: zoomifyUrl, - size: [imgWidth / pixelRatio, imgHeight / pixelRatio], - crossOrigin: 'anonymous' - }) - ); - const resolutions = layer.getSource().getTileGrid().getResolutions(); - map.setView( - new View({ - // adjust zoom levels to those provided by the source - minResolution: resolutions[resolutions.length - 1] / pixelRatio, - maxResolution: resolutions[0], - // constrain the center: center cannot be set outside this extent - extent: extent - }) - ); - map.getView().fit(extent); + layer.setSource(new Zoomify({ + tileSize: 256, // The tile size is 256px on the server, this is the default value + tilePixelRatio: 2, // We want to display this on a retina screen + tilePixelRatioOriginal: 1, // But the server serves 256px tiles, not 512px tiles that would be needed nominally. + zDirection: -1, //Ensure we get the most precise tile in any case + url: zoomifyUrl, + size: [imgWidth, imgHeight], + crossOrigin: 'anonymous' + })); } - }); + + diff --git a/src/ol/source/Zoomify.js b/src/ol/source/Zoomify.js index 5a5bd2efa1..cc2ec00c34 100644 --- a/src/ol/source/Zoomify.js +++ b/src/ol/source/Zoomify.js @@ -90,7 +90,8 @@ export class CustomTile extends ImageTile { * you must provide a `crossOrigin` value you want to access pixel data with the Canvas renderer. * See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail. * @property {import("../proj.js").ProjectionLike} [projection] Projection. - * @property {number} [tilePixelRatio] The pixel ratio used by the tile service. For example, if the tile service advertizes 256px by 256px tiles but actually sends 512px by 512px images (for retina/hidpi devices) then `tilePixelRatio` should be set to `2` + * @property {number} [tilePixelRatio] The pixel ratio to display on screen. If on a retina screen then `tilePixelRatio` should be 2, else it should be 1. + * @property {number} [tilePixelRatioOriginal] The pixel ratio used by the tile service. For example, if the tile service advertizes 256px by 256px tiles but actually sends 512px by 512px images (for retina/hidpi devices) then `tilePixelRatioOriginal` should be set to `2` * @property {number} [reprojectionErrorThreshold=0.5] Maximum allowed reprojection error (in pixels). * Higher values can increase reprojection performance, but decrease precision. * @property {string} [url] URL template or base URL of the Zoomify service. @@ -147,6 +148,7 @@ class Zoomify extends TileImage { const tierSizeInTiles = []; const tileSize = options.tileSize || DEFAULT_TILE_SIZE; const tilePixelRatio = options.tilePixelRatio || 1; + const tilePixelRatioOriginal = options.tilePixelRatioOriginal || tilePixelRatio; let tileSizeForTierSizeCalculation = tileSize; switch (tierSizeCalculation) { @@ -246,7 +248,7 @@ class Zoomify extends TileImage { const tileUrlFunction = createFromTileUrlFunctions(urls.map(createFromTemplate)); - const ZoomifyTileClass = CustomTile.bind(null, tilePixelRatio, tileGrid); + const ZoomifyTileClass = CustomTile.bind(null, tilePixelRatioOriginal, tileGrid); super({ attributions: options.attributions, From 8c89ddceca3712780fee996aa2dbd3589d859118 Mon Sep 17 00:00:00 2001 From: Vincent Lecrubier Date: Fri, 24 Jan 2020 15:33:42 +0000 Subject: [PATCH 2/3] Revert #9489 and solves retina tiles on zoomify apparently. --- examples/zoomify.js | 11 +++++------ src/ol/source/Zoomify.js | 26 ++++++++++---------------- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/examples/zoomify.js b/examples/zoomify.js index 0da330b013..047dbd1146 100644 --- a/examples/zoomify.js +++ b/examples/zoomify.js @@ -38,17 +38,16 @@ control.addEventListener('change', function(event) { layer.setSource(new Zoomify({ url: zoomifyUrl, size: [imgWidth, imgHeight], - crossOrigin: 'anonymous' + crossOrigin: 'anonymous', + zDirection: -1 // Ensure we get the most precise tile in any case })); } else if (value === 'zoomifyretina') { layer.setSource(new Zoomify({ - tileSize: 256, // The tile size is 256px on the server, this is the default value - tilePixelRatio: 2, // We want to display this on a retina screen - tilePixelRatioOriginal: 1, // But the server serves 256px tiles, not 512px tiles that would be needed nominally. - zDirection: -1, //Ensure we get the most precise tile in any case url: zoomifyUrl, size: [imgWidth, imgHeight], - crossOrigin: 'anonymous' + crossOrigin: 'anonymous', + zDirection: -1, // Ensure we get the most precise tile in any case + tilePixelRatio: 2 // Display retina tiles })); } }); diff --git a/src/ol/source/Zoomify.js b/src/ol/source/Zoomify.js index cc2ec00c34..e42ebaddeb 100644 --- a/src/ol/source/Zoomify.js +++ b/src/ol/source/Zoomify.js @@ -26,7 +26,6 @@ const TierSizeCalculation = { export class CustomTile extends ImageTile { /** - * @param {number} tilePixelRatio Tile pixel ratio to display the tile * @param {import("../tilegrid/TileGrid.js").default} tileGrid TileGrid that the tile belongs to. * @param {import("../tilecoord.js").TileCoord} tileCoord Tile coordinate. * @param {TileState} state State. @@ -35,7 +34,8 @@ export class CustomTile extends ImageTile { * @param {import("../Tile.js").LoadFunction} tileLoadFunction Tile load function. * @param {import("../Tile.js").Options=} opt_options Tile options. */ - constructor(tilePixelRatio, tileGrid, tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options) { + constructor(tileGrid, tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options) { + super(tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options); /** @@ -48,11 +48,8 @@ export class CustomTile extends ImageTile { * @private * @type {import("../size.js").Size} */ - this.tileSize_ = toSize(tileGrid.getTileSize(tileCoord[0])).map( - function(x) { - return x * tilePixelRatio; - } - ); + this.tileSize_ = toSize(tileGrid.getTileSize(tileCoord[0])); + } /** @@ -90,8 +87,7 @@ export class CustomTile extends ImageTile { * you must provide a `crossOrigin` value you want to access pixel data with the Canvas renderer. * See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail. * @property {import("../proj.js").ProjectionLike} [projection] Projection. - * @property {number} [tilePixelRatio] The pixel ratio to display on screen. If on a retina screen then `tilePixelRatio` should be 2, else it should be 1. - * @property {number} [tilePixelRatioOriginal] The pixel ratio used by the tile service. For example, if the tile service advertizes 256px by 256px tiles but actually sends 512px by 512px images (for retina/hidpi devices) then `tilePixelRatioOriginal` should be set to `2` + * @property {number} [tilePixelRatio] The pixel ratio used by the tile service. For example, if the tile service advertizes 256px by 256px tiles but actually sends 512px by 512px images (for retina/hidpi devices) then `tilePixelRatio` should be set to `2` * @property {number} [reprojectionErrorThreshold=0.5] Maximum allowed reprojection error (in pixels). * Higher values can increase reprojection performance, but decrease precision. * @property {string} [url] URL template or base URL of the Zoomify service. @@ -115,8 +111,8 @@ export class CustomTile extends ImageTile { * @property {number} [transition] Duration of the opacity transition for rendering. * To disable the opacity transition, pass `transition: 0`. * @property {number} [tileSize=256] Tile size. Same tile size is used for all zoom levels. - * @property {number} [zDirection=0] Indicate which resolution should be used - * by a renderer if the view resolution does not match any resolution of the tile source. + * @property {number} [zDirection] Indicate which resolution should be used + * by a renderer if the views resolution does not match any resolution of the tile source. * If 0, the nearest resolution will be used. If 1, the nearest lower resolution * will be used. If -1, the nearest higher resolution will be used. */ @@ -147,8 +143,6 @@ class Zoomify extends TileImage { const extent = options.extent || [0, -size[1], size[0], 0]; const tierSizeInTiles = []; const tileSize = options.tileSize || DEFAULT_TILE_SIZE; - const tilePixelRatio = options.tilePixelRatio || 1; - const tilePixelRatioOriginal = options.tilePixelRatioOriginal || tilePixelRatio; let tileSizeForTierSizeCalculation = tileSize; switch (tierSizeCalculation) { @@ -248,14 +242,14 @@ class Zoomify extends TileImage { const tileUrlFunction = createFromTileUrlFunctions(urls.map(createFromTemplate)); - const ZoomifyTileClass = CustomTile.bind(null, tilePixelRatioOriginal, tileGrid); + const ZoomifyTileClass = CustomTile.bind(null, tileGrid); super({ attributions: options.attributions, cacheSize: options.cacheSize, crossOrigin: options.crossOrigin, projection: options.projection, - tilePixelRatio: tilePixelRatio, + tilePixelRatio: options.tilePixelRatio, reprojectionErrorThreshold: options.reprojectionErrorThreshold, tileClass: ZoomifyTileClass, tileGrid: tileGrid, @@ -272,4 +266,4 @@ class Zoomify extends TileImage { } -export default Zoomify; +export default Zoomify; \ No newline at end of file From 23d441f9f27469b1bac9ec1aaf8e62ea2e885c3f Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Sat, 25 Jan 2020 14:24:12 +0100 Subject: [PATCH 3/3] Handle tileSize and tilePixelRatio properly --- examples/zoomify.js | 41 ++++++++++++++++++++-------------------- src/ol/source/IIIF.js | 7 +++++-- src/ol/source/Zoomify.js | 29 +++++++++++++--------------- 3 files changed, 39 insertions(+), 38 deletions(-) diff --git a/examples/zoomify.js b/examples/zoomify.js index 047dbd1146..47a32ae0db 100644 --- a/examples/zoomify.js +++ b/examples/zoomify.js @@ -8,15 +8,27 @@ const imgHeight = 3000; const zoomifyUrl = 'https://ol-zoomify.surge.sh/zoomify/'; -const layer = new TileLayer({ - source: new Zoomify({ - url: zoomifyUrl, - size: [imgWidth, imgHeight], - crossOrigin: 'anonymous' - }) +const source = new Zoomify({ + url: zoomifyUrl, + size: [imgWidth, imgHeight], + crossOrigin: 'anonymous', + zDirection: -1 // Ensure we get a tile with the screen resolution or higher +}); +const extent = source.getTileGrid().getExtent(); + +const retinaPixelRatio = 2; +const retinaSource = new Zoomify({ + url: zoomifyUrl, + size: [imgWidth, imgHeight], + crossOrigin: 'anonymous', + zDirection: -1, // Ensure we get a tile with the screen resolution or higher + tilePixelRatio: retinaPixelRatio, // Display retina tiles + tileSize: 256 / retinaPixelRatio // from a higher zoom level }); -const extent = [0, -imgHeight, imgWidth, 0]; +const layer = new TileLayer({ + source: source +}); const map = new Map({ layers: [layer], @@ -35,20 +47,9 @@ const control = document.getElementById('zoomifyProtocol'); control.addEventListener('change', function(event) { const value = event.currentTarget.value; if (value === 'zoomify') { - layer.setSource(new Zoomify({ - url: zoomifyUrl, - size: [imgWidth, imgHeight], - crossOrigin: 'anonymous', - zDirection: -1 // Ensure we get the most precise tile in any case - })); + layer.setSource(source); } else if (value === 'zoomifyretina') { - layer.setSource(new Zoomify({ - url: zoomifyUrl, - size: [imgWidth, imgHeight], - crossOrigin: 'anonymous', - zDirection: -1, // Ensure we get the most precise tile in any case - tilePixelRatio: 2 // Display retina tiles - })); + layer.setSource(retinaSource); } }); diff --git a/src/ol/source/IIIF.js b/src/ol/source/IIIF.js index 2fb4014673..8370a936f3 100644 --- a/src/ol/source/IIIF.js +++ b/src/ol/source/IIIF.js @@ -9,6 +9,7 @@ import {Versions} from '../format/IIIFInfo.js'; import {assert} from '../asserts.js'; import TileGrid from '../tilegrid/TileGrid.js'; import TileImage from './TileImage.js'; +import {toSize} from '../size.js'; /** * @typedef {Object} Options @@ -87,7 +88,7 @@ class IIIF extends TileImage { const extent = options.extent || [0, -height, width, 0]; const supportsListedSizes = sizes != undefined && Array.isArray(sizes) && sizes.length > 0; - const supportsListedTiles = tileSize != undefined && (typeof tileSize === 'number' && Number.isInteger(tileSize) && tileSize > 0 || Array.isArray(tileSize) && tileSize.length > 0); + const supportsListedTiles = tileSize !== undefined && (typeof tileSize === 'number' && Number.isInteger(tileSize) && tileSize > 0 || Array.isArray(tileSize) && tileSize.length > 0); const supportsArbitraryTiling = supports != undefined && Array.isArray(supports) && (supports.includes('regionByPx') || supports.includes('regionByPct')) && (supports.includes('sizeByWh') || supports.includes('sizeByH') || @@ -265,7 +266,9 @@ class IIIF extends TileImage { return baseUrl + regionParam + '/' + sizeParam + '/0/' + quality + '.' + format; }; - const IiifTileClass = CustomTile.bind(null, tilePixelRatio, tileGrid); + const IiifTileClass = CustomTile.bind(null, toSize(tileSize || 256).map(function(size) { + return size * tilePixelRatio; + })); super({ attributions: options.attributions, diff --git a/src/ol/source/Zoomify.js b/src/ol/source/Zoomify.js index e42ebaddeb..509a7e0ae2 100644 --- a/src/ol/source/Zoomify.js +++ b/src/ol/source/Zoomify.js @@ -8,7 +8,6 @@ import TileState from '../TileState.js'; import {expandUrl, createFromTileUrlFunctions} from '../tileurlfunction.js'; import {assert} from '../asserts.js'; import {createCanvasContext2D} from '../dom.js'; -import {getTopLeft} from '../extent.js'; import {toSize} from '../size.js'; import TileImage from './TileImage.js'; import TileGrid from '../tilegrid/TileGrid.js'; @@ -26,7 +25,7 @@ const TierSizeCalculation = { export class CustomTile extends ImageTile { /** - * @param {import("../tilegrid/TileGrid.js").default} tileGrid TileGrid that the tile belongs to. + * @param {import("../size.js").Size} tileSize Full tile size. * @param {import("../tilecoord.js").TileCoord} tileCoord Tile coordinate. * @param {TileState} state State. * @param {string} src Image source URI. @@ -34,7 +33,7 @@ export class CustomTile extends ImageTile { * @param {import("../Tile.js").LoadFunction} tileLoadFunction Tile load function. * @param {import("../Tile.js").Options=} opt_options Tile options. */ - constructor(tileGrid, tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options) { + constructor(tileSize, tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options) { super(tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options); @@ -45,10 +44,9 @@ export class CustomTile extends ImageTile { this.zoomifyImage_ = null; /** - * @private * @type {import("../size.js").Size} */ - this.tileSize_ = toSize(tileGrid.getTileSize(tileCoord[0])); + this.tileSize_ = tileSize; } @@ -138,12 +136,12 @@ class Zoomify extends TileImage { options.tierSizeCalculation : TierSizeCalculation.DEFAULT; + const tilePixelRatio = options.tilePixelRatio || 1; const imageWidth = size[0]; const imageHeight = size[1]; - const extent = options.extent || [0, -size[1], size[0], 0]; const tierSizeInTiles = []; const tileSize = options.tileSize || DEFAULT_TILE_SIZE; - let tileSizeForTierSizeCalculation = tileSize; + let tileSizeForTierSizeCalculation = tileSize * tilePixelRatio; switch (tierSizeCalculation) { case TierSizeCalculation.DEFAULT: @@ -175,10 +173,10 @@ class Zoomify extends TileImage { tierSizeInTiles.push([1, 1]); tierSizeInTiles.reverse(); - const resolutions = [1]; + const resolutions = [tilePixelRatio]; const tileCountUpToTier = [0]; for (let i = 1, ii = tierSizeInTiles.length; i < ii; i++) { - resolutions.push(1 << i); + resolutions.push(tilePixelRatio << i); tileCountUpToTier.push( tierSizeInTiles[i - 1][0] * tierSizeInTiles[i - 1][1] + tileCountUpToTier[i - 1] @@ -188,8 +186,7 @@ class Zoomify extends TileImage { const tileGrid = new TileGrid({ tileSize: tileSize, - extent: extent, - origin: getTopLeft(extent), + extent: options.extent || [0, -imageHeight, imageWidth, 0], resolutions: resolutions }); @@ -199,6 +196,8 @@ class Zoomify extends TileImage { } const urls = expandUrl(url); + const tileWidth = tileSize * tilePixelRatio; + /** * @param {string} template Template. * @return {import("../Tile.js").UrlFunction} Tile URL function. @@ -222,8 +221,6 @@ class Zoomify extends TileImage { const tileIndex = tileCoordX + tileCoordY * tierSizeInTiles[tileCoordZ][0]; - const tileSize = tileGrid.getTileSize(tileCoordZ); - const tileWidth = Array.isArray(tileSize) ? tileSize[0] : tileSize; const tileGroup = ((tileIndex + tileCountUpToTier[tileCoordZ]) / tileWidth) | 0; const localContext = { 'z': tileCoordZ, @@ -242,14 +239,14 @@ class Zoomify extends TileImage { const tileUrlFunction = createFromTileUrlFunctions(urls.map(createFromTemplate)); - const ZoomifyTileClass = CustomTile.bind(null, tileGrid); + const ZoomifyTileClass = CustomTile.bind(null, toSize(tileSize * tilePixelRatio)); super({ attributions: options.attributions, cacheSize: options.cacheSize, crossOrigin: options.crossOrigin, projection: options.projection, - tilePixelRatio: options.tilePixelRatio, + tilePixelRatio: tilePixelRatio, reprojectionErrorThreshold: options.reprojectionErrorThreshold, tileClass: ZoomifyTileClass, tileGrid: tileGrid, @@ -266,4 +263,4 @@ class Zoomify extends TileImage { } -export default Zoomify; \ No newline at end of file +export default Zoomify;