Merge pull request #9430 from lutzhelm/iiif
Adds IIIF Image API tile source
This commit is contained in:
@@ -220,3 +220,11 @@ Duplicate item added to a unique collection. For example, it may be that you tr
|
||||
### 59
|
||||
|
||||
Invalid command found in the PBF. This indicates that the loaded vector tile may be corrupt.
|
||||
|
||||
### 60
|
||||
|
||||
Missing or invalid `size`.
|
||||
|
||||
### 61
|
||||
|
||||
Cannot determine IIIF Image API version from provided image information JSON.
|
||||
|
||||
16
examples/iiif.html
Normal file
16
examples/iiif.html
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
layout: example.html
|
||||
title: IIIF Image API
|
||||
shortdesc: Example of a IIIF Image API source.
|
||||
docs: >
|
||||
Example of a tile source for an International Image Interoperability Framework (IIIF) Image Service.
|
||||
Try any Image API version 1 or 2 service.
|
||||
tags: "IIIF, IIIF Image API, tile source"
|
||||
---
|
||||
<div id="map" class="map"></div>
|
||||
<div class="controls">
|
||||
<div id="iiif-notification"> </div>
|
||||
Enter <code>info.json</code> URL:
|
||||
<input type="text" id="imageInfoUrl" value="https://iiif.ub.uni-leipzig.de/iiif/j2k/0000/0107/0000010732/00000072.jpx/info.json">
|
||||
<button id="display">Display image</button>
|
||||
</div>
|
||||
46
examples/iiif.js
Normal file
46
examples/iiif.js
Normal file
@@ -0,0 +1,46 @@
|
||||
import Map from '../src/ol/Map.js';
|
||||
import View from '../src/ol/View.js';
|
||||
import TileLayer from '../src/ol/layer/Tile.js';
|
||||
import IIIF from '../src/ol/source/IIIF.js';
|
||||
import IIIFInfo from '../src/ol/format/IIIFInfo.js';
|
||||
|
||||
const layer = new TileLayer(),
|
||||
map = new Map({
|
||||
layers: [layer],
|
||||
target: 'map'
|
||||
}),
|
||||
notifyDiv = document.getElementById('iiif-notification'),
|
||||
urlInput = document.getElementById('imageInfoUrl'),
|
||||
displayButton = document.getElementById('display');
|
||||
|
||||
function refreshMap(imageInfoUrl) {
|
||||
fetch(imageInfoUrl).then(function(response) {
|
||||
response.json().then(function(imageInfo) {
|
||||
const options = new IIIFInfo(imageInfo).getTileSourceOptions();
|
||||
if (options === undefined || options.version === undefined) {
|
||||
notifyDiv.textContent = 'Data seems to be no valid IIIF image information.';
|
||||
return;
|
||||
}
|
||||
options.zDirection = -1;
|
||||
const iiifTileSource = new IIIF(options);
|
||||
layer.setSource(iiifTileSource);
|
||||
map.setView(new View({
|
||||
resolutions: iiifTileSource.getTileGrid().getResolutions(),
|
||||
extent: iiifTileSource.getTileGrid().getExtent(),
|
||||
constrainOnlyCenter: true
|
||||
}));
|
||||
map.getView().fit(iiifTileSource.getTileGrid().getExtent());
|
||||
notifyDiv.textContent = '';
|
||||
}).catch(function(body) {
|
||||
notifyDiv.textContent = 'Could not read image info json. ' + body;
|
||||
});
|
||||
}).catch(function() {
|
||||
notifyDiv.textContent = 'Could not read data from URL.';
|
||||
});
|
||||
}
|
||||
|
||||
displayButton.addEventListener('click', function() {
|
||||
refreshMap(urlInput.value);
|
||||
});
|
||||
|
||||
refreshMap(urlInput.value);
|
||||
@@ -7,6 +7,7 @@ export {default as GeoJSON} from './format/GeoJSON.js';
|
||||
export {default as GML} from './format/GML.js';
|
||||
export {default as GPX} from './format/GPX.js';
|
||||
export {default as IGC} from './format/IGC.js';
|
||||
export {default as IIIFInfo} from './format/IIIFInfo.js';
|
||||
export {default as KML} from './format/KML.js';
|
||||
export {default as MVT} from './format/MVT.js';
|
||||
export {default as OWS} from './format/OWS.js';
|
||||
|
||||
426
src/ol/format/IIIFInfo.js
Normal file
426
src/ol/format/IIIFInfo.js
Normal file
@@ -0,0 +1,426 @@
|
||||
/**
|
||||
* @module ol/format/IIIFInfo
|
||||
*/
|
||||
|
||||
import {assert} from '../asserts.js';
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {Object} PreferredOptions
|
||||
* @property {string} [format] Preferred image format. Will be used if the image information
|
||||
* indicates support for that format.
|
||||
* @property {string} [quality] IIIF image qualitiy. Will be used if the image information
|
||||
* indicates support for that quality.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} SupportedFeatures
|
||||
* @property {Array<string>} [supports] Supported IIIF image size and region
|
||||
* calculation features.
|
||||
* @property {Array<string>} [formats] Supported image formats.
|
||||
* @property {Array<string>} [qualities] Supported IIIF image qualities.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} ImageInformationResponse1_0
|
||||
* @property {string} identifier
|
||||
* @property {number} width
|
||||
* @property {number} height
|
||||
* @property {Array<number>} [scale_factors] Resolution scaling factors.
|
||||
* @property {number} [tile_width]
|
||||
* @property {number} [tile_height]
|
||||
* @property {Array<string>} [formats] Supported image formats.
|
||||
* @property {string} [profile] Compliance level URI.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} ImageInformationResponse1_1
|
||||
* @property {string} "@id" The base URI of the image service.
|
||||
* @property {string} "@context" JSON-LD context URI.
|
||||
* @property {number} width Full image width.
|
||||
* @property {number} height Full image height.
|
||||
* @property {Array<number>} [scale_factors] Resolution scaling factors.
|
||||
* @property {number} [tile_width]
|
||||
* @property {number} [tile_height]
|
||||
* @property {Array<string>} [formats] Supported image formats.
|
||||
* @property {string} [profile] Compliance level URI.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} TileInfo
|
||||
* @property {Array<number>} scaleFactors Supported resolution scaling factors.
|
||||
* @property {number} width Tile width in pixels.
|
||||
* @property {number} [height] Tile height in pixels. Same as tile width if height is
|
||||
* not given.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} IiifProfile
|
||||
* @property {Array<string>} [formats] Supported image formats for the image service.
|
||||
* @property {Array<string>} [qualities] Supported IIIF image qualities.
|
||||
* @property {Array<string>} [supports] Supported features.
|
||||
* @property {number} [maxArea] Maximum area (pixels) available for this image service.
|
||||
* @property {number} [maxHeight] Maximum height.
|
||||
* @property {number} [maxWidth] Maximum width.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} ImageInformationResponse2
|
||||
* @property {string} "@id" The base URI of the image service.
|
||||
* @property {string} "@context" JSON-LD context IRI
|
||||
* @property {number} width Full image width.
|
||||
* @property {number} height Full image height.
|
||||
* @property {Array<string|IiifProfile>} profile Additional informations about the image
|
||||
* service's capabilities.
|
||||
* @property {Array<Object<string, number>>} [sizes] Supported full image dimensions.
|
||||
* @property {Array<TileInfo>} [tiles] Supported tile sizes and resolution scaling factors.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} ImageInformationResponse3
|
||||
* @property {string} id The base URI of the image service.
|
||||
* @property {string} "@context" JSON-LD context IRI
|
||||
* @property {number} width Full image width.
|
||||
* @property {number} height Full image height.
|
||||
* @property {string} profile Compliance level, one of 'level0', 'level1' or 'level2'
|
||||
* @property {Array<Object<string, number>>} [sizes] Supported full image dimensions.
|
||||
* @property {Array<TileInfo>} [tiles] Supported tile sizes and resolution scaling factors.
|
||||
* @property {number} [maxArea] Maximum area (pixels) available for this image service.
|
||||
* @property {number} [maxHeight] Maximum height.
|
||||
* @property {number} [maxWidth] Maximum width.
|
||||
* @property {Array<string>} [extraQualities] IIIF image qualities supported by the
|
||||
* image service additional to the ones indicated by the compliance level.
|
||||
* @property {Array<string>} [extraFormats] Image formats supported by the
|
||||
* image service additional to the ones indicated by the compliance level.
|
||||
* @property {Array<string>} [extraFeatures] Additional supported features whose support
|
||||
* is not indicated by the compliance level.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @enum {string}
|
||||
*/
|
||||
export const Versions = {
|
||||
VERSION1: 'version1',
|
||||
VERSION2: 'version2',
|
||||
VERSION3: 'version3'
|
||||
};
|
||||
|
||||
/**
|
||||
* Supported image formats, qualities and supported region / size calculation features
|
||||
* for different image API versions and compliance levels
|
||||
* @const
|
||||
* @type {Object<string, Object<string, SupportedFeatures>>}
|
||||
*/
|
||||
const IIIF_PROFILE_VALUES = {};
|
||||
IIIF_PROFILE_VALUES[Versions.VERSION1] = {
|
||||
'level0': {
|
||||
supports: [],
|
||||
formats: [],
|
||||
qualities: ['native']
|
||||
},
|
||||
'level1': {
|
||||
supports: ['regionByPx', 'sizeByW', 'sizeByH', 'sizeByPct'],
|
||||
formats: ['jpg'],
|
||||
qualities: ['native']
|
||||
},
|
||||
'level2': {
|
||||
supports: ['regionByPx', 'regionByPct', 'sizeByW', 'sizeByH', 'sizeByPct',
|
||||
'sizeByConfinedWh', 'sizeByWh'],
|
||||
formats: ['jpg', 'png'],
|
||||
qualities: ['native', 'color', 'grey', 'bitonal']
|
||||
}
|
||||
};
|
||||
IIIF_PROFILE_VALUES[Versions.VERSION2] = {
|
||||
'level0': {
|
||||
supports: [],
|
||||
formats: ['jpg'],
|
||||
qualities: ['default']
|
||||
},
|
||||
'level1': {
|
||||
supports: ['regionByPx', 'sizeByW', 'sizeByH', 'sizeByPct'],
|
||||
formats: ['jpg'],
|
||||
qualities: ['default']
|
||||
},
|
||||
'level2': {
|
||||
supports: ['regionByPx', 'regionByPct', 'sizeByW', 'sizeByH', 'sizeByPct',
|
||||
'sizeByConfinedWh', 'sizeByDistortedWh', 'sizeByWh'],
|
||||
formats: ['jpg', 'png'],
|
||||
qualities: ['default', 'bitonal']
|
||||
}
|
||||
};
|
||||
IIIF_PROFILE_VALUES[Versions.VERSION3] = {
|
||||
'level0': {
|
||||
supports: [],
|
||||
formats: ['jpg'],
|
||||
qualities: ['default']
|
||||
},
|
||||
'level1': {
|
||||
supports: ['regionByPx', 'regionSquare', 'sizeByW', 'sizeByH'],
|
||||
formats: ['jpg'],
|
||||
qualities: ['default']
|
||||
},
|
||||
'level2': {
|
||||
supports: ['regionByPx', 'regionSquare', 'regionByPct',
|
||||
'sizeByW', 'sizeByH', 'sizeByPct', 'sizeByConfinedWh', 'sizeByWh'],
|
||||
formats: ['jpg'],
|
||||
qualities: ['default', 'bitonal']
|
||||
}
|
||||
};
|
||||
IIIF_PROFILE_VALUES['none'] = {
|
||||
'none': {
|
||||
supports: [],
|
||||
formats: [],
|
||||
qualities: []
|
||||
}
|
||||
};
|
||||
|
||||
const COMPLIANCE_VERSION1 = new RegExp('^https?\:\/\/library\.stanford\.edu\/iiif\/image-api\/(1\.1\/)?compliance\.html#level[0-2]$');
|
||||
const COMPLIANCE_VERSION2 = new RegExp('^https?\:\/\/iiif\.io\/api\/image\/2\/level[0-2](\.json)?$');
|
||||
const COMPLIANCE_VERSION3 = new RegExp('(^https?\:\/\/iiif\.io\/api\/image\/3\/level[0-2](\.json)?$)|(^level[0-2]$)');
|
||||
|
||||
function generateVersion1Options(iiifInfo) {
|
||||
let levelProfile = iiifInfo.getComplianceLevelSupportedFeatures();
|
||||
// Version 1.0 and 1.1 do not require a profile.
|
||||
if (levelProfile === undefined) {
|
||||
levelProfile = IIIF_PROFILE_VALUES[Versions.VERSION1]['level0'];
|
||||
}
|
||||
return {
|
||||
url: iiifInfo.imageInfo['@id'] === undefined ? undefined : iiifInfo.imageInfo['@id'].replace(/\/?(info.json)?$/g, ''),
|
||||
supports: levelProfile.supports,
|
||||
formats: [...levelProfile.formats, iiifInfo.imageInfo.formats === undefined ?
|
||||
[] : iiifInfo.imageInfo.formats
|
||||
],
|
||||
qualities: [...levelProfile.qualities, iiifInfo.imageInfo.qualities === undefined ?
|
||||
[] : iiifInfo.imageInfo.qualities
|
||||
],
|
||||
resolutions: iiifInfo.imageInfo.scale_factors,
|
||||
tileSize: iiifInfo.imageInfo.tile_width !== undefined ? (iiifInfo.imageInfo.tile_height !== undefined ?
|
||||
[iiifInfo.imageInfo.tile_width, iiifInfo.imageInfo.tile_height] : [iiifInfo.imageInfo.tile_width, iiifInfo.imageInfo.tile_width]) :
|
||||
(iiifInfo.imageInfo.tile_height != undefined ? [iiifInfo.imageInfo.tile_height, iiifInfo.imageInfo.tile_height] : undefined)
|
||||
};
|
||||
}
|
||||
|
||||
function generateVersion2Options(iiifInfo) {
|
||||
const levelProfile = iiifInfo.getComplianceLevelSupportedFeatures(),
|
||||
additionalProfile = Array.isArray(iiifInfo.imageInfo.profile) && iiifInfo.imageInfo.profile.length > 1,
|
||||
profileSupports = additionalProfile && iiifInfo.imageInfo.profile[1].supports ? iiifInfo.imageInfo.profile[1].supports : [],
|
||||
profileFormats = additionalProfile && iiifInfo.imageInfo.profile[1].formats ? iiifInfo.imageInfo.profile[1].formats : [],
|
||||
profileQualities = additionalProfile && iiifInfo.imageInfo.profile[1].qualities ? iiifInfo.imageInfo.profile[1].qualities : [];
|
||||
return {
|
||||
url: iiifInfo.imageInfo['@id'].replace(/\/?(info.json)?$/g, ''),
|
||||
sizes: iiifInfo.imageInfo.sizes === undefined ? undefined : iiifInfo.imageInfo.sizes.map(function(size) {
|
||||
return [size.width, size.height];
|
||||
}),
|
||||
tileSize: iiifInfo.imageInfo.tiles === undefined ? undefined : [
|
||||
iiifInfo.imageInfo.tiles.map(function(tile) {
|
||||
return tile.width;
|
||||
})[0],
|
||||
iiifInfo.imageInfo.tiles.map(function(tile) {
|
||||
return tile.height === undefined ? tile.width : tile.height;
|
||||
})[0]
|
||||
],
|
||||
resolutions: iiifInfo.imageInfo.tiles === undefined ? undefined :
|
||||
iiifInfo.imageInfo.tiles.map(function(tile) {
|
||||
return tile.scaleFactors;
|
||||
})[0],
|
||||
supports: [...levelProfile.supports, ...profileSupports],
|
||||
formats: [...levelProfile.formats, ...profileFormats],
|
||||
qualities: [...levelProfile.qualities, ...profileQualities]
|
||||
};
|
||||
}
|
||||
|
||||
function generateVersion3Options(iiifInfo) {
|
||||
const levelProfile = iiifInfo.getComplianceLevelSupportedFeatures();
|
||||
return {
|
||||
url: iiifInfo.imageInfo['id'],
|
||||
sizes: iiifInfo.imageInfo.sizes === undefined ? undefined : iiifInfo.imageInfo.sizes.map(function(size) {
|
||||
return [size.width, size.height];
|
||||
}),
|
||||
tileSize: iiifInfo.imageInfo.tiles === undefined ? undefined : [
|
||||
iiifInfo.imageInfo.tiles.map(function(tile) {
|
||||
return tile.width;
|
||||
})[0],
|
||||
iiifInfo.imageInfo.tiles.map(function(tile) {
|
||||
return tile.height;
|
||||
})[0]
|
||||
],
|
||||
resolutions: iiifInfo.imageInfo.tiles === undefined ? undefined :
|
||||
iiifInfo.imageInfo.tiles.map(function(tile) {
|
||||
return tile.scaleFactors;
|
||||
})[0],
|
||||
supports: iiifInfo.imageInfo.extraFeatures === undefined ? levelProfile.supports :
|
||||
[...levelProfile.supports, ...iiifInfo.imageInfo.extraFeatures],
|
||||
formats: iiifInfo.imageInfo.extraFormats === undefined ? levelProfile.formats :
|
||||
[...levelProfile.formats, ...iiifInfo.imageInfo.extraFormats],
|
||||
qualities: iiifInfo.imageInfo.extraQualities === undefined ? levelProfile.qualities :
|
||||
[...levelProfile.supports, ...iiifInfo.imageInfo.extraQualities],
|
||||
maxWidth: undefined,
|
||||
maxHeight: undefined,
|
||||
maxArea: undefined
|
||||
};
|
||||
}
|
||||
|
||||
const versionFunctions = {};
|
||||
versionFunctions[Versions.VERSION1] = generateVersion1Options;
|
||||
versionFunctions[Versions.VERSION2] = generateVersion2Options;
|
||||
versionFunctions[Versions.VERSION3] = generateVersion3Options;
|
||||
|
||||
/**
|
||||
* @classdesc
|
||||
* Format for transforming IIIF Image API image information responses into
|
||||
* IIIF tile source ready options
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
class IIIFInfo {
|
||||
|
||||
/**
|
||||
* @param {ImageInformationResponse1_0|ImageInformationResponse1_1|ImageInformationResponse2|ImageInformationResponse3|string} imageInfo
|
||||
* Deserialized image information JSON response object or JSON response as string
|
||||
*/
|
||||
constructor(imageInfo) {
|
||||
this.setImageInfo(imageInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object|string} imageInfo Deserialized image information JSON response
|
||||
* object or JSON response as string
|
||||
*/
|
||||
setImageInfo(imageInfo) {
|
||||
if (typeof imageInfo == 'string') {
|
||||
this.imageInfo = JSON.parse(imageInfo);
|
||||
} else {
|
||||
this.imageInfo = imageInfo;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Versions} Major IIIF version.
|
||||
*/
|
||||
getImageApiVersion() {
|
||||
if (this.imageInfo === undefined) {
|
||||
return;
|
||||
}
|
||||
let context = this.imageInfo['@context'] || 'ol-no-context';
|
||||
if (typeof context == 'string') {
|
||||
context = [context];
|
||||
}
|
||||
for (let i = 0; i < context.length; i++) {
|
||||
switch (context[i]) {
|
||||
case 'http://library.stanford.edu/iiif/image-api/1.1/context.json':
|
||||
case 'http://iiif.io/api/image/1/context.json':
|
||||
return Versions.VERSION1;
|
||||
case 'http://iiif.io/api/image/2/context.json':
|
||||
return Versions.VERSION2;
|
||||
case 'http://iiif.io/api/image/3/context.json':
|
||||
return Versions.VERSION3;
|
||||
case 'ol-no-context':
|
||||
// Image API 1.0 has no '@context'
|
||||
if (this.getComplianceLevelEntryFromProfile(Versions.VERSION1) && this.imageInfo.identifier) {
|
||||
return Versions.VERSION1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
assert(false, 61);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Versions} version Optional IIIF image API version
|
||||
* @returns {string} Compliance level as it appears in the IIIF image information
|
||||
* response.
|
||||
*/
|
||||
getComplianceLevelEntryFromProfile(version) {
|
||||
if (this.imageInfo === undefined || this.imageInfo.profile === undefined) {
|
||||
return;
|
||||
}
|
||||
if (version === undefined) {
|
||||
version = this.getImageApiVersion();
|
||||
}
|
||||
switch (version) {
|
||||
case Versions.VERSION1:
|
||||
if (COMPLIANCE_VERSION1.test(this.imageInfo.profile)) {
|
||||
return this.imageInfo.profile;
|
||||
}
|
||||
break;
|
||||
case Versions.VERSION3:
|
||||
if (COMPLIANCE_VERSION3.test(this.imageInfo.profile)) {
|
||||
return this.imageInfo.profile;
|
||||
}
|
||||
break;
|
||||
case Versions.VERSION2:
|
||||
if (typeof this.imageInfo.profile === 'string' && COMPLIANCE_VERSION2.test(this.imageInfo.profile)) {
|
||||
return this.imageInfo.profile;
|
||||
}
|
||||
if (Array.isArray(this.imageInfo.profile) && this.imageInfo.profile.length > 0
|
||||
&& typeof this.imageInfo.profile[0] === 'string' && COMPLIANCE_VERSION2.test(this.imageInfo.profile[0])) {
|
||||
return this.imageInfo.profile[0];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Versions} version Optional IIIF image API version
|
||||
* @returns {string} Compliance level, on of 'level0', 'level1' or 'level2' or undefined
|
||||
*/
|
||||
getComplianceLevelFromProfile(version) {
|
||||
const complianceLevel = this.getComplianceLevelEntryFromProfile(version);
|
||||
if (complianceLevel === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
const level = complianceLevel.match(/level[0-2](\.json)?$/g);
|
||||
return Array.isArray(level) ? level[0].replace('.json', '') : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {SupportedFeatures} Image formats, qualities and region / size calculation
|
||||
* methods that are supported by the IIIF service.
|
||||
*/
|
||||
getComplianceLevelSupportedFeatures() {
|
||||
if (this.imageInfo === undefined) {
|
||||
return;
|
||||
}
|
||||
const version = this.getImageApiVersion();
|
||||
const level = this.getComplianceLevelFromProfile(version);
|
||||
if (level === undefined) {
|
||||
return IIIF_PROFILE_VALUES['none']['none'];
|
||||
}
|
||||
return IIIF_PROFILE_VALUES[version][level];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PreferredOptions} opt_preferredOptions Optional options for preferred format and quality.
|
||||
* @returns {import("../source/IIIF.js").Options} IIIF tile source ready constructor options.
|
||||
*/
|
||||
getTileSourceOptions(opt_preferredOptions) {
|
||||
const options = opt_preferredOptions || {},
|
||||
version = this.getImageApiVersion();
|
||||
if (version === undefined) {
|
||||
return;
|
||||
}
|
||||
const imageOptions = version === undefined ? undefined : versionFunctions[version](this);
|
||||
if (imageOptions === undefined) {
|
||||
return;
|
||||
}
|
||||
return {
|
||||
url: imageOptions.url,
|
||||
version: version,
|
||||
size: [this.imageInfo.width, this.imageInfo.height],
|
||||
sizes: imageOptions.sizes,
|
||||
format: imageOptions.formats.includes(options.format) ? options.format : 'jpg',
|
||||
supports: imageOptions.supports,
|
||||
quality: options.quality && imageOptions.qualities.includes(options.quality) ?
|
||||
options.quality : imageOptions.qualities.includes('native') ? 'native' : 'default',
|
||||
resolutions: Array.isArray(imageOptions.resolutions) ? imageOptions.resolutions.sort(function(a, b) {
|
||||
return b - a;
|
||||
}) : undefined,
|
||||
tileSize: imageOptions.tileSize
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default IIIFInfo;
|
||||
@@ -5,6 +5,7 @@
|
||||
export {default as BingMaps} from './source/BingMaps.js';
|
||||
export {default as CartoDB} from './source/CartoDB.js';
|
||||
export {default as Cluster} from './source/Cluster.js';
|
||||
export {default as IIIF} from './source/IIIF.js';
|
||||
export {default as Image} from './source/Image.js';
|
||||
export {default as ImageArcGISRest} from './source/ImageArcGISRest.js';
|
||||
export {default as ImageCanvas} from './source/ImageCanvas.js';
|
||||
|
||||
284
src/ol/source/IIIF.js
Normal file
284
src/ol/source/IIIF.js
Normal file
@@ -0,0 +1,284 @@
|
||||
/**
|
||||
* @module ol/source/IIIF
|
||||
*/
|
||||
|
||||
import {DEFAULT_TILE_SIZE} from '../tilegrid/common.js';
|
||||
import {getTopLeft} from '../extent.js';
|
||||
import {CustomTile} from './Zoomify.js';
|
||||
import {Versions} from '../format/IIIFInfo.js';
|
||||
import {assert} from '../asserts.js';
|
||||
import TileGrid from '../tilegrid/TileGrid.js';
|
||||
import TileImage from './TileImage.js';
|
||||
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
|
||||
* @property {boolean} [attributionsCollapsible=true] Attributions are collapsible.
|
||||
* @property {number} [cacheSize]
|
||||
* @property {null|string} [crossOrigin]
|
||||
* @property {import("../extent.js").Extent} [extent=[0, -height, width, 0]]
|
||||
* @property {string} [format='jpg'] Requested image format.
|
||||
* @property {import("../proj.js").ProjectionLike} [projection]
|
||||
* @property {string} [quality] Requested IIIF image quality. Default is 'native'
|
||||
* for version 1, 'default' for versions 2 and 3.
|
||||
* @property {number} [reprojectionErrorThreshold=0.5] Maximum allowed reprojection error (in pixels).
|
||||
* Higher values can increase reprojection performance, but decrease precision.
|
||||
* @property {import("../size.js").Size} size Size of the image [width, height].
|
||||
* @property {import("../size.js").Size[]} [sizes] Supported scaled image sizes.
|
||||
* Content of the IIIF info.json 'sizes' property, but as array of Size objects.
|
||||
* @property {import("./State.js").default} [state] Source state.
|
||||
* @property {Array<string>} [supports=[]] Supported IIIF region and size calculation
|
||||
* features.
|
||||
* @property {number} [tilePixelRatio]
|
||||
* @property {number|import("../size.js").Size} [tileSize] Tile size.
|
||||
* Same tile size is used for all zoom levels. If tile size is a number,
|
||||
* a square tile is assumed. If the IIIF image service supports arbitrary
|
||||
* tiling (sizeByH, sizeByW, sizeByWh or sizeByPct as well as regionByPx or regionByPct
|
||||
* are supported), the default tilesize is 256.
|
||||
* @property {number} [transition]
|
||||
* @property {string} [url] Base URL of the IIIF Image service.
|
||||
* This should be the same as the IIIF Image @id.
|
||||
* @property {Versions} [version=Versions.VERSION2] Service's IIIF Image API version.
|
||||
* @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.
|
||||
*/
|
||||
|
||||
function formatPercentage(percentage) {
|
||||
return percentage.toLocaleString('en', {maximumFractionDigits: 10});
|
||||
}
|
||||
|
||||
/**
|
||||
* @classdesc
|
||||
* Layer source for IIIF Image API services.
|
||||
* @api
|
||||
*/
|
||||
class IIIF extends TileImage {
|
||||
|
||||
constructor(opt_options) {
|
||||
|
||||
const options = opt_options || {};
|
||||
|
||||
let baseUrl = options.url || '';
|
||||
baseUrl = baseUrl + (baseUrl.lastIndexOf('/') === baseUrl.length - 1 || baseUrl === '' ? '' : '/');
|
||||
const version = options.version || Versions.VERSION2;
|
||||
const sizes = options.sizes || [];
|
||||
const size = options.size;
|
||||
assert(size != undefined && Array.isArray(size) && size.length == 2 &&
|
||||
!isNaN(size[0]) && size[0] > 0 && !isNaN(size[1]) && size[1] > 0, 60);
|
||||
const width = size[0];
|
||||
const height = size[1];
|
||||
const tileSize = options.tileSize;
|
||||
const format = options.format || 'jpg';
|
||||
const quality = options.quality || (options.version == Versions.VERSION1 ? 'native' : 'default');
|
||||
let resolutions = options.resolutions || [];
|
||||
const supports = options.supports || [];
|
||||
const extent = options.extent || [0, -height, width, 0];
|
||||
|
||||
const supportsListedSizes = sizes != undefined && Array.isArray(sizes) && sizes.length > 0;
|
||||
const supportsListedTiles = tileSize != undefined && (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') ||
|
||||
supports.includes('sizeByW') || supports.includes('sizeByPct'));
|
||||
|
||||
let tileWidth,
|
||||
tileHeight,
|
||||
maxZoom;
|
||||
|
||||
resolutions.sort(function(a, b) {
|
||||
return b - a;
|
||||
});
|
||||
|
||||
if (supportsListedTiles || supportsArbitraryTiling) {
|
||||
if (tileSize != undefined) {
|
||||
if (Number.isInteger(tileSize) && tileSize > 0) {
|
||||
tileWidth = tileSize;
|
||||
tileHeight = tileSize;
|
||||
} else if (Array.isArray(tileSize) && tileSize.length > 0) {
|
||||
if (tileSize.length == 1 || tileSize[1] == undefined && Number.isInteger(tileSize[0])) {
|
||||
tileWidth = tileSize[0];
|
||||
tileHeight = tileSize[0];
|
||||
}
|
||||
if (tileSize.length == 2) {
|
||||
if (Number.isInteger(tileSize[0]) && Number.isInteger(tileSize[1])) {
|
||||
tileWidth = tileSize[0];
|
||||
tileHeight = tileSize[1];
|
||||
} else if (tileSize[0] == undefined && Number.isInteger(tileSize[1])) {
|
||||
tileWidth = tileSize[1];
|
||||
tileHeight = tileSize[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tileWidth === undefined || tileHeight === undefined) {
|
||||
tileWidth = DEFAULT_TILE_SIZE;
|
||||
tileHeight = DEFAULT_TILE_SIZE;
|
||||
}
|
||||
if (resolutions.length == 0) {
|
||||
maxZoom = Math.max(
|
||||
Math.ceil(Math.log(width / tileWidth) / Math.LN2),
|
||||
Math.ceil(Math.log(height / tileHeight) / Math.LN2)
|
||||
);
|
||||
for (let i = maxZoom; i >= 0; i--) {
|
||||
resolutions.push(Math.pow(2, i));
|
||||
}
|
||||
} else {
|
||||
const maxScaleFactor = Math.max([...resolutions]);
|
||||
// TODO maxScaleFactor might not be a power to 2
|
||||
maxZoom = Math.round(Math.log(maxScaleFactor) / Math.LN2);
|
||||
}
|
||||
} else {
|
||||
// No tile support.
|
||||
tileWidth = width;
|
||||
tileHeight = height;
|
||||
resolutions = [];
|
||||
if (supportsListedSizes) {
|
||||
/*
|
||||
* 'sizes' provided. Use full region in different resolutions. Every
|
||||
* resolution has only one tile.
|
||||
*/
|
||||
sizes.sort(function(a, b) {
|
||||
return a[0] - b[0];
|
||||
});
|
||||
maxZoom = -1;
|
||||
const ignoredSizesIndex = [];
|
||||
for (let i = 0; i < sizes.length; i++) {
|
||||
const resolution = width / sizes[i][0];
|
||||
if (resolutions.length > 0 && resolutions[resolutions.length - 1] == resolution) {
|
||||
ignoredSizesIndex.push(i);
|
||||
continue;
|
||||
}
|
||||
resolutions.push(resolution);
|
||||
maxZoom++;
|
||||
}
|
||||
if (ignoredSizesIndex.length > 0) {
|
||||
for (let i = 0; i < ignoredSizesIndex.length; i++) {
|
||||
sizes.splice(ignoredSizesIndex[i] - i, 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No useful image information at all. Try pseudo tile with full image.
|
||||
resolutions.push(1);
|
||||
sizes.push([width, height]);
|
||||
maxZoom = 0;
|
||||
}
|
||||
}
|
||||
|
||||
const tileGrid = new TileGrid({
|
||||
tileSize: [tileWidth, tileHeight],
|
||||
extent: extent,
|
||||
origin: getTopLeft(extent),
|
||||
resolutions: resolutions
|
||||
});
|
||||
|
||||
const tileUrlFunction = function(tileCoord, pixelRatio, projection) {
|
||||
let regionParam,
|
||||
sizeParam;
|
||||
const zoom = tileCoord[0];
|
||||
if (zoom > maxZoom) {
|
||||
return;
|
||||
}
|
||||
const tileX = tileCoord[1],
|
||||
tileY = tileCoord[2],
|
||||
scale = resolutions[zoom];
|
||||
if (tileX === undefined || tileY === undefined || scale === undefined ||
|
||||
tileX < 0 || Math.ceil(width / scale / tileWidth) <= tileX ||
|
||||
tileY < 0 || Math.ceil(height / scale / tileHeight) <= tileY) {
|
||||
return;
|
||||
}
|
||||
if (supportsArbitraryTiling || supportsListedTiles) {
|
||||
const regionX = tileX * tileWidth * scale,
|
||||
regionY = tileY * tileHeight * scale;
|
||||
let regionW = tileWidth * scale,
|
||||
regionH = tileHeight * scale,
|
||||
sizeW = tileWidth,
|
||||
sizeH = tileHeight;
|
||||
if (regionX + regionW > width) {
|
||||
regionW = width - regionX;
|
||||
}
|
||||
if (regionY + regionH > height) {
|
||||
regionH = height - regionY;
|
||||
}
|
||||
if (regionX + tileWidth * scale > width) {
|
||||
sizeW = Math.floor((width - regionX + scale - 1) / scale);
|
||||
}
|
||||
if (regionY + tileHeight * scale > height) {
|
||||
sizeH = Math.floor((height - regionY + scale - 1) / scale);
|
||||
}
|
||||
if (regionX == 0 && regionW == width && regionY == 0 && regionH == height) {
|
||||
// canonical full image region parameter is 'full', not 'x,y,w,h'
|
||||
regionParam = 'full';
|
||||
} else if (!supportsArbitraryTiling || supports.includes('regionByPx')) {
|
||||
regionParam = regionX + ',' + regionY + ',' + regionW + ',' + regionH;
|
||||
} else if (supports.includes('regionByPct')) {
|
||||
const pctX = formatPercentage(regionX / width * 100),
|
||||
pctY = formatPercentage(regionY / height * 100),
|
||||
pctW = formatPercentage(regionW / width * 100),
|
||||
pctH = formatPercentage(regionH / height * 100);
|
||||
regionParam = 'pct:' + pctX + ',' + pctY + ',' + pctW + ',' + pctH;
|
||||
}
|
||||
if (version == Versions.VERSION3 && (!supportsArbitraryTiling || supports.includes('sizeByWh'))) {
|
||||
sizeParam = sizeW + ',' + sizeH;
|
||||
} else if (!supportsArbitraryTiling || supports.includes('sizeByW')) {
|
||||
sizeParam = sizeW + ',';
|
||||
} else if (supports.includes('sizeByH')) {
|
||||
sizeParam = ',' + sizeH;
|
||||
} else if (supports.includes('sizeByWh')) {
|
||||
sizeParam = sizeW + ',' + sizeH;
|
||||
} else if (supports.includes('sizeByPct')) {
|
||||
sizeParam = 'pct:' + formatPercentage(100 / scale);
|
||||
}
|
||||
} else {
|
||||
regionParam = 'full';
|
||||
if (supportsListedSizes) {
|
||||
const regionWidth = sizes[zoom][0],
|
||||
regionHeight = sizes[zoom][1];
|
||||
if (version == Versions.VERSION3) {
|
||||
if (regionWidth == width && regionHeight == height) {
|
||||
sizeParam = 'max';
|
||||
} else {
|
||||
sizeParam = regionWidth + ',' + regionHeight;
|
||||
}
|
||||
} else {
|
||||
if (regionWidth == width) {
|
||||
sizeParam = 'full';
|
||||
} else {
|
||||
sizeParam = regionWidth + ',';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sizeParam = version == Versions.VERSION3 ? 'max' : 'full';
|
||||
}
|
||||
}
|
||||
return baseUrl + regionParam + '/' + sizeParam + '/0/' + quality + '.' + format;
|
||||
};
|
||||
|
||||
const IiifTileClass = CustomTile.bind(null, tileGrid);
|
||||
|
||||
super({
|
||||
attributions: options.attributions,
|
||||
attributionsCollapsible: options.attributionsCollapsible,
|
||||
cacheSize: options.cacheSize,
|
||||
crossOrigin: options.crossOrigin,
|
||||
projection: options.projection,
|
||||
reprojectionErrorThreshold: options.reprojectionErrorThreshold,
|
||||
state: options.state,
|
||||
tileClass: IiifTileClass,
|
||||
tileGrid: tileGrid,
|
||||
tilePixelRatio: options.tilePixelRatio,
|
||||
tileUrlFunction: tileUrlFunction,
|
||||
transition: options.transition
|
||||
});
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
this.zDirection = options.zDirection;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default IIIF;
|
||||
@@ -237,6 +237,7 @@ class TileGrid {
|
||||
/**
|
||||
* Get the extent for this tile grid, if it was configured.
|
||||
* @return {import("../extent.js").Extent} Extent.
|
||||
* @api
|
||||
*/
|
||||
getExtent() {
|
||||
return this.extent_;
|
||||
|
||||
487
test/spec/ol/format/iiif.test.js
Normal file
487
test/spec/ol/format/iiif.test.js
Normal file
@@ -0,0 +1,487 @@
|
||||
import IIIFInfo from '../../../../src/ol/format/IIIFInfo.js';
|
||||
import {Versions} from '../../../../src/ol/format/IIIFInfo.js';
|
||||
|
||||
describe('ol.format.IIIFInfo', function() {
|
||||
|
||||
const iiifInfo = new IIIFInfo();
|
||||
|
||||
describe('setImageInfo', function() {
|
||||
|
||||
it('can handle image info JSON as object or as string serialization', function() {
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://iiif.io/api/image/3/context.json',
|
||||
'@id': 'http://iiif.test/id'
|
||||
});
|
||||
expect(iiifInfo.getImageApiVersion()).to.be(Versions.VERSION3);
|
||||
|
||||
iiifInfo.setImageInfo('{"@context": "http://iiif.io/api/image/2/context.json","@id":"http://iiif.test/id"}');
|
||||
expect(iiifInfo.getImageApiVersion()).to.be(Versions.VERSION2);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('getImageApiVersion', function() {
|
||||
|
||||
it('provides the correct Image API version', function() {
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@id': 'http://iiif.test/id'
|
||||
});
|
||||
expect(function() {
|
||||
iiifInfo.getImageApiVersion();
|
||||
}).to.throwException();
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
identifier: 'http://iiif.test/id',
|
||||
profile: 'this is no valid profile'
|
||||
});
|
||||
expect(function() {
|
||||
iiifInfo.getImageApiVersion();
|
||||
}).to.throwException();
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'this is no valid context',
|
||||
'@id': 'http://iiif.test/id'
|
||||
});
|
||||
expect(function() {
|
||||
iiifInfo.getImageApiVersion();
|
||||
}).to.throwException();
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
identifier: 'http://iiif.test/id',
|
||||
profile: 'http://library.stanford.edu/iiif/image-api/compliance.html#level0'
|
||||
});
|
||||
expect(iiifInfo.getImageApiVersion()).to.be(Versions.VERSION1);
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://library.stanford.edu/iiif/image-api/1.1/context.json',
|
||||
'@id': 'http://iiif.test/id'
|
||||
});
|
||||
expect(iiifInfo.getImageApiVersion()).to.be(Versions.VERSION1);
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://iiif.io/api/image/1/context.json',
|
||||
identifier: 'http://iiif.test/id'
|
||||
});
|
||||
expect(iiifInfo.getImageApiVersion()).to.be(Versions.VERSION1);
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://iiif.io/api/image/2/context.json',
|
||||
'@id': 'http://iiif.test/id'
|
||||
});
|
||||
expect(iiifInfo.getImageApiVersion()).to.be(Versions.VERSION2);
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://iiif.io/api/image/3/context.json',
|
||||
id: 'http://iiif.test/id'
|
||||
});
|
||||
expect(iiifInfo.getImageApiVersion()).to.be(Versions.VERSION3);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('getComplianceLevelFromProfile', function() {
|
||||
|
||||
it('detects the correct compliance level', function() {
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://iiif.io/api/image/2/context.json',
|
||||
profile: 'level0'
|
||||
});
|
||||
expect(iiifInfo.getComplianceLevelFromProfile()).to.be(undefined);
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://iiif.io/api/image/2/context.json',
|
||||
profile: 'http://iiif.io/api/image/level3.json'
|
||||
});
|
||||
expect(iiifInfo.getComplianceLevelFromProfile()).to.be(undefined);
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://iiif.io/api/image/2/context.json',
|
||||
profile: 'level1'
|
||||
});
|
||||
expect(iiifInfo.getComplianceLevelFromProfile()).to.be(undefined);
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://iiif.io/api/image/2/context.json',
|
||||
profile: 'http://iiif.io/api/image/2/level2.json'
|
||||
});
|
||||
expect(iiifInfo.getComplianceLevelFromProfile()).to.be('level2');
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://iiif.io/api/image/2/context.json',
|
||||
profile: ['http://iiif.io/api/image/2/level1.json']
|
||||
});
|
||||
expect(iiifInfo.getComplianceLevelFromProfile()).to.be('level1');
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://iiif.io/api/image/3/context.json',
|
||||
profile: 'level4'
|
||||
});
|
||||
expect(iiifInfo.getComplianceLevelFromProfile()).to.be(undefined);
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://iiif.io/api/image/3/context.json',
|
||||
profile: 'http://iiif.io/api/image/3/level3.json'
|
||||
});
|
||||
expect(iiifInfo.getComplianceLevelFromProfile()).to.be(undefined);
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://iiif.io/api/image/3/context.json',
|
||||
profile: 'http://iiif.io/api/image/2/level1.json'
|
||||
});
|
||||
expect(iiifInfo.getComplianceLevelFromProfile()).to.be(undefined);
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://iiif.io/api/image/3/context.json',
|
||||
profile: 'level2'
|
||||
});
|
||||
expect(iiifInfo.getComplianceLevelFromProfile()).to.be('level2');
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://iiif.io/api/image/3/context.json',
|
||||
profile: 'http://iiif.io/api/image/3/level1.json'
|
||||
});
|
||||
expect(iiifInfo.getComplianceLevelFromProfile()).to.be('level1');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('getComplianceLevelSupportedFeatures', function() {
|
||||
|
||||
it('provides the correct features for given versions and compliance levels', function() {
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://library.stanford.edu/iiif/image-api/1.1/context.json',
|
||||
profile: 'http://library.stanford.edu/iiif/image-api/compliance.html#level0'
|
||||
});
|
||||
let level = iiifInfo.getComplianceLevelSupportedFeatures();
|
||||
expect(level.supports).to.be.empty();
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://library.stanford.edu/iiif/image-api/1.1/context.json',
|
||||
profile: 'http://library.stanford.edu/iiif/image-api/compliance.html#level1'
|
||||
});
|
||||
level = iiifInfo.getComplianceLevelSupportedFeatures();
|
||||
expect(level.supports).to.have.length(4);
|
||||
expect(level.supports).to.contain('regionByPx');
|
||||
expect(level.supports).to.contain('sizeByW');
|
||||
expect(level.supports).to.contain('sizeByH');
|
||||
expect(level.supports).to.contain('sizeByPct');
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://library.stanford.edu/iiif/image-api/1.1/context.json',
|
||||
profile: 'http://library.stanford.edu/iiif/image-api/compliance.html#level2'
|
||||
});
|
||||
level = iiifInfo.getComplianceLevelSupportedFeatures();
|
||||
expect(level.supports).to.have.length(7);
|
||||
expect(level.supports).to.contain('regionByPx');
|
||||
expect(level.supports).to.contain('regionByPct');
|
||||
expect(level.supports).to.contain('sizeByW');
|
||||
expect(level.supports).to.contain('sizeByH');
|
||||
expect(level.supports).to.contain('sizeByPct');
|
||||
expect(level.supports).to.contain('sizeByConfinedWh');
|
||||
expect(level.supports).to.contain('sizeByWh');
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://iiif.io/api/image/2/context.json',
|
||||
profile: 'http://iiif.io/api/image/2/level0.json'
|
||||
});
|
||||
level = iiifInfo.getComplianceLevelSupportedFeatures();
|
||||
expect(level.supports).to.be.empty();
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://iiif.io/api/image/2/context.json',
|
||||
profile: 'http://iiif.io/api/image/2/level1.json'
|
||||
});
|
||||
level = iiifInfo.getComplianceLevelSupportedFeatures();
|
||||
expect(level.supports).to.have.length(4);
|
||||
expect(level.supports).to.contain('regionByPx');
|
||||
expect(level.supports).to.contain('sizeByW');
|
||||
expect(level.supports).to.contain('sizeByH');
|
||||
expect(level.supports).to.contain('sizeByPct');
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://iiif.io/api/image/2/context.json',
|
||||
profile: 'http://iiif.io/api/image/2/level2.json'
|
||||
});
|
||||
level = iiifInfo.getComplianceLevelSupportedFeatures();
|
||||
expect(level.supports).to.have.length(8);
|
||||
expect(level.supports).to.contain('regionByPx');
|
||||
expect(level.supports).to.contain('regionByPct');
|
||||
expect(level.supports).to.contain('sizeByW');
|
||||
expect(level.supports).to.contain('sizeByH');
|
||||
expect(level.supports).to.contain('sizeByPct');
|
||||
expect(level.supports).to.contain('sizeByConfinedWh');
|
||||
expect(level.supports).to.contain('sizeByDistortedWh');
|
||||
expect(level.supports).to.contain('sizeByWh');
|
||||
|
||||
// TODO test version 3 compliance level features once version 3 is final
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('getTileSourceOptions', function() {
|
||||
|
||||
it('produces options from minimal information responses', function() {
|
||||
|
||||
expect(function() {
|
||||
iiifInfo.setImageInfo({
|
||||
width: 2000,
|
||||
height: 1500
|
||||
});
|
||||
iiifInfo.getTileSourceOptions();
|
||||
}).to.throwException();
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
identifier: 'id',
|
||||
profile: 'http://library.stanford.edu/iiif/image-api/compliance.html#level0'
|
||||
});
|
||||
let options = iiifInfo.getTileSourceOptions();
|
||||
|
||||
expect(options).to.be.an('object');
|
||||
expect(options).to.have.property('version', Versions.VERSION1);
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
identifier: 'identifier-version-1.0',
|
||||
width: 2000,
|
||||
height: 1500,
|
||||
profile: 'http://library.stanford.edu/iiif/image-api/compliance.html#level0'
|
||||
});
|
||||
options = iiifInfo.getTileSourceOptions();
|
||||
|
||||
expect(options).to.not.be(undefined);
|
||||
expect(options).to.not.be(null);
|
||||
expect(options).to.have.property('version', Versions.VERSION1);
|
||||
expect(options).to.have.property('size');
|
||||
expect(options.size).to.be.an('array');
|
||||
expect(options.size.length).to.be(2);
|
||||
expect(options.size[0]).to.be(2000);
|
||||
expect(options.size[1]).to.be(1500);
|
||||
expect(options.quality).to.be('native');
|
||||
expect(options.url).to.be(undefined);
|
||||
expect(options.sizes).to.be(undefined);
|
||||
expect(options.tileSize).to.be(undefined);
|
||||
expect(options.format).to.be('jpg');
|
||||
expect(options.supports).to.be.empty();
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://iiif.io/api/image/2/context.json',
|
||||
'@id': 'http://iiif.test/version2/id'
|
||||
});
|
||||
options = iiifInfo.getTileSourceOptions();
|
||||
|
||||
expect(options).to.be.an('object');
|
||||
expect(options).to.have.property('version', Versions.VERSION2);
|
||||
expect(options).to.have.property('url', 'http://iiif.test/version2/id');
|
||||
expect(options).to.have.property('format', 'jpg');
|
||||
|
||||
});
|
||||
|
||||
it('uses preferred options if applicable', function() {
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://iiif.io/api/image/2/context.json',
|
||||
'@id': 'http://iiif.test/version2/id',
|
||||
width: 2000,
|
||||
height: 1500,
|
||||
profile: ['http://iiif.io/api/image/2/level2.json']
|
||||
});
|
||||
const options = iiifInfo.getTileSourceOptions({
|
||||
quality: 'bitonal',
|
||||
format: 'png'
|
||||
});
|
||||
expect(options).to.have.property('quality', 'bitonal');
|
||||
expect(options).to.have.property('format', 'png');
|
||||
|
||||
});
|
||||
|
||||
it('ignores preferred options that are not supported', function() {
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://iiif.io/api/image/2/context.json',
|
||||
'@id': 'http://iiif.test/version2/id',
|
||||
width: 2000,
|
||||
height: 1500,
|
||||
profile: ['http://iiif.io/api/image/2/level1.json']
|
||||
});
|
||||
const options = iiifInfo.getTileSourceOptions({
|
||||
quality: 'bitonal',
|
||||
format: 'png'
|
||||
});
|
||||
expect(options).to.have.property('quality', 'default');
|
||||
expect(options).to.have.property('format', 'jpg');
|
||||
|
||||
});
|
||||
|
||||
it('combines supported features indicated by compliance level and explicitly stated in image info', function() {
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://iiif.io/api/image/2/context.json',
|
||||
'@id': 'http://iiif.test/id',
|
||||
profile: ['http://iiif.io/api/image/2/level1.json', {
|
||||
supports: ['regionByPct', 'sizeByWh']
|
||||
}]
|
||||
});
|
||||
|
||||
let options = iiifInfo.getTileSourceOptions();
|
||||
expect(options.supports).to.contain('regionByPct');
|
||||
expect(options.supports).to.contain('sizeByWh');
|
||||
expect(options.supports).to.contain('regionByPx');
|
||||
expect(options.supports).to.contain('sizeByW');
|
||||
expect(options.supports).to.contain('sizeByH');
|
||||
expect(options.supports).to.contain('sizeByPct');
|
||||
expect(options.supports).to.have.length(6);
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://iiif.io/api/image/3/context.json',
|
||||
id: 'http://iiif.test/id',
|
||||
profile: 'level1',
|
||||
extraFeatures: ['regionByPct', 'sizeByPct']
|
||||
});
|
||||
|
||||
options = iiifInfo.getTileSourceOptions();
|
||||
expect(options.supports).to.contain('regionByPct');
|
||||
expect(options.supports).to.contain('sizeByPct');
|
||||
expect(options.supports).to.contain('regionByPx');
|
||||
expect(options.supports).to.contain('regionSquare');
|
||||
expect(options.supports).to.contain('sizeByW');
|
||||
expect(options.supports).to.contain('sizeByH');
|
||||
expect(options.supports).to.have.length(6);
|
||||
|
||||
});
|
||||
|
||||
it('uses the first available scale factors and tile sizes', function() {
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://library.stanford.edu/iiif/image-api/1.1/context.json',
|
||||
'@id': 'http://iiif.test/id',
|
||||
profile: 'http://library.stanford.edu/iiif/image-api/compliance.html#level0'
|
||||
});
|
||||
let options = iiifInfo.getTileSourceOptions();
|
||||
expect(options.resolutions).to.be(undefined);
|
||||
expect(options.tileSize).to.be(undefined);
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://library.stanford.edu/iiif/image-api/1.1/context.json',
|
||||
'@id': 'http://iiif.test/id',
|
||||
profile: 'http://library.stanford.edu/iiif/image-api/compliance.html#level0',
|
||||
scale_factors: [1, 2, 4],
|
||||
tile_width: 512
|
||||
});
|
||||
options = iiifInfo.getTileSourceOptions();
|
||||
expect(options.resolutions).to.have.length(3);
|
||||
expect(options.resolutions).to.contain(1);
|
||||
expect(options.resolutions).to.contain(2);
|
||||
expect(options.resolutions).to.contain(4);
|
||||
expect(options.tileSize).to.have.length(2);
|
||||
expect(options.tileSize[0]).to.be(512);
|
||||
expect(options.tileSize[1]).to.be(512);
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://library.stanford.edu/iiif/image-api/1.1/context.json',
|
||||
'@id': 'http://iiif.test/id',
|
||||
profile: 'http://library.stanford.edu/iiif/image-api/compliance.html#level0',
|
||||
scale_factors: [1, 2, 4],
|
||||
tile_width: 512,
|
||||
tile_height: 1024
|
||||
});
|
||||
options = iiifInfo.getTileSourceOptions();
|
||||
expect(options.resolutions).to.have.length(3);
|
||||
expect(options.resolutions).to.contain(1);
|
||||
expect(options.resolutions).to.contain(2);
|
||||
expect(options.resolutions).to.contain(4);
|
||||
expect(options.tileSize).to.have.length(2);
|
||||
expect(options.tileSize[0]).to.be(512);
|
||||
expect(options.tileSize[1]).to.be(1024);
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://iiif.io/api/image/2/context.json',
|
||||
'@id': 'http://iiif.test/id',
|
||||
profile: 'http://iiif.io/api/image/2/level0.json'
|
||||
});
|
||||
options = iiifInfo.getTileSourceOptions();
|
||||
expect(options.resolutions).to.be(undefined);
|
||||
expect(options.tileSize).to.be(undefined);
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://iiif.io/api/image/2/context.json',
|
||||
'@id': 'http://iiif.test/id',
|
||||
profile: 'http://iiif.io/api/image/2/level0.json',
|
||||
tiles: [{
|
||||
scaleFactors: [1, 2, 4],
|
||||
width: 512
|
||||
},
|
||||
{
|
||||
scaleFactors: [1, 2, 4, 8, 16],
|
||||
width: 256
|
||||
}]
|
||||
});
|
||||
options = iiifInfo.getTileSourceOptions();
|
||||
expect(options.resolutions).to.have.length(3);
|
||||
expect(options.resolutions).to.contain(1);
|
||||
expect(options.resolutions).to.contain(2);
|
||||
expect(options.resolutions).to.contain(4);
|
||||
expect(options.tileSize).to.have.length(2);
|
||||
expect(options.tileSize[0]).to.be(512);
|
||||
expect(options.tileSize[1]).to.be(512);
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://iiif.io/api/image/2/context.json',
|
||||
'@id': 'http://iiif.test/id',
|
||||
profile: 'http://iiif.io/api/image/2/level0.json',
|
||||
tiles: [{
|
||||
scaleFactors: [1, 2, 4],
|
||||
width: 512,
|
||||
height: 1024
|
||||
}]
|
||||
});
|
||||
options = iiifInfo.getTileSourceOptions();
|
||||
expect(options.resolutions).to.have.length(3);
|
||||
expect(options.resolutions).to.contain(1);
|
||||
expect(options.resolutions).to.contain(2);
|
||||
expect(options.resolutions).to.contain(4);
|
||||
expect(options.tileSize).to.have.length(2);
|
||||
expect(options.tileSize[0]).to.be(512);
|
||||
expect(options.tileSize[1]).to.be(1024);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('provides each given size in sizes as OpenLayers Size', function() {
|
||||
|
||||
iiifInfo.setImageInfo({
|
||||
'@context': 'http://iiif.io/api/image/2/context.json',
|
||||
'@id': 'http://iiif.test/id',
|
||||
'sizes': [{
|
||||
width: 2000,
|
||||
height: 1000
|
||||
},
|
||||
{
|
||||
width: 1000,
|
||||
height: 500
|
||||
},
|
||||
{
|
||||
width: 500,
|
||||
height: 250
|
||||
}]
|
||||
});
|
||||
const options = iiifInfo.getTileSourceOptions();
|
||||
expect(options.sizes).to.have.length(3);
|
||||
expect(options.sizes[0]).to.have.length(2);
|
||||
expect(options.sizes[0][0]).to.be(2000);
|
||||
expect(options.sizes[0][1]).to.be(1000);
|
||||
expect(options.sizes[1]).to.have.length(2);
|
||||
expect(options.sizes[1][0]).to.be(1000);
|
||||
expect(options.sizes[1][1]).to.be(500);
|
||||
expect(options.sizes[2]).to.have.length(2);
|
||||
expect(options.sizes[2][0]).to.be(500);
|
||||
expect(options.sizes[2][1]).to.be(250);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
387
test/spec/ol/source/iiif.test.js
Normal file
387
test/spec/ol/source/iiif.test.js
Normal file
@@ -0,0 +1,387 @@
|
||||
import {DEFAULT_TILE_SIZE} from '../../../../src/ol/tilegrid/common.js';
|
||||
import IIIF from '../../../../src/ol/source/IIIF.js';
|
||||
import {Versions} from '../../../../src/ol/format/IIIFInfo.js';
|
||||
|
||||
|
||||
describe('ol.source.IIIF', function() {
|
||||
const width = 2000,
|
||||
height = 1500,
|
||||
size = [width, height],
|
||||
url = 'http://iiif.test/image-id';
|
||||
|
||||
function getMinimalSource() {
|
||||
return new IIIF({
|
||||
size: size
|
||||
});
|
||||
}
|
||||
|
||||
function getSource(additionalOptions) {
|
||||
const options = Object.assign({}, {
|
||||
size: size,
|
||||
url: url
|
||||
}, additionalOptions === undefined ? {} : additionalOptions);
|
||||
return new IIIF(options);
|
||||
}
|
||||
|
||||
describe('constructor', function() {
|
||||
|
||||
it('requires valid size option', function() {
|
||||
|
||||
expect(function() {
|
||||
new IIIF();
|
||||
}).to.throwException();
|
||||
|
||||
expect(function() {
|
||||
new IIIF({});
|
||||
}).to.throwException();
|
||||
|
||||
expect(function() {
|
||||
new IIIF({
|
||||
size: []
|
||||
});
|
||||
}).to.throwException();
|
||||
|
||||
expect(function() {
|
||||
new IIIF({
|
||||
size: 100
|
||||
});
|
||||
}).to.throwException();
|
||||
|
||||
expect(function() {
|
||||
new IIIF({
|
||||
size: [100]
|
||||
});
|
||||
}).to.throwException();
|
||||
|
||||
expect(function() {
|
||||
new IIIF({
|
||||
size: [null, 100]
|
||||
});
|
||||
}).to.throwException();
|
||||
|
||||
expect(function() {
|
||||
new IIIF({
|
||||
size: ['very wide', 100]
|
||||
});
|
||||
}).to.throwException();
|
||||
|
||||
expect(function() {
|
||||
new IIIF({
|
||||
size: [0, 100]
|
||||
});
|
||||
}).to.throwException();
|
||||
|
||||
expect(function() {
|
||||
new IIIF({
|
||||
size: [100, null]
|
||||
});
|
||||
}).to.throwException();
|
||||
|
||||
expect(function() {
|
||||
new IIIF({
|
||||
size: [100, 0]
|
||||
});
|
||||
}).to.throwException();
|
||||
|
||||
expect(function() {
|
||||
new IIIF({
|
||||
size: [100, 'not that high']
|
||||
});
|
||||
}).to.throwException();
|
||||
|
||||
expect(function() {
|
||||
new IIIF({
|
||||
size: [100, 200, 300]
|
||||
});
|
||||
}).to.throwException();
|
||||
|
||||
let source;
|
||||
|
||||
expect(function() {
|
||||
source = new IIIF({
|
||||
size: [100, 200]
|
||||
});
|
||||
}).to.not.throwException();
|
||||
|
||||
expect(source).to.be.a(IIIF);
|
||||
|
||||
expect(function() {
|
||||
getMinimalSource();
|
||||
}).to.not.throwException();
|
||||
|
||||
});
|
||||
|
||||
it('uses empty base URL, default quality, jpg format as default', function() {
|
||||
|
||||
const tileUrlFunction = getMinimalSource().getTileUrlFunction();
|
||||
expect(tileUrlFunction([0, 0, 0])).to.be('full/full/0/default.jpg');
|
||||
|
||||
});
|
||||
|
||||
it('uses native as default quality for version 1', function() {
|
||||
|
||||
const tileUrlFunction = new IIIF({
|
||||
size: size,
|
||||
version: Versions.VERSION1
|
||||
}).getTileUrlFunction();
|
||||
expect(tileUrlFunction([0, 0, 0])).to.be('full/full/0/native.jpg');
|
||||
|
||||
});
|
||||
|
||||
it('corrects non empty base URL if trailing slash is missing', function() {
|
||||
|
||||
// missing trailing slash is added
|
||||
let tileUrlFunction = getSource().getTileUrlFunction();
|
||||
expect(tileUrlFunction([0, 0, 0])).to.be('http://iiif.test/image-id/full/full/0/default.jpg');
|
||||
|
||||
// existent trailing slash isn't doubled
|
||||
tileUrlFunction = getSource({
|
||||
url: 'http://iiif.test/other-image-id/'
|
||||
}).getTileUrlFunction();
|
||||
expect(tileUrlFunction([0, 0, 0])).to.be('http://iiif.test/other-image-id/full/full/0/default.jpg');
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('tileUrlFunction', function() {
|
||||
|
||||
it('has only one resolution and one tile if no tiles, resolutions, sizes and supported features are given', function() {
|
||||
|
||||
let tileUrlFunction = getSource().getTileUrlFunction();
|
||||
expect(tileUrlFunction([0, 0, 0])).to.be('http://iiif.test/image-id/full/full/0/default.jpg');
|
||||
expect(tileUrlFunction([-1, 0, 0])).to.be(undefined);
|
||||
expect(tileUrlFunction([1, 0, 0])).to.be(undefined);
|
||||
expect(tileUrlFunction([0, 1, 0])).to.be(undefined);
|
||||
expect(tileUrlFunction([0, 0, 1])).to.be(undefined);
|
||||
|
||||
tileUrlFunction = getSource({
|
||||
version: Versions.VERSION1
|
||||
}).getTileUrlFunction();
|
||||
expect(tileUrlFunction([0, 0, 0])).to.be('http://iiif.test/image-id/full/full/0/native.jpg');
|
||||
|
||||
tileUrlFunction = getSource({
|
||||
version: Versions.VERSION3
|
||||
}).getTileUrlFunction();
|
||||
expect(tileUrlFunction([0, 0, 0])).to.be('http://iiif.test/image-id/full/max/0/default.jpg');
|
||||
|
||||
});
|
||||
|
||||
it('constructs the same number of resolutions as distinguishable sizes are given', function() {
|
||||
|
||||
let tileUrlFunction = getSource({
|
||||
sizes: [[2000, 1500], [1000, 750], [500, 375]]
|
||||
}).getTileUrlFunction();
|
||||
|
||||
expect(tileUrlFunction([0, 0, 0])).to.be('http://iiif.test/image-id/full/500,/0/default.jpg');
|
||||
expect(tileUrlFunction([1, 0, 0])).to.be('http://iiif.test/image-id/full/1000,/0/default.jpg');
|
||||
expect(tileUrlFunction([2, 0, 0])).to.be('http://iiif.test/image-id/full/full/0/default.jpg');
|
||||
expect(tileUrlFunction([3, 0, 0])).to.be(undefined);
|
||||
expect(tileUrlFunction([-1, 0, 0])).to.be(undefined);
|
||||
expect(tileUrlFunction([0, 1, 0])).to.be(undefined);
|
||||
expect(tileUrlFunction([0, 0, 1])).to.be(undefined);
|
||||
expect(tileUrlFunction([1, 1, 0])).to.be(undefined);
|
||||
expect(tileUrlFunction([1, 0, 1])).to.be(undefined);
|
||||
|
||||
tileUrlFunction = getSource({
|
||||
sizes: [[2000, 1500], [1000, 750], [500, 375]],
|
||||
version: Versions.VERSION3
|
||||
}).getTileUrlFunction();
|
||||
|
||||
expect(tileUrlFunction([0, 0, 0])).to.be('http://iiif.test/image-id/full/500,375/0/default.jpg');
|
||||
expect(tileUrlFunction([1, 0, 0])).to.be('http://iiif.test/image-id/full/1000,750/0/default.jpg');
|
||||
expect(tileUrlFunction([2, 0, 0])).to.be('http://iiif.test/image-id/full/max/0/default.jpg');
|
||||
|
||||
tileUrlFunction = getSource({
|
||||
sizes: [[2000, 1500], [1000, 749], [1000, 750], [500, 375], [500, 374]]
|
||||
}).getTileUrlFunction();
|
||||
|
||||
expect(tileUrlFunction([0, 0, 0])).to.be('http://iiif.test/image-id/full/500,/0/default.jpg');
|
||||
expect(tileUrlFunction([1, 0, 0])).to.be('http://iiif.test/image-id/full/1000,/0/default.jpg');
|
||||
expect(tileUrlFunction([2, 0, 0])).to.be('http://iiif.test/image-id/full/full/0/default.jpg');
|
||||
expect(tileUrlFunction([3, 0, 0])).to.be(undefined);
|
||||
|
||||
});
|
||||
|
||||
it('cannot provide scaled tiles without provided tilesize or supported features', function() {
|
||||
|
||||
const tileUrlFunction = getSource({
|
||||
resolutions: [16, 8, 4, 2, 1]
|
||||
}).getTileUrlFunction();
|
||||
|
||||
expect(tileUrlFunction([0, 0, 0])).to.be('http://iiif.test/image-id/full/full/0/default.jpg');
|
||||
expect(tileUrlFunction([-1, 0, 0])).to.be(undefined);
|
||||
expect(tileUrlFunction([1, 0, 0])).to.be(undefined);
|
||||
expect(tileUrlFunction([0, 1, 0])).to.be(undefined);
|
||||
expect(tileUrlFunction([0, 0, 1])).to.be(undefined);
|
||||
|
||||
});
|
||||
|
||||
it('provides canonical tile URLs for all necessary resolutions if only a tileSize exists', function() {
|
||||
|
||||
let tileUrlFunction = getSource({
|
||||
tileSize: 512
|
||||
}).getTileUrlFunction();
|
||||
|
||||
expect(tileUrlFunction([0, 0, 0])).to.be('http://iiif.test/image-id/full/500,/0/default.jpg');
|
||||
expect(tileUrlFunction([-1, 0, 0])).to.be(undefined);
|
||||
expect(tileUrlFunction([0, 1, 0])).to.be(undefined);
|
||||
expect(tileUrlFunction([0, 0, 1])).to.be(undefined);
|
||||
expect(tileUrlFunction([1, 0, 0])).to.be('http://iiif.test/image-id/0,0,1024,1024/512,/0/default.jpg');
|
||||
expect(tileUrlFunction([1, 1, 0])).to.be('http://iiif.test/image-id/1024,0,976,1024/488,/0/default.jpg');
|
||||
expect(tileUrlFunction([1, 0, 1])).to.be('http://iiif.test/image-id/0,1024,1024,476/512,/0/default.jpg');
|
||||
expect(tileUrlFunction([1, 1, 1])).to.be('http://iiif.test/image-id/1024,1024,976,476/488,/0/default.jpg');
|
||||
expect(tileUrlFunction([2, 0, 0])).to.be('http://iiif.test/image-id/0,0,512,512/512,/0/default.jpg');
|
||||
expect(tileUrlFunction([2, 3, 0])).to.be('http://iiif.test/image-id/1536,0,464,512/464,/0/default.jpg');
|
||||
expect(tileUrlFunction([2, 0, 2])).to.be('http://iiif.test/image-id/0,1024,512,476/512,/0/default.jpg');
|
||||
expect(tileUrlFunction([2, 3, 2])).to.be('http://iiif.test/image-id/1536,1024,464,476/464,/0/default.jpg');
|
||||
expect(tileUrlFunction([3, 0, 0])).to.be(undefined);
|
||||
|
||||
tileUrlFunction = getSource({
|
||||
tileSize: 512,
|
||||
version: Versions.VERSION3
|
||||
}).getTileUrlFunction();
|
||||
|
||||
expect(tileUrlFunction([0, 0, 0])).to.be('http://iiif.test/image-id/full/500,375/0/default.jpg');
|
||||
expect(tileUrlFunction([1, 0, 0])).to.be('http://iiif.test/image-id/0,0,1024,1024/512,512/0/default.jpg');
|
||||
expect(tileUrlFunction([1, 1, 0])).to.be('http://iiif.test/image-id/1024,0,976,1024/488,512/0/default.jpg');
|
||||
expect(tileUrlFunction([1, 0, 1])).to.be('http://iiif.test/image-id/0,1024,1024,476/512,238/0/default.jpg');
|
||||
expect(tileUrlFunction([1, 1, 1])).to.be('http://iiif.test/image-id/1024,1024,976,476/488,238/0/default.jpg');
|
||||
expect(tileUrlFunction([2, 0, 0])).to.be('http://iiif.test/image-id/0,0,512,512/512,512/0/default.jpg');
|
||||
expect(tileUrlFunction([2, 3, 0])).to.be('http://iiif.test/image-id/1536,0,464,512/464,512/0/default.jpg');
|
||||
expect(tileUrlFunction([2, 0, 2])).to.be('http://iiif.test/image-id/0,1024,512,476/512,476/0/default.jpg');
|
||||
expect(tileUrlFunction([2, 3, 2])).to.be('http://iiif.test/image-id/1536,1024,464,476/464,476/0/default.jpg');
|
||||
|
||||
});
|
||||
|
||||
it('provides canonical tile URLs for all provided resolutions if a tileSize also exists', function() {
|
||||
|
||||
const tileUrlFunction = getSource({
|
||||
tileSize: 512,
|
||||
resolutions: [8, 4, 2, 1]
|
||||
}).getTileUrlFunction();
|
||||
|
||||
expect(tileUrlFunction([0, 0, 0])).to.be('http://iiif.test/image-id/full/250,/0/default.jpg');
|
||||
expect(tileUrlFunction([1, 0, 0])).to.be('http://iiif.test/image-id/full/500,/0/default.jpg');
|
||||
expect(tileUrlFunction([2, 0, 0])).to.be('http://iiif.test/image-id/0,0,1024,1024/512,/0/default.jpg');
|
||||
expect(tileUrlFunction([2, 1, 0])).to.be('http://iiif.test/image-id/1024,0,976,1024/488,/0/default.jpg');
|
||||
expect(tileUrlFunction([2, 0, 1])).to.be('http://iiif.test/image-id/0,1024,1024,476/512,/0/default.jpg');
|
||||
expect(tileUrlFunction([2, 1, 1])).to.be('http://iiif.test/image-id/1024,1024,976,476/488,/0/default.jpg');
|
||||
expect(tileUrlFunction([3, 0, 0])).to.be('http://iiif.test/image-id/0,0,512,512/512,/0/default.jpg');
|
||||
expect(tileUrlFunction([3, 3, 0])).to.be('http://iiif.test/image-id/1536,0,464,512/464,/0/default.jpg');
|
||||
expect(tileUrlFunction([3, 0, 2])).to.be('http://iiif.test/image-id/0,1024,512,476/512,/0/default.jpg');
|
||||
expect(tileUrlFunction([3, 3, 2])).to.be('http://iiif.test/image-id/1536,1024,464,476/464,/0/default.jpg');
|
||||
expect(tileUrlFunction([4, 0, 0])).to.be(undefined);
|
||||
|
||||
});
|
||||
|
||||
it('supports non square tiles', function() {
|
||||
|
||||
let tileUrlFunction = getSource({
|
||||
tileSize: [1024, 512]
|
||||
}).getTileUrlFunction();
|
||||
|
||||
expect(tileUrlFunction([0, 0, 0])).to.be('http://iiif.test/image-id/full/500,/0/default.jpg');
|
||||
expect(tileUrlFunction([1, 0, 0])).to.be('http://iiif.test/image-id/0,0,2000,1024/1000,/0/default.jpg');
|
||||
expect(tileUrlFunction([1, 0, 1])).to.be('http://iiif.test/image-id/0,1024,2000,476/1000,/0/default.jpg');
|
||||
expect(tileUrlFunction([2, 0, 0])).to.be('http://iiif.test/image-id/0,0,1024,512/1024,/0/default.jpg');
|
||||
expect(tileUrlFunction([2, 1, 0])).to.be('http://iiif.test/image-id/1024,0,976,512/976,/0/default.jpg');
|
||||
expect(tileUrlFunction([2, 0, 2])).to.be('http://iiif.test/image-id/0,1024,1024,476/1024,/0/default.jpg');
|
||||
expect(tileUrlFunction([2, 1, 2])).to.be('http://iiif.test/image-id/1024,1024,976,476/976,/0/default.jpg');
|
||||
expect(tileUrlFunction([3, 0, 0])).to.be(undefined);
|
||||
|
||||
tileUrlFunction = getSource({
|
||||
tileSize: [1024, 512],
|
||||
version: Versions.VERSION3
|
||||
}).getTileUrlFunction();
|
||||
|
||||
expect(tileUrlFunction([0, 0, 0])).to.be('http://iiif.test/image-id/full/500,375/0/default.jpg');
|
||||
expect(tileUrlFunction([2, 0, 0])).to.be('http://iiif.test/image-id/0,0,1024,512/1024,512/0/default.jpg');
|
||||
|
||||
});
|
||||
|
||||
it('provides tile URLs with default tile size if sufficient supported features are provided', function() {
|
||||
|
||||
let tileUrlFunction = getSource({
|
||||
supports: ['regionByPx', 'sizeByW']
|
||||
}).getTileUrlFunction();
|
||||
|
||||
const maxZoom = Math.ceil(Math.log2(width / DEFAULT_TILE_SIZE));
|
||||
|
||||
expect(tileUrlFunction([maxZoom, 0, 0])).to.be('http://iiif.test/image-id/0,0,' + DEFAULT_TILE_SIZE + ',' + DEFAULT_TILE_SIZE + '/' + DEFAULT_TILE_SIZE + ',/0/default.jpg');
|
||||
expect(tileUrlFunction([maxZoom + 1, 0, 0])).to.be(undefined);
|
||||
|
||||
tileUrlFunction = getSource({
|
||||
supports: ['regionByPx', 'sizeByH']
|
||||
}).getTileUrlFunction();
|
||||
|
||||
expect(tileUrlFunction([maxZoom, 0, 0])).to.be('http://iiif.test/image-id/0,0,' + DEFAULT_TILE_SIZE + ',' + DEFAULT_TILE_SIZE + '/,' + DEFAULT_TILE_SIZE + '/0/default.jpg');
|
||||
expect(tileUrlFunction([maxZoom + 1, 0, 0])).to.be(undefined);
|
||||
|
||||
tileUrlFunction = getSource({
|
||||
supports: ['regionByPx', 'sizeByWh']
|
||||
}).getTileUrlFunction();
|
||||
|
||||
expect(tileUrlFunction([maxZoom, 0, 0])).to.be('http://iiif.test/image-id/0,0,' + DEFAULT_TILE_SIZE + ',' + DEFAULT_TILE_SIZE + '/' + DEFAULT_TILE_SIZE + ',' + DEFAULT_TILE_SIZE + '/0/default.jpg');
|
||||
expect(tileUrlFunction([maxZoom + 1, 0, 0])).to.be(undefined);
|
||||
|
||||
tileUrlFunction = getSource({
|
||||
supports: ['regionByPct', 'sizeByPct']
|
||||
}).getTileUrlFunction();
|
||||
|
||||
const tileWPct = (DEFAULT_TILE_SIZE / width * 100).toLocaleString('en', {maximumFractionDigits: 10}),
|
||||
tileHPct = (DEFAULT_TILE_SIZE / height * 100).toLocaleString('en', {maximumFractionDigits: 10});
|
||||
|
||||
expect(tileUrlFunction([maxZoom, 0, 0])).to.be('http://iiif.test/image-id/pct:0,0,' + tileWPct + ',' + tileHPct + '/pct:100/0/default.jpg');
|
||||
expect(tileUrlFunction([maxZoom + 1, 0, 0])).to.be(undefined);
|
||||
|
||||
});
|
||||
|
||||
it('prefers canonical tile URLs', function() {
|
||||
|
||||
let tileUrlFunction = getSource({
|
||||
tileSize: 512,
|
||||
supports: ['regionByPx', 'regionByPct', 'sizeByW', 'sizeByH', 'sizeByWh', 'sizeByPct']
|
||||
}).getTileUrlFunction();
|
||||
|
||||
expect(tileUrlFunction([2, 0, 0])).to.be('http://iiif.test/image-id/0,0,512,512/512,/0/default.jpg');
|
||||
|
||||
tileUrlFunction = getSource({
|
||||
tileSize: 512,
|
||||
version: Versions.VERSION3,
|
||||
supports: ['regionByPx', 'regionByPct', 'sizeByW', 'sizeByH', 'sizeByWh', 'sizeByPct']
|
||||
}).getTileUrlFunction();
|
||||
|
||||
expect(tileUrlFunction([2, 0, 0])).to.be('http://iiif.test/image-id/0,0,512,512/512,512/0/default.jpg');
|
||||
|
||||
});
|
||||
|
||||
|
||||
it('provides correct tile URLs for percentage URL parameter values', function() {
|
||||
|
||||
const tileUrlFunction = getSource({
|
||||
tileSize: 512,
|
||||
supports: ['regionByPct', 'sizeByPct']
|
||||
}).getTileUrlFunction();
|
||||
|
||||
expect(tileUrlFunction([0, 0, 0])).to.be('http://iiif.test/image-id/full/pct:25/0/default.jpg');
|
||||
expect(tileUrlFunction([-1, 0, 0])).to.be(undefined);
|
||||
expect(tileUrlFunction([0, 1, 0])).to.be(undefined);
|
||||
expect(tileUrlFunction([0, 0, 1])).to.be(undefined);
|
||||
|
||||
expect(tileUrlFunction([1, 0, 0])).to.be('http://iiif.test/image-id/pct:0,0,51.2,68.2666666667/pct:50/0/default.jpg');
|
||||
expect(tileUrlFunction([1, 1, 0])).to.be('http://iiif.test/image-id/pct:51.2,0,48.8,68.2666666667/pct:50/0/default.jpg');
|
||||
expect(tileUrlFunction([1, 0, 1])).to.be('http://iiif.test/image-id/pct:0,68.2666666667,51.2,31.7333333333/pct:50/0/default.jpg');
|
||||
expect(tileUrlFunction([1, 1, 1])).to.be('http://iiif.test/image-id/pct:51.2,68.2666666667,48.8,31.7333333333/pct:50/0/default.jpg');
|
||||
|
||||
expect(tileUrlFunction([2, 0, 0])).to.be('http://iiif.test/image-id/pct:0,0,25.6,34.1333333333/pct:100/0/default.jpg');
|
||||
expect(tileUrlFunction([2, 3, 0])).to.be('http://iiif.test/image-id/pct:76.8,0,23.2,34.1333333333/pct:100/0/default.jpg');
|
||||
expect(tileUrlFunction([2, 0, 2])).to.be('http://iiif.test/image-id/pct:0,68.2666666667,25.6,31.7333333333/pct:100/0/default.jpg');
|
||||
expect(tileUrlFunction([2, 3, 2])).to.be('http://iiif.test/image-id/pct:76.8,68.2666666667,23.2,31.7333333333/pct:100/0/default.jpg');
|
||||
expect(tileUrlFunction([3, 0, 0])).to.be(undefined);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user