From 576c00b37dd0bc8ae6487ba33d60286ad07e669d Mon Sep 17 00:00:00 2001
From: thatcher
Date: Fri, 8 Feb 2013 09:21:28 -0500
Subject: [PATCH] adding support for several new tile sources including iiif,
osm and tms (osm and tms are thanks to seajax-utils project)
---
build.properties | 2 +-
build.xml | 5 +
openseadragon.js | 521 ++++++++++++++++++++++++++++++++++++++-
src/drawer.js | 18 +-
src/viewer.js | 3 +-
www/base.html | 13 +-
www/tilesource-iiif.html | 170 +++++++++++--
7 files changed, 693 insertions(+), 39 deletions(-)
diff --git a/build.properties b/build.properties
index 7c375c5b..d629ef5c 100644
--- a/build.properties
+++ b/build.properties
@@ -6,7 +6,7 @@
PROJECT: openseadragon
BUILD_MAJOR: 0
BUILD_MINOR: 9
-BUILD_ID: 98
+BUILD_ID: 101
BUILD: ${PROJECT}.${BUILD_MAJOR}.${BUILD_MINOR}.${BUILD_ID}
VERSION: ${BUILD_MAJOR}.${BUILD_MINOR}.${BUILD_ID}
diff --git a/build.xml b/build.xml
index 0a308132..ab826346 100644
--- a/build.xml
+++ b/build.xml
@@ -31,6 +31,9 @@
+
+
+
@@ -63,6 +66,8 @@
+
+
diff --git a/openseadragon.js b/openseadragon.js
index 9e7df3df..7bac1e01 100644
--- a/openseadragon.js
+++ b/openseadragon.js
@@ -1,7 +1,7 @@
/*globals OpenSeadragon */
/**
- * @version OpenSeadragon 0.9.98
+ * @version OpenSeadragon 0.9.101
*
* @fileOverview
*
@@ -3410,7 +3410,8 @@ $.Viewer = function( options ) {
//These are originally not part options but declared as members
//in initialize. Its still considered idiomatic to put them here
source: null,
- drawer: null,
+ drawer: null,
+ drawers: [],
viewport: null,
navigator: null,
@@ -5987,7 +5988,503 @@ function configureFromObject( tileSource, configuration ){
};
}( OpenSeadragon ));
-/*globals OpenSeadragon */
+(function( $ ){
+
+/**
+ * A client implementation of the International Image Interoperability
+ * Format: Image API Draft 0.2 - Please read more about the specification
+ * at
+ *
+ * The getTileUrl implementation is based on the gist from:
+ * https://gist.github.com/jpstroop/4624253
+ *
+ * @class
+ * @name OpenSeadragon.IIIFTileSource
+ * @see http://library.stanford.edu/iiif/image-api/
+ */
+$.IIIFTileSource = function( options ){
+
+ $.extend( true, this, options );
+
+ if( !(this.height && this.width && this.identifier && this.tilesUrl ) ){
+ throw new Error('IIIF required parameters not provided.');
+ }
+
+ //TODO: at this point the base tile source implementation assumes
+ // a tile is a square and so only has one property tileSize
+ // to store it. It may be possible to make tileSize a vector
+ // OpenSeadraon.Point but would require careful implementation
+ // to preserve backward compatibility.
+ options.tileSize = this.tile_width;
+
+ options.maxLevel = options.maxLevel ? options.maxLevel : Number(
+ Math.ceil( Math.log( Math.max( this.width, this.height ), 2 ) )
+ );
+
+ $.TileSource.apply( this, [ options ] );
+};
+
+$.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, {
+
+
+ /**
+ * Determine if the data and/or url imply the image service is supported by
+ * this tile source.
+ * @function
+ * @name OpenSeadragon.IIIFTileSource.prototype.supports
+ * @param {Object|Array} data
+ * @param {String} optional - url
+ */
+ supports: function( data, url ){
+ return (
+ data.ns &&
+ "http://library.stanford.edu/iiif/image-api/ns/" == data.ns
+ ) || (
+ data.profile && (
+ "http://library.stanford.edu/iiif/image-api/compliance.html#level1" == data.profile ||
+ "http://library.stanford.edu/iiif/image-api/compliance.html#level2" == data.profile ||
+ "http://library.stanford.edu/iiif/image-api/compliance.html#level3" == data.profile ||
+ "http://library.stanford.edu/iiif/image-api/compliance.html" == data.profile
+ )
+ ) || (
+ data.documentElement &&
+ "info" == data.documentElement.tagName &&
+ "http://library.stanford.edu/iiif/image-api/ns/" ==
+ data.documentElement.namespaceURI
+ );
+ },
+
+ /**
+ *
+ * @function
+ * @name OpenSeadragon.IIIFTileSource.prototype.configure
+ * @param {Object|XMLDocument} data - the raw configuration
+ * @param {String} url - the url the data was retreived from if any.
+ * @return {Object} options - A dictionary of keyword arguments sufficient
+ * to configure this tile source via it's constructor.
+ */
+ configure: function( data, url ){
+ var service,
+ identifier,
+ options,
+ host;
+
+ if( !$.isPlainObject(data) ){
+
+ options = configureFromXML( this, data );
+
+ }else{
+
+ options = configureFromObject( this, data );
+ }
+
+ if( url && !options.tilesUrl ){
+ service = url.split('/');
+ service.pop(); //info.json or info.xml
+ service = service.join('/');
+ if( !( 'http' == url.substring( 0, 4 ) ) ){
+ host = location.protocol + '//' + location.host;
+ service = host + service;
+ }
+ options.tilesUrl = service.replace(
+ data.identifier,
+ ''
+ );
+ }
+
+ return options;
+ },
+
+ /**
+ * Responsible for retreiving the url which will return an image for the
+ * region speified by the given x, y, and level components.
+ * @function
+ * @name OpenSeadragon.IIIFTileSource.prototype.getTileUrl
+ * @param {Number} level - z index
+ * @param {Number} x
+ * @param {Number} y
+ * @throws {Error}
+ */
+ getTileUrl: function( level, x, y ){
+
+ //# constants
+ var IIIF_ROTATION = '0',
+ IIIF_QUALITY = 'native.jpg',
+
+ //## get the scale (level as a decimal)
+ scale = Math.pow( 0.5, this.maxLevel - level ),
+
+ //## get iiif size
+ iiif_size = 'pct:' + ( scale * 100 ),
+
+ //# image dimensions at this level
+ level_width = Math.ceil( this.width * scale ),
+ level_height = Math.ceil( this.height * scale ),
+
+ //## iiif region
+ iiif_tile_size_width = Math.ceil( this.tileSize / scale ),
+ iiif_tile_size_height = Math.ceil( this.tileSize / scale ),
+ iiif_region,
+ iiif_tile_x,
+ iiif_tile_y,
+ iiif_tile_w,
+ iiif_tile_h;
+
+
+ if ( level_width < this.tile_width || level_height < this.tile_height ){
+ iiif_region = 'full';
+ } else {
+ iiif_tile_x = x * iiif_tile_size_width;
+ iiif_tile_y = y * iiif_tile_size_height;
+ iiif_tile_w = Math.min( iiif_tile_size_width, this.width - iiif_tile_x );
+ iiif_tile_h = Math.min( iiif_tile_size_height, this.height - iiif_tile_y );
+ iiif_region = [ iiif_tile_x, iiif_tile_y, iiif_tile_w, iiif_tile_h ].join(',');
+ }
+
+ return [
+ this.tilesUrl,
+ this.identifier,
+ iiif_region,
+ iiif_size,
+ IIIF_ROTATION,
+ IIIF_QUALITY
+ ].join('/');
+ }
+
+
+});
+
+/**
+ * @private
+ * @inner
+ * @function
+ *
+
+
+ 1E34750D-38DB-4825-A38A-B60A345E591C
+ 6000
+ 4000
+
+ 1
+ 2
+ 4
+
+ 1024
+ 1024
+
+ jpg
+ png
+
+
+ native
+ grey
+
+
+ */
+function configureFromXml( tileSource, xmlDoc ){
+ var configuration = {};
+
+ //parse the xml
+ if ( !xmlDoc || !xmlDoc.documentElement ) {
+ throw new Error( $.getString( "Errors.Xml" ) );
+ }
+
+ var root = xmlDoc.documentElement,
+ rootName = root.tagName,
+ configuration = null,
+ scale_factors,
+ formats,
+ qualities,
+ i;
+
+ if ( rootName == "info" ) {
+
+ try {
+
+ configuration = {
+ "ns": root.namespaceURI,
+ "identifier": root.getElement('identifier').innerHTML,
+ "width": root.getElement('width').innerHTML,
+ "height": root.getElement('height').innerHTML,
+ "scale_factors": null,
+ "tile_width": root.getElement('tile_width').innerHTML,
+ "tile_height": root.getElement('tile_height').innerHTML,
+ "formats": [ "jpg", "png" ],
+ "quality": [ "native", "grey" ]
+ };
+
+ scale_factors = root.getElement('scale_factors');
+ if( scale_factors ){
+ scale_factors = scale_factors.getElementsByTagName('scale_factor');
+ configuration.scale_factors = [];
+ for( i = 0; i < scale_factors.length; i++ ){
+ configuration.scale_factors.push(
+ scale_factors[ i ].innerHTML
+ );
+ }
+ }
+
+ formats = root.getElement('formats');
+ if( formats ){
+ formats = formats.getElementsByTagName('format');
+ configuration.formats = [];
+ for( i = 0; i < formats.length; i++ ){
+ configuration.formats.push(
+ formats[ i ].innerHTML
+ );
+ }
+ }
+
+ qualities = root.getElement('qualities');
+ if( qualities ){
+ qualities = formats.getElementsByTagName('quality');
+ configuration.quality = [];
+ for( i = 0; i < qualities.length; i++ ){
+ configuration.quality.push(
+ qualities[ i ].innerHTML
+ );
+ }
+ }
+
+ return configureFromObject( tileSource, configuration );
+
+ } catch ( e ) {
+ throw (e instanceof Error) ?
+ e :
+ new Error( $.getString("Errors.IIIF") );
+ }
+ }
+
+ throw new Error( $.getString( "Errors.IIIF" ) );
+
+};
+
+
+/**
+ * @private
+ * @inner
+ * @function
+ *
+ {
+ "profile" : "http://library.stanford.edu/iiif/image-api/compliance.html#level1",
+ "identifier" : "1E34750D-38DB-4825-A38A-B60A345E591C",
+ "width" : 6000,
+ "height" : 4000,
+ "scale_factors" : [ 1, 2, 4 ],
+ "tile_width" : 1024,
+ "tile_height" : 1024,
+ "formats" : [ "jpg", "png" ],
+ "quality" : [ "native", "grey" ]
+ }
+ */
+function configureFromObject( tileSource, configuration ){
+ return configuration;
+};
+
+}( OpenSeadragon ));(function( $ ){
+
+/**
+ * A tilesource implementation for OpenStreetMap. Adopted from Rainer Simon
+ * project http://github.com/rsimon/seajax-utils.
+ *
+ * Note 1. Zoomlevels. Deep Zoom and OSM define zoom levels differently. In Deep
+ * Zoom, level 0 equals an image of 1x1 pixels. In OSM, level 0 equals an image of
+ * 256x256 levels (see http://gasi.ch/blog/inside-deep-zoom-2). I.e. there is a
+ * difference of log2(256)=8 levels.
+ *
+ * Note 2. Image dimension. According to the OSM Wiki
+ * (http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Zoom_levels)
+ * the highest Mapnik zoom level has 256.144x256.144 tiles, with a 256x256
+ * pixel size. I.e. the Deep Zoom image dimension is 65.572.864x65.572.864
+ * pixels.
+ *
+ * @class
+ * @extends OpenSeadragon.TileSource
+ * @param {Number|Object} width - the pixel width of the image or the idiomatic
+ * options object which is used instead of positional arguments.
+ * @param {Number} height
+ * @param {Number} tileSize
+ * @param {Number} tileOverlap
+ * @param {String} tilesUrl
+ */
+$.OsmTileSource = function( width, height, tileSize, tileOverlap, tilesUrl ) {
+ var options;
+
+ if( $.isPlainObject( width ) ){
+ options = width;
+ }else{
+ options = {
+ width: arguments[0],
+ height: arguments[1],
+ tileSize: arguments[2],
+ tileOverlap: arguments[3],
+ tilesUrl: arguments[4]
+ };
+ }
+ //apply default setting for standard public OpenStreatMaps service
+ //but allow them to be specified so fliks can host there own instance
+ //or apply against other services supportting the same standard
+ if( !options.width || !options.height ){
+ options.width = 65572864;
+ options.height = 65572864;
+ }
+ if( !options.tileSize ){
+ options.tileSize = 256;
+ options.tileOverlap = 0;
+ }
+ if( !options.tilesUrl ){
+ options.tilesUrl = "http://tile.openstreetmap.org/";
+ }
+ options.minLevel = 8;
+
+ $.TileSource.apply( this, [ options ] );
+
+};
+
+$.extend( $.OsmTileSource.prototype, $.TileSource.prototype, {
+
+
+ /**
+ * Determine if the data and/or url imply the image service is supported by
+ * this tile source.
+ * @function
+ * @name OpenSeadragon.DziTileSource.prototype.supports
+ * @param {Object|Array} data
+ * @param {String} optional - url
+ */
+ supports: function( data, url ){
+ return (
+ data.type &&
+ "openstreetmaps" == data.type
+ )
+ },
+
+ /**
+ *
+ * @function
+ * @name OpenSeadragon.OsmTileSource.prototype.configure
+ * @param {Object} data - the raw configuration
+ * @param {String} url - the url the data was retreived from if any.
+ * @return {Object} options - A dictionary of keyword arguments sufficient
+ * to configure this tile sources constructor.
+ */
+ configure: function( data, url ){
+ return data;
+ },
+
+
+ /**
+ * @function
+ * @name OpenSeadragon.OsmTileSource.prototype.getTileUrl
+ * @param {Number} level
+ * @param {Number} x
+ * @param {Number} y
+ */
+ getTileUrl: function( level, x, y ) {
+ return this.tilesUrl + (level - 8) + "/" + x + "/" + y + ".png";
+ }
+});
+
+
+}( OpenSeadragon ));
+(function( $ ){
+
+/**
+ * A tilesource implementation for Tiled Map Services (TMS). Adopted from Rainer Simon
+ * project http://github.com/rsimon/seajax-utils. TMS tile
+ * scheme ( [ as supported by OpenLayers ] is described here
+ * ( http://openlayers.org/dev/examples/tms.html ) )
+ *
+ * @class
+ * @extends OpenSeadragon.TileSource
+ * @param {Number|Object} width - the pixel width of the image or the idiomatic
+ * options object which is used instead of positional arguments.
+ * @param {Number} height
+ * @param {Number} tileSize
+ * @param {Number} tileOverlap
+ * @param {String} tilesUrl
+ */
+$.TmsTileSource = function( width, height, tileSize, tileOverlap, tilesUrl ) {
+ var options;
+
+ if( $.isPlainObject( width ) ){
+ options = width;
+ }else{
+ options = {
+ width: arguments[0],
+ height: arguments[1],
+ tileSize: arguments[2],
+ tileOverlap: arguments[3],
+ tilesUrl: arguments[4]
+ };
+ }
+ // TMS has integer multiples of 256 for width/height and adds buffer
+ // if necessary -> account for this!
+ var bufferedWidth = Math.ceil(options.width / 256) * 256,
+ bufferedHeight = Math.ceil(options.height / 256) * 256,
+ max;
+
+ // Compute number of zoomlevels in this tileset
+ if (bufferedWidth > bufferedHeight) {
+ max = bufferedWidth / 256;
+ } else {
+ max = bufferedHeight / 256;
+ }
+ options.maxLevel = Math.ceil(Math.log(max)/Math.log(2)) - 1;
+ options.tileSize = 256;
+ options.width = bufferedWidth;
+ options.height = bufferedHeight;
+
+ $.TileSource.apply( this, [ options ] );
+
+};
+
+$.extend( $.TmsTileSource.prototype, $.TileSource.prototype, {
+
+
+ /**
+ * Determine if the data and/or url imply the image service is supported by
+ * this tile source.
+ * @function
+ * @name OpenSeadragon.TmsTileSource.prototype.supports
+ * @param {Object|Array} data
+ * @param {String} optional - url
+ */
+ supports: function( data, url ){
+ return ( data.type && "tiledmapservice" == data.type );
+ },
+
+ /**
+ *
+ * @function
+ * @name OpenSeadragon.TmsTileSource.prototype.configure
+ * @param {Object} data - the raw configuration
+ * @param {String} url - the url the data was retreived from if any.
+ * @return {Object} options - A dictionary of keyword arguments sufficient
+ * to configure this tile sources constructor.
+ */
+ configure: function( data, url ){
+ return data;
+ },
+
+
+ /**
+ * @function
+ * @name OpenSeadragon.TmsTileSource.prototype.getTileUrl
+ * @param {Number} level
+ * @param {Number} x
+ * @param {Number} y
+ */
+ getTileUrl: function( level, x, y ) {
+ // Convert from Deep Zoom definition to TMS zoom definition
+ var yTiles = this.getNumTiles( level ).y - 1;
+
+ return this.tilesUrl + level + "/" + x + "/" + (yTiles - y) + ".png";
+ }
+});
+
+
+}( OpenSeadragon ));/*globals OpenSeadragon */
(function( $ ){
@@ -8343,7 +8840,7 @@ function updateViewport( drawer ) {
Math.log( 2 )
)
),
- highestLevel = Math.min(
+ highestLevel = Math.max(
Math.abs(drawer.source.maxLevel),
Math.abs(Math.floor(
Math.log( zeroRatioC / drawer.minPixelRatio ) /
@@ -8366,11 +8863,10 @@ function updateViewport( drawer ) {
//TODO
drawer.canvas.innerHTML = "";
if ( USE_CANVAS ) {
- if( drawer.canvas.width != viewportSize.x ||
- drawer.canvas.height != viewportSize.y
- ){
- drawer.canvas.width = viewportSize.x;
- drawer.canvas.height = viewportSize.y;
+ if( drawer.canvas.width != viewportSize.x ||
+ drawer.canvas.height != viewportSize.y ){
+ drawer.canvas.width = viewportSize.x;
+ drawer.canvas.height = viewportSize.y;
}
drawer.context.clearRect( 0, 0, viewportSize.x, viewportSize.y );
}
@@ -8647,8 +9143,10 @@ function onTileLoad( drawer, tile, time, image ) {
return;
} else if ( !image && !drawer.viewport.collectionMode ) {
$.console.log( "Tile %s failed to load: %s", tile, tile.url );
- tile.exists = false;
- return;
+ if( !drawer.debugMode ){
+ tile.exists = false;
+ return;
+ }
} else if ( time < drawer.lastResetTime ) {
$.console.log( "Ignoring tile %s loaded before reset: %s", tile, tile.url );
return;
@@ -8657,6 +9155,7 @@ function onTileLoad( drawer, tile, time, image ) {
tile.loaded = true;
tile.image = image;
+
insertionIndex = drawer.tilesLoaded.length;
if ( drawer.tilesLoaded.length >= drawer.maxImageCacheCount ) {
diff --git a/src/drawer.js b/src/drawer.js
index 7331f07b..99f7bcd1 100644
--- a/src/drawer.js
+++ b/src/drawer.js
@@ -365,7 +365,7 @@ function updateViewport( drawer ) {
Math.log( 2 )
)
),
- highestLevel = Math.min(
+ highestLevel = Math.max(
Math.abs(drawer.source.maxLevel),
Math.abs(Math.floor(
Math.log( zeroRatioC / drawer.minPixelRatio ) /
@@ -388,11 +388,10 @@ function updateViewport( drawer ) {
//TODO
drawer.canvas.innerHTML = "";
if ( USE_CANVAS ) {
- if( drawer.canvas.width != viewportSize.x ||
- drawer.canvas.height != viewportSize.y
- ){
- drawer.canvas.width = viewportSize.x;
- drawer.canvas.height = viewportSize.y;
+ if( drawer.canvas.width != viewportSize.x ||
+ drawer.canvas.height != viewportSize.y ){
+ drawer.canvas.width = viewportSize.x;
+ drawer.canvas.height = viewportSize.y;
}
drawer.context.clearRect( 0, 0, viewportSize.x, viewportSize.y );
}
@@ -669,8 +668,10 @@ function onTileLoad( drawer, tile, time, image ) {
return;
} else if ( !image && !drawer.viewport.collectionMode ) {
$.console.log( "Tile %s failed to load: %s", tile, tile.url );
- tile.exists = false;
- return;
+ if( !drawer.debugMode ){
+ tile.exists = false;
+ return;
+ }
} else if ( time < drawer.lastResetTime ) {
$.console.log( "Ignoring tile %s loaded before reset: %s", tile, tile.url );
return;
@@ -679,6 +680,7 @@ function onTileLoad( drawer, tile, time, image ) {
tile.loaded = true;
tile.image = image;
+
insertionIndex = drawer.tilesLoaded.length;
if ( drawer.tilesLoaded.length >= drawer.maxImageCacheCount ) {
diff --git a/src/viewer.js b/src/viewer.js
index 28d5a05f..93933008 100644
--- a/src/viewer.js
+++ b/src/viewer.js
@@ -91,7 +91,8 @@ $.Viewer = function( options ) {
//These are originally not part options but declared as members
//in initialize. Its still considered idiomatic to put them here
source: null,
- drawer: null,
+ drawer: null,
+ drawers: [],
viewport: null,
navigator: null,
diff --git a/www/base.html b/www/base.html
index 9c61d96b..ebe3ec25 100644
--- a/www/base.html
+++ b/www/base.html
@@ -67,11 +67,20 @@
via XMLHTTPRequest, JSONP, as well as direct inline configuration.
+ -
+ Legacy Image Pyramids
+
+
-
DZI (Deep Zoom Images)
- -
- Legacy Image Pyramids
+
-
+ OSM (Open Street Maps)
+
+ -
+ TMS (Tiled Map Service)
-
Custom Tile Sources
diff --git a/www/tilesource-iiif.html b/www/tilesource-iiif.html
index 94981000..4bad843b 100644
--- a/www/tilesource-iiif.html
+++ b/www/tilesource-iiif.html
@@ -1,16 +1,154 @@
-
\ No newline at end of file
+
+ example: iiif tiles support
+
+
+ The International Image Interoperability Format is described formally here
+ http://library.stanford.edu/iiif/image-api/.
+
+
+ The IIIF API specifies a web service that returns an image in response to a standard http or https request.
+ The URL can specify the region, size, rotation, quality characteristics and format of the requested image. A
+ URL can also be constructed to request basic technical information about the image to support client
+ applications. The IIIF API was conceived of to facilitate systematic reuse of image resources in digital
+ image repositories maintained by cultural heritage organizations. The API could be adopted by any image
+ repository or service, and can be used to retrieve static images in response to a properly constructed URL.
+
+
+ OpenSeadragon has added support for IIIF thanks to several of its specifications authors
+ as well as the gist provided by Jon Stroop at
+ this gist.
+ The tile source reference in this page are not meant for general public consumption
+ without individual consideration of potential copyright infringement.
+
+
+
+
+
Inline Configuration for IIIF tile sources.
+
+ Inline configuration is very straight forward. The tile source type is
+ identified by it's profile attribute an must specify the
+ url of the tile service with the tilesUrl attribute.
+
+
+
+
+ Example Inline Configuration for IIIF
+
+
+
+
+ In this example we also specified the 'preserveViewport' option to retain the
+ viewport zoom and position when changing pages.
+
+
+
+OpenSeadragon({
+ ...
+ preserveViewport: true,
+ tileSources: [{
+ "tilesUrl": "http://img.princeton.edu/loris",
+ "identifier": "pudl0001/4609321/s42/00000001",
+ "width": 2617,
+ "height": 3600,
+ "scale_factors": [1, 2, 3, 4, 5],
+ "tile_width": 256,
+ "tile_height": 256,
+ "formats": [ "jpg", "png" ],
+ "qualities": ["native", "bitonal", "grey", "color"],
+ "profile": "http://library.stanford.edu/iiif/image-api/compliance.html#level1"
+ }...,{
+
+ ...//more tile sources
+
+ }]
+ ...
+});
+
+
+