diff --git a/src/ol/source/Zoomify.js b/src/ol/source/Zoomify.js index 509a7e0ae2..59cf65e288 100644 --- a/src/ol/source/Zoomify.js +++ b/src/ol/source/Zoomify.js @@ -11,6 +11,7 @@ import {createCanvasContext2D} from '../dom.js'; import {toSize} from '../size.js'; import TileImage from './TileImage.js'; import TileGrid from '../tilegrid/TileGrid.js'; +import {getCenter} from '../extent.js'; /** @@ -88,7 +89,7 @@ export class CustomTile extends ImageTile { * @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. + * @property {string} url URL template or base URL of the Zoomify service. * A base URL is the fixed part * of the URL, excluding the tile group, z, x, and y folder structure, e.g. * `http://my.zoomify.info/IMAGE.TIF/`. A URL template must include @@ -100,7 +101,7 @@ export class CustomTile extends ImageTile { * A `{?-?}` template pattern, for example `subdomain{a-f}.domain.com`, may be * used instead of defining each one separately in the `urls` option. * @property {string} [tierSizeCalculation] Tier size calculation method: `default` or `truncated`. - * @property {import("../size.js").Size} [size] Size of the image. + * @property {import("../size.js").Size} size * @property {import("../extent.js").Extent} [extent] Extent for the TileGrid that is created. * Default sets the TileGrid in the * fourth quadrant, meaning extent is `[0, -height, width, 0]`. To change the @@ -125,11 +126,11 @@ export class CustomTile extends ImageTile { class Zoomify extends TileImage { /** - * @param {Options=} opt_options Options. + * @param {Options} opt_options Options. */ constructor(opt_options) { - const options = opt_options || {}; + const options = opt_options; const size = options.size; const tierSizeCalculation = options.tierSizeCalculation !== undefined ? @@ -196,7 +197,7 @@ class Zoomify extends TileImage { } const urls = expandUrl(url); - const tileWidth = tileSize * tilePixelRatio; + let tileWidth = tileSize * tilePixelRatio; /** * @param {string} template Template. @@ -259,6 +260,19 @@ class Zoomify extends TileImage { */ this.zDirection = options.zDirection; + // Server retina tile detection (non-standard): + // Try loading the center tile for the highest resolution. If it is not + // available, we are dealing with retina tiles, and need to adjust the + // tile url calculation. + const tileUrl = tileGrid.getTileCoordForCoordAndResolution(getCenter(tileGrid.getExtent()), resolutions[resolutions.length - 1]); + const testTileUrl = tileUrlFunction(tileUrl, 1, null); + const image = new Image(); + image.addEventListener('error', function() { + tileWidth = tileSize; + this.changed(); + }.bind(this)); + image.src = testTileUrl; + } } diff --git a/test/spec/ol/source/zoomify.test.js b/test/spec/ol/source/zoomify.test.js index 3edf6fb68f..3385979774 100644 --- a/test/spec/ol/source/zoomify.test.js +++ b/test/spec/ol/source/zoomify.test.js @@ -45,7 +45,7 @@ describe('ol.source.Zoomify', function() { describe('constructor', function() { - it('requires config "size"', function() { + it('requires config "size" and "url"', function() { let source; // undefined config object @@ -58,7 +58,7 @@ describe('ol.source.Zoomify', function() { source = new Zoomify({}); }).to.throwException(); - // not passing "size" in config object + // passing "url" in config object expect(function() { source = new Zoomify({ url: 'some-url' @@ -70,6 +70,14 @@ describe('ol.source.Zoomify', function() { source = new Zoomify({ size: [47, 11] }); + }).to.throwException(); + + // passing "size" and "url" in config object + expect(function() { + source = new Zoomify({ + url: '', + size: [47, 11] + }); }).to.not.throwException(); // we got a source expect(source).to.be.a(Zoomify); @@ -88,6 +96,7 @@ describe('ol.source.Zoomify', function() { it('does not need "tierSizeCalculation" option', function() { expect(function() { new Zoomify({ + url: '', size: [47, 11] }); }).to.not.throwException(); @@ -96,6 +105,7 @@ describe('ol.source.Zoomify', function() { it('accepts "tierSizeCalculation" option "default"', function() { expect(function() { new Zoomify({ + url: '', size: [47, 11], tierSizeCalculation: 'default' }); @@ -105,6 +115,7 @@ describe('ol.source.Zoomify', function() { it('accepts "tierSizeCalculation" option "truncated"', function() { expect(function() { new Zoomify({ + url: '', size: [47, 11], tierSizeCalculation: 'truncated' }); @@ -115,6 +126,7 @@ describe('ol.source.Zoomify', function() { // passing unknown string will throw expect(function() { new Zoomify({ + url: '', size: [47, 11], tierSizeCalculation: 'ace-of-spades' });