Merge pull request #10562 from crubier/master

Zoomify: Separate the service pixel ratio and the device pixel ratio
This commit is contained in:
Andreas Hocevar
2020-01-27 15:46:11 +01:00
committed by GitHub
4 changed files with 50 additions and 112 deletions
-1
View File
@@ -10,7 +10,6 @@ tags: "zoomify, deep zoom, IIP, pixel, projection"
<div class="controls"> <div class="controls">
<select id="zoomifyProtocol"> <select id="zoomifyProtocol">
<option value="zoomify">Zoomify</option> <option value="zoomify">Zoomify</option>
<option value="iip">IIP</option>
<option value="zoomifyretina">Zoomify Retina</option> <option value="zoomifyretina">Zoomify Retina</option>
</select> </select>
</div> </div>
+30 -87
View File
@@ -3,36 +3,42 @@ import View from '../src/ol/View.js';
import TileLayer from '../src/ol/layer/Tile.js'; import TileLayer from '../src/ol/layer/Tile.js';
import Zoomify from '../src/ol/source/Zoomify.js'; import Zoomify from '../src/ol/source/Zoomify.js';
const imgWidth = 9911; const imgWidth = 4000;
const imgHeight = 6100; const imgHeight = 3000;
const zoomifyUrl = 'http://vips.vtech.fr/cgi-bin/iipsrv.fcgi?zoomify=' + const zoomifyUrl = 'https://ol-zoomify.surge.sh/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 layer = new TileLayer({ const source = new Zoomify({
source: new Zoomify({ url: zoomifyUrl,
tileSize: 256, size: [imgWidth, imgHeight],
tilePixelRatio: 1, crossOrigin: 'anonymous',
url: zoomifyUrl, zDirection: -1 // Ensure we get a tile with the screen resolution or higher
size: [imgWidth, imgHeight], });
crossOrigin: 'anonymous' 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 resolutions = layer.getSource().getTileGrid().getResolutions(); });
const map = new Map({ const map = new Map({
layers: [layer], layers: [layer],
target: 'map', target: 'map',
view: new View({ view: new View({
// adjust zoom levels to those provided by the source // adjust zoom levels to those provided by the source
minResolution: resolutions[resolutions.length - 1], resolutions: layer.getSource().getTileGrid().getResolutions(),
maxResolution: resolutions[0],
// constrain the center: center cannot be set outside this extent // constrain the center: center cannot be set outside this extent
extent: extent extent: extent,
constrainOnlyCenter: true
}) })
}); });
map.getView().fit(extent); map.getView().fit(extent);
@@ -40,74 +46,11 @@ map.getView().fit(extent);
const control = document.getElementById('zoomifyProtocol'); const control = document.getElementById('zoomifyProtocol');
control.addEventListener('change', function(event) { control.addEventListener('change', function(event) {
const value = event.currentTarget.value; const value = event.currentTarget.value;
if (value === 'iip') { if (value === 'zoomify') {
const extent = [0, -imgHeight, imgWidth, 0]; layer.setSource(source);
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);
} else if (value === 'zoomifyretina') { } else if (value === 'zoomifyretina') {
const pixelRatio = 4; layer.setSource(retinaSource);
// 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);
} }
}); });
+5 -2
View File
@@ -9,6 +9,7 @@ import {Versions} from '../format/IIIFInfo.js';
import {assert} from '../asserts.js'; import {assert} from '../asserts.js';
import TileGrid from '../tilegrid/TileGrid.js'; import TileGrid from '../tilegrid/TileGrid.js';
import TileImage from './TileImage.js'; import TileImage from './TileImage.js';
import {toSize} from '../size.js';
/** /**
* @typedef {Object} Options * @typedef {Object} Options
@@ -87,7 +88,7 @@ class IIIF extends TileImage {
const extent = options.extent || [0, -height, width, 0]; const extent = options.extent || [0, -height, width, 0];
const supportsListedSizes = sizes != undefined && Array.isArray(sizes) && sizes.length > 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) && const supportsArbitraryTiling = supports != undefined && Array.isArray(supports) &&
(supports.includes('regionByPx') || supports.includes('regionByPct')) && (supports.includes('regionByPx') || supports.includes('regionByPct')) &&
(supports.includes('sizeByWh') || supports.includes('sizeByH') || (supports.includes('sizeByWh') || supports.includes('sizeByH') ||
@@ -265,7 +266,9 @@ class IIIF extends TileImage {
return baseUrl + regionParam + '/' + sizeParam + '/0/' + quality + '.' + format; 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({ super({
attributions: options.attributions, attributions: options.attributions,
+15 -22
View File
@@ -8,7 +8,6 @@ import TileState from '../TileState.js';
import {expandUrl, createFromTileUrlFunctions} from '../tileurlfunction.js'; import {expandUrl, createFromTileUrlFunctions} from '../tileurlfunction.js';
import {assert} from '../asserts.js'; import {assert} from '../asserts.js';
import {createCanvasContext2D} from '../dom.js'; import {createCanvasContext2D} from '../dom.js';
import {getTopLeft} from '../extent.js';
import {toSize} from '../size.js'; import {toSize} from '../size.js';
import TileImage from './TileImage.js'; import TileImage from './TileImage.js';
import TileGrid from '../tilegrid/TileGrid.js'; import TileGrid from '../tilegrid/TileGrid.js';
@@ -26,8 +25,7 @@ const TierSizeCalculation = {
export class CustomTile extends ImageTile { export class CustomTile extends ImageTile {
/** /**
* @param {number} tilePixelRatio Tile pixel ratio to display the tile * @param {import("../size.js").Size} tileSize Full tile size.
* @param {import("../tilegrid/TileGrid.js").default} tileGrid TileGrid that the tile belongs to.
* @param {import("../tilecoord.js").TileCoord} tileCoord Tile coordinate. * @param {import("../tilecoord.js").TileCoord} tileCoord Tile coordinate.
* @param {TileState} state State. * @param {TileState} state State.
* @param {string} src Image source URI. * @param {string} src Image source URI.
@@ -35,7 +33,8 @@ export class CustomTile extends ImageTile {
* @param {import("../Tile.js").LoadFunction} tileLoadFunction Tile load function. * @param {import("../Tile.js").LoadFunction} tileLoadFunction Tile load function.
* @param {import("../Tile.js").Options=} opt_options Tile options. * @param {import("../Tile.js").Options=} opt_options Tile options.
*/ */
constructor(tilePixelRatio, 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); super(tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options);
/** /**
@@ -45,14 +44,10 @@ export class CustomTile extends ImageTile {
this.zoomifyImage_ = null; this.zoomifyImage_ = null;
/** /**
* @private
* @type {import("../size.js").Size} * @type {import("../size.js").Size}
*/ */
this.tileSize_ = toSize(tileGrid.getTileSize(tileCoord[0])).map( this.tileSize_ = tileSize;
function(x) {
return x * tilePixelRatio;
}
);
} }
/** /**
@@ -114,8 +109,8 @@ export class CustomTile extends ImageTile {
* @property {number} [transition] Duration of the opacity transition for rendering. * @property {number} [transition] Duration of the opacity transition for rendering.
* To disable the opacity transition, pass `transition: 0`. * 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} [tileSize=256] Tile size. Same tile size is used for all zoom levels.
* @property {number} [zDirection=0] Indicate which resolution should be used * @property {number} [zDirection] Indicate which resolution should be used
* by a renderer if the view resolution does not match any resolution of the tile source. * 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 * 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. * will be used. If -1, the nearest higher resolution will be used.
*/ */
@@ -141,13 +136,12 @@ class Zoomify extends TileImage {
options.tierSizeCalculation : options.tierSizeCalculation :
TierSizeCalculation.DEFAULT; TierSizeCalculation.DEFAULT;
const tilePixelRatio = options.tilePixelRatio || 1;
const imageWidth = size[0]; const imageWidth = size[0];
const imageHeight = size[1]; const imageHeight = size[1];
const extent = options.extent || [0, -size[1], size[0], 0];
const tierSizeInTiles = []; const tierSizeInTiles = [];
const tileSize = options.tileSize || DEFAULT_TILE_SIZE; const tileSize = options.tileSize || DEFAULT_TILE_SIZE;
const tilePixelRatio = options.tilePixelRatio || 1; let tileSizeForTierSizeCalculation = tileSize * tilePixelRatio;
let tileSizeForTierSizeCalculation = tileSize;
switch (tierSizeCalculation) { switch (tierSizeCalculation) {
case TierSizeCalculation.DEFAULT: case TierSizeCalculation.DEFAULT:
@@ -179,10 +173,10 @@ class Zoomify extends TileImage {
tierSizeInTiles.push([1, 1]); tierSizeInTiles.push([1, 1]);
tierSizeInTiles.reverse(); tierSizeInTiles.reverse();
const resolutions = [1]; const resolutions = [tilePixelRatio];
const tileCountUpToTier = [0]; const tileCountUpToTier = [0];
for (let i = 1, ii = tierSizeInTiles.length; i < ii; i++) { for (let i = 1, ii = tierSizeInTiles.length; i < ii; i++) {
resolutions.push(1 << i); resolutions.push(tilePixelRatio << i);
tileCountUpToTier.push( tileCountUpToTier.push(
tierSizeInTiles[i - 1][0] * tierSizeInTiles[i - 1][1] + tierSizeInTiles[i - 1][0] * tierSizeInTiles[i - 1][1] +
tileCountUpToTier[i - 1] tileCountUpToTier[i - 1]
@@ -192,8 +186,7 @@ class Zoomify extends TileImage {
const tileGrid = new TileGrid({ const tileGrid = new TileGrid({
tileSize: tileSize, tileSize: tileSize,
extent: extent, extent: options.extent || [0, -imageHeight, imageWidth, 0],
origin: getTopLeft(extent),
resolutions: resolutions resolutions: resolutions
}); });
@@ -203,6 +196,8 @@ class Zoomify extends TileImage {
} }
const urls = expandUrl(url); const urls = expandUrl(url);
const tileWidth = tileSize * tilePixelRatio;
/** /**
* @param {string} template Template. * @param {string} template Template.
* @return {import("../Tile.js").UrlFunction} Tile URL function. * @return {import("../Tile.js").UrlFunction} Tile URL function.
@@ -226,8 +221,6 @@ class Zoomify extends TileImage {
const tileIndex = const tileIndex =
tileCoordX + tileCoordX +
tileCoordY * tierSizeInTiles[tileCoordZ][0]; 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 tileGroup = ((tileIndex + tileCountUpToTier[tileCoordZ]) / tileWidth) | 0;
const localContext = { const localContext = {
'z': tileCoordZ, 'z': tileCoordZ,
@@ -246,7 +239,7 @@ class Zoomify extends TileImage {
const tileUrlFunction = createFromTileUrlFunctions(urls.map(createFromTemplate)); const tileUrlFunction = createFromTileUrlFunctions(urls.map(createFromTemplate));
const ZoomifyTileClass = CustomTile.bind(null, tilePixelRatio, tileGrid); const ZoomifyTileClass = CustomTile.bind(null, toSize(tileSize * tilePixelRatio));
super({ super({
attributions: options.attributions, attributions: options.attributions,