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
This commit is contained in:
70
examples/zoomify.html
Normal file
70
examples/zoomify.html
Normal file
@@ -0,0 +1,70 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>OpenLayers Zoomify Example</title>
|
||||
<link rel="stylesheet" href="../theme/default/style.css" type="text/css" />
|
||||
<link rel="stylesheet" href="style.css" type="text/css" />
|
||||
<script src="../lib/OpenLayers.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
var zoomify_width = 8001;
|
||||
var zoomify_height = 6943;
|
||||
var zoomify_url = "http://almor.mzk.cz/moll/AA22/0103/";
|
||||
|
||||
var map, zoomify;
|
||||
|
||||
function init(){
|
||||
/* First we initialize the zoomify pyramid (to get number of tiers) */
|
||||
var zoomify = new OpenLayers.Layer.Zoomify( "Zoomify", zoomify_url,
|
||||
new OpenLayers.Size( zoomify_width, zoomify_height ) );
|
||||
|
||||
/* Map with raster coordinates (pixels) from Zoomify image */
|
||||
var options = {
|
||||
controls: [],
|
||||
maxExtent: new OpenLayers.Bounds(0, 0, zoomify_width, zoomify_height),
|
||||
maxResolution: Math.pow(2, zoomify.numberOfTiers-1 ),
|
||||
numZoomLevels: zoomify.numberOfTiers,
|
||||
units: 'pixels'
|
||||
};
|
||||
|
||||
map = new OpenLayers.Map("map", options);
|
||||
map.addLayer(zoomify);
|
||||
|
||||
map.addControl(new OpenLayers.Control.MousePosition());
|
||||
map.addControl(new OpenLayers.Control.PanZoomBar());
|
||||
map.addControl(new OpenLayers.Control.MouseDefaults());
|
||||
map.addControl(new OpenLayers.Control.KeyboardDefaults());
|
||||
|
||||
map.setBaseLayer(zoomify);
|
||||
map.zoomToMaxExtent();
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
<body onload="init()">
|
||||
<h1 id="title">Zoomify Layer Example</h1>
|
||||
|
||||
<div id="tags"></div>
|
||||
|
||||
<p id="shortdesc">
|
||||
Demo of a layer with Zoomify tiles.
|
||||
</p>
|
||||
|
||||
<div id="map" class="smallmap"></div>
|
||||
|
||||
<div id="docs">
|
||||
<p>
|
||||
Demonstration of the Zoomify layer in OpenLayers.<br />
|
||||
You can have a look at <a href="http://almor.mzk.cz/moll/AA22/103.html">Zoomify viewer for this picture</a>, which is using the same <a href="http://almor.mzk.cz/moll/AA22/0103/">tiles</a>.
|
||||
</p>
|
||||
<p>
|
||||
For change to our own image you have to specify 'url' (zoomifyImagePath in Zoomify terminology) and 'size' ('width' and 'height' from ImageProperty.xml file).<br />
|
||||
Custom tiles can be easily generated with original <a href="http://www.zoomify.com/">Zoomify software</a> like with freely available <a href="http://www.zoomify.com/express.htm">ZoomifyerEZ</a> or with Adobe PhotoShop CS3 (it has built in support for export into Zoomify tiles).<br />
|
||||
There is also a <a href="http://sourceforge.net/projects/zoomifyimage/">ZoomifyImage SourceForge Project</a>, a tile cutter available under GPL license.<br />
|
||||
Zoomify tiles can be also served dynamically on the server side from JPEG2000 masters using <a href="http://dltj.org/article/introducing-j2ktilerenderer/">J2KTileRender</a> with available integration for DSpace and soon for Fedora Digital Repository.<br/>
|
||||
<a href="http://iipimage.sourceforge.net/">IIPImage server</a> can serve Zoomify tiles dynamically from TIFF files.
|
||||
</p>
|
||||
<p>
|
||||
Development of the Zoomify support for OpenLayers was supported from the grant <a href="http://www.oldmapsonline.org/">Old Maps Online</a>.
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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",
|
||||
|
||||
306
lib/OpenLayers/Layer/Zoomify.js
Normal file
306
lib/OpenLayers/Layer/Zoomify.js
Normal file
@@ -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.Grid>
|
||||
*/
|
||||
OpenLayers.Layer.Zoomify = OpenLayers.Class(OpenLayers.Layer.Grid, {
|
||||
|
||||
/**
|
||||
* Property: url
|
||||
* {String} URL for root directory with TileGroupX subdirectories.
|
||||
*/
|
||||
url: null,
|
||||
|
||||
/**
|
||||
* Property: size
|
||||
* {<OpenLayers.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(<OpenLayers.Size>)} Size (in tiles) for each tier of pyramid.
|
||||
* - filled during Zoomify pyramid initialization.
|
||||
*/
|
||||
tierSizeInTiles: new Array(),
|
||||
|
||||
/**
|
||||
* Property: tierImageSize
|
||||
* {Array(<OpenLayers.Size>)} 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 - {<OpenLayers.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 - {<OpenLayers.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:
|
||||
* {<OpenLayers.Layer.Zoomify>} An exact clone of this <OpenLayers.Layer.Zoomify>
|
||||
*/
|
||||
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 - {<OpenLayers.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 - {<OpenLayers.Bounds>}
|
||||
* position - {<OpenLayers.Pixel>}
|
||||
*
|
||||
* Returns:
|
||||
* {<OpenLayers.Tile.Image>} 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 - {<OpenLayers.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 - {<OpenLayers.Bound>}
|
||||
* extent - {<OpenLayers.Bounds>}
|
||||
* 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"
|
||||
});
|
||||
Reference in New Issue
Block a user