From 22f0f933fbc21ce6a9de4455ad9f75b839f8ef22 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Thu, 4 Mar 2010 08:41:18 +0000 Subject: [PATCH] Added Layer.Zoomify. This is a late follow-up on the FOSS4G 2009 sprint in Sydney. Thanks Petr for the hard work on this. p=klokan, r=me (closes #1285) git-svn-id: http://svn.openlayers.org/trunk/openlayers@10086 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf --- examples/zoomify.html | 70 ++++++++ lib/OpenLayers.js | 1 + lib/OpenLayers/Layer/Zoomify.js | 306 ++++++++++++++++++++++++++++++++ 3 files changed, 377 insertions(+) create mode 100644 examples/zoomify.html create mode 100644 lib/OpenLayers/Layer/Zoomify.js diff --git a/examples/zoomify.html b/examples/zoomify.html new file mode 100644 index 0000000000..d3fb305c58 --- /dev/null +++ b/examples/zoomify.html @@ -0,0 +1,70 @@ + + + OpenLayers Zoomify Example + + + + + + +

Zoomify Layer Example

+ +
+ +

+ Demo of a layer with Zoomify tiles. +

+ +
+ +
+

+ Demonstration of the Zoomify layer in OpenLayers.
+ You can have a look at Zoomify viewer for this picture, which is using the same tiles. +

+

+ For change to our own image you have to specify 'url' (zoomifyImagePath in Zoomify terminology) and 'size' ('width' and 'height' from ImageProperty.xml file).
+ Custom tiles can be easily generated with original Zoomify software like with freely available ZoomifyerEZ or with Adobe PhotoShop CS3 (it has built in support for export into Zoomify tiles).
+ There is also a ZoomifyImage SourceForge Project, a tile cutter available under GPL license.
+ Zoomify tiles can be also served dynamically on the server side from JPEG2000 masters using J2KTileRender with available integration for DSpace and soon for Fedora Digital Repository.
+ IIPImage server can serve Zoomify tiles dynamically from TIFF files. +

+

+ Development of the Zoomify support for OpenLayers was supported from the grant Old Maps Online. +

+
+ + diff --git a/lib/OpenLayers.js b/lib/OpenLayers.js index 652e93ce8b..61d30a39f3 100644 --- a/lib/OpenLayers.js +++ b/lib/OpenLayers.js @@ -130,6 +130,7 @@ "OpenLayers/Layer/XYZ.js", "OpenLayers/Layer/TMS.js", "OpenLayers/Layer/TileCache.js", + "OpenLayers/Layer/Zoomify.js", "OpenLayers/Popup/Anchored.js", "OpenLayers/Popup/AnchoredBubble.js", "OpenLayers/Popup/Framed.js", diff --git a/lib/OpenLayers/Layer/Zoomify.js b/lib/OpenLayers/Layer/Zoomify.js new file mode 100644 index 0000000000..dba37bdba5 --- /dev/null +++ b/lib/OpenLayers/Layer/Zoomify.js @@ -0,0 +1,306 @@ +/* Copyright (c) 2008 Klokan Petr Pridal, published under the Clear BSD + * licence. See http://svn.openlayers.org/trunk/openlayers/license.txt for the + * full text of the license. */ +/* + * Development supported by a R&D grant DC08P02OUK006 - Old Maps Online + * (www.oldmapsonline.org) from Ministry of Culture of the Czech Republic. + */ + + +/** + * @requires OpenLayers/Layer/Grid.js + */ + +/** + * Class: OpenLayers.Layer.Zoomify + * + * Inherits from: + * - + */ +OpenLayers.Layer.Zoomify = OpenLayers.Class(OpenLayers.Layer.Grid, { + + /** + * Property: url + * {String} URL for root directory with TileGroupX subdirectories. + */ + url: null, + + /** + * Property: size + * {} The Zoomify image size in pixels. + */ + size: null, + + /** + * APIProperty: isBaseLayer + * {Boolean} + */ + isBaseLayer: true, + + /** + * Property: standardTileSize + * {Integer} The size of a standard (non-border) square tile in pixels. + */ + standardTileSize: 256, + + /** + * Property: numberOfTiers + * {Integer} Depth of the Zoomify pyramid, number of tiers (zoom levels) + * - filled during Zoomify pyramid initialization. + */ + numberOfTiers: 0, + + /** + * Property: tileCountUpToTier + * {Array(Integer)} Number of tiles up to the given tier of pyramid. + * - filled during Zoomify pyramid initialization. + */ + tileCountUpToTier: new Array(), + + /** + * Property: tierSizeInTiles + * {Array()} Size (in tiles) for each tier of pyramid. + * - filled during Zoomify pyramid initialization. + */ + tierSizeInTiles: new Array(), + + /** + * Property: tierImageSize + * {Array()} Image size in pixels for each pyramid tier. + * - filled during Zoomify pyramid initialization. + */ + tierImageSize: new Array(), + + /** + * Constructor: OpenLayers.Layer.Zoomify + * + * Parameters: + * name - {String} A name for the layer. + * url - {String} - Relative or absolute path to the image or more + * precisly to the TileGroup[X] directories root. + * Flash plugin use the variable name "zoomifyImagePath" for this. + * size - {} The size (in pixels) of the image. + * options - {Object} Hashtable of extra options to tag onto the layer + */ + initialize: function(name, url, size, options) { + + // initilize the Zoomify pyramid for given size + this.initializeZoomify( size ); + + var newArguments = []; + newArguments.push(name, url, size, {}, options); + + OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments); + }, + + /** + * Method: initializeZoomify + * It generates constants for all tiers of the Zoomify pyramid + * + * Parameters: + * size - {} The size of the image in pixels + * + */ + initializeZoomify: function( size ) { + + var imageSize = size.clone() + var tiles = new OpenLayers.Size( + Math.ceil( imageSize.w / this.standardTileSize ), + Math.ceil( imageSize.h / this.standardTileSize ) + ); + + this.tierSizeInTiles.push( tiles ); + this.tierImageSize.push( imageSize ); + + while (imageSize.w > this.standardTileSize || + imageSize.h > this.standardTileSize ) { + + imageSize = new OpenLayers.Size( + Math.floor( imageSize.w / 2 ), + Math.floor( imageSize.h / 2 ) + ); + tiles = new OpenLayers.Size( + Math.ceil( imageSize.w / this.standardTileSize ), + Math.ceil( imageSize.h / this.standardTileSize ) + ); + this.tierSizeInTiles.push( tiles ); + this.tierImageSize.push( imageSize ); + } + + this.tierSizeInTiles.reverse(); + this.tierImageSize.reverse(); + + this.numberOfTiers = this.tierSizeInTiles.length; + + this.tileCountUpToTier[0] = 0; + for (var i = 1; i < this.numberOfTiers; i++) { + this.tileCountUpToTier.push( + this.tierSizeInTiles[i-1].w * this.tierSizeInTiles[i-1].h + + this.tileCountUpToTier[i-1] + ); + } + }, + + /** + * APIMethod:destroy + */ + destroy: function() { + // for now, nothing special to do here. + OpenLayers.Layer.Grid.prototype.destroy.apply(this, arguments); + + // Remove from memory the Zoomify pyramid - is that enough? + this.tileCountUpToTier.length = 0 + this.tierSizeInTiles.length = 0 + this.tierImageSize.length = 0 + + }, + + /** + * APIMethod: clone + * + * Parameters: + * obj - {Object} + * + * Returns: + * {} An exact clone of this + */ + clone: function (obj) { + + if (obj == null) { + obj = new OpenLayers.Layer.Zoomify(this.name, + this.url, + this.size, + this.options); + } + + //get all additions from superclasses + obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); + + // copy/set any non-init, non-simple values here + + return obj; + }, + + /** + * Method: getURL + * + * Parameters: + * bounds - {} + * + * Returns: + * {String} A string with the layer's url and parameters and also the + * passed-in bounds and appropriate tile size specified as + * parameters + */ + getURL: function (bounds) { + bounds = this.adjustBounds(bounds); + var res = this.map.getResolution(); + var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w)); + var y = Math.round((this.tileOrigin.lat - bounds.top) / (res * this.tileSize.h)); + var z = this.map.getZoom(); + + var tileIndex = x + y * this.tierSizeInTiles[z].w + this.tileCountUpToTier[z]; + var path = "TileGroup" + Math.floor( (tileIndex) / 256 ) + + "/" + z + "-" + x + "-" + y + ".jpg"; + var url = this.url; + if (url instanceof Array) { + url = this.selectUrl(path, url); + } + return url + path; + }, + + /** + * Method: getImageSize + * getImageSize returns size for a particular tile. If bounds are given as + * first argument, size is calculated (bottom-right tiles are non square). + * + */ + getImageSize: function() { + if (arguments.length > 0) { + bounds = this.adjustBounds(arguments[0]); + var res = this.map.getResolution(); + var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w)); + var y = Math.round((this.tileOrigin.lat - bounds.top) / (res * this.tileSize.h)); + var z = this.map.getZoom(); + var w = this.standardTileSize; + var h = this.standardTileSize; + if (x == this.tierSizeInTiles[z].w -1 ) { + var w = this.tierImageSize[z].w % this.standardTileSize; + }; + if (y == this.tierSizeInTiles[z].h -1 ) { + var h = this.tierImageSize[z].h % this.standardTileSize; + }; + return (new OpenLayers.Size(w, h)); + } else { + return this.tileSize; + } + }, + + /** + * Method: addTile + * addTile creates a tile, initializes it, and adds it to the layer div. + * + * Parameters: + * bounds - {} + * position - {} + * + * Returns: + * {} The added OpenLayers.Tile.Image + */ + addTile:function(bounds,position) { + return new OpenLayers.Tile.Image(this, position, bounds, + null, this.tileSize); + }, + + /** + * APIMethod: setMap + * When the layer is added to a map, then we can fetch our origin + * (if we don't have one.) + * + * Parameters: + * map - {} + */ + setMap: function(map) { + OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments); + this.tileOrigin = new OpenLayers.LonLat(this.map.maxExtent.left, + this.map.maxExtent.top); + }, + + /** + * Method: calculateGridLayout + * Generate parameters for the grid layout. This + * + * Parameters: + * bounds - {} + * extent - {} + * resolution - {Number} + * + * Returns: + * Object containing properties tilelon, tilelat, tileoffsetlat, + * tileoffsetlat, tileoffsetx, tileoffsety + */ + calculateGridLayout: function(bounds, extent, resolution) { + var tilelon = resolution * this.tileSize.w; + var tilelat = resolution * this.tileSize.h; + + var offsetlon = bounds.left - extent.left; + var tilecol = Math.floor(offsetlon/tilelon) - this.buffer; + var tilecolremain = offsetlon/tilelon - tilecol; + var tileoffsetx = -tilecolremain * this.tileSize.w; + var tileoffsetlon = extent.left + tilecol * tilelon; + + var offsetlat = extent.top - bounds.top + tilelat; + var tilerow = Math.floor(offsetlat/tilelat) - this.buffer; + var tilerowremain = tilerow - offsetlat/tilelat; + var tileoffsety = tilerowremain * this.tileSize.h; + var tileoffsetlat = extent.top - tilelat*tilerow; + + return { + tilelon: tilelon, tilelat: tilelat, + tileoffsetlon: tileoffsetlon, tileoffsetlat: tileoffsetlat, + tileoffsetx: tileoffsetx, tileoffsety: tileoffsety + }; + }, + + CLASS_NAME: "OpenLayers.Layer.Zoomify" +});