diff --git a/examples/zoomify.html b/examples/zoomify.html new file mode 100644 index 0000000000..396a736392 --- /dev/null +++ b/examples/zoomify.html @@ -0,0 +1,50 @@ + + + + + + + + + + + Zoomify example + + + + + +
+ +
+
+
+
+
+ +
+ +
+

Zoomify example

+

Example of a Zoomify source.

+
+

See the zoomify.js source to see how this is done.

+
+
zoomify
+
+ +
+ +
+ + + + + + diff --git a/examples/zoomify.js b/examples/zoomify.js new file mode 100644 index 0000000000..8da21f32ab --- /dev/null +++ b/examples/zoomify.js @@ -0,0 +1,39 @@ +goog.require('ol.Map'); +goog.require('ol.RendererHint'); +goog.require('ol.View2D'); +goog.require('ol.layer.Tile'); +goog.require('ol.proj'); +goog.require('ol.proj.Projection'); +goog.require('ol.proj.Units'); +goog.require('ol.source.Zoomify'); + +var imgWidth = 8001; +var imgHeight = 6943; +var imgCenter = [imgWidth / 2, -imgHeight / 2]; +var url = 'http://mapy.mzk.cz/AA22/0103/'; + +var proj = new ol.proj.Projection({ + code: 'ZOOMIFY', + units: ol.proj.Units.PIXELS, + extent: [0, 0, imgWidth, imgHeight] +}); + +var source = new ol.source.Zoomify({ + url: url, + size: [imgWidth, imgHeight] +}); + +var map = new ol.Map({ + layers: [ + new ol.layer.Tile({ + source: source + }) + ], + renderer: ol.RendererHint.CANVAS, + target: 'map', + view: new ol.View2D({ + projection: proj, + center: imgCenter, + zoom: 0 + }) +}); diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc index 8f9a3053fc..ecb47640cb 100644 --- a/src/objectliterals.jsdoc +++ b/src/objectliterals.jsdoc @@ -840,6 +840,17 @@ * @todo stability experimental */ +/** + * @typedef {Object} ol.source.ZoomifyOptions + * @property {Array.|undefined} attributions Attributions. + * @property {null|string|undefined} crossOrigin Cross origin setting for image + * requests. + * @property {string|undefined} logo Logo. + * @property {!string} url Prefix of URL template. + * @property {ol.Size} size Size of the image. + * @todo stability experimental + */ + /** * @typedef {Object} olx.style.IconOptions * @property {string|ol.expr.Expression} url Icon image URL. @@ -954,3 +965,9 @@ * @property {number} maxZoom Maximum zoom. * @todo stability experimental */ + +/** + * @typedef {Object} ol.tilegrid.ZoomifyOptions + * @property {!Array.} resolutions Resolutions. + * @todo stability experimental + */ diff --git a/src/ol/proj/proj.js b/src/ol/proj/proj.js index bcb3f31e2d..c0c750a88f 100644 --- a/src/ol/proj/proj.js +++ b/src/ol/proj/proj.js @@ -40,7 +40,8 @@ ol.proj.ProjectionLike; ol.proj.Units = { DEGREES: 'degrees', FEET: 'ft', - METERS: 'm' + METERS: 'm', + PIXELS: 'pixels' }; diff --git a/src/ol/source/zoomifysource.exports b/src/ol/source/zoomifysource.exports new file mode 100644 index 0000000000..57fce6e8b7 --- /dev/null +++ b/src/ol/source/zoomifysource.exports @@ -0,0 +1 @@ +@exportClass ol.source.Zoomify ol.source.ZoomifyOptions diff --git a/src/ol/source/zoomifysource.js b/src/ol/source/zoomifysource.js new file mode 100644 index 0000000000..fa0250e4e1 --- /dev/null +++ b/src/ol/source/zoomifysource.js @@ -0,0 +1,142 @@ +goog.provide('ol.source.Zoomify'); + +goog.require('goog.array'); +goog.require('ol.TileCoord'); +goog.require('ol.TileUrlFunction'); +goog.require('ol.proj'); +goog.require('ol.source.TileImage'); +goog.require('ol.tilegrid.Zoomify'); + + + +/** + * @constructor + * @extends {ol.source.TileImage} + * @param {ol.source.ZoomifyOptions} options Zoomify options. + * @todo stability experimental + */ +ol.source.Zoomify = function(options) { + + /** + * Prefix of URL template. + * @private + * @type {!string} + */ + this.url_ = options.url; + + /** + * Size of the image. + * @private + * @type {ol.Size} + */ + this.size_ = options.size; + + /** + * Depth of the Zoomify pyramid, number of tiers (zoom levels). + * @private + * @type {number} + */ + this.numberOfTiers_ = 0; + + /** + * Number of tiles up to the given tier of pyramid. + * @private + * @type {Array.} + */ + this.tileCountUpToTier_ = null; + + /** + * Size (in tiles) for each tier of pyramid. + * @private + * @type {Array.} + */ + this.tierSizeInTiles_ = null; + + /** + * Image size in pixels for each pyramid tier. + * @private + * @type {Array.} + */ + this.tierImageSize_ = null; + + var imageSize = [].concat(this.size_); + var tiles = [ + Math.ceil(this.size_[0] / ol.DEFAULT_TILE_SIZE), + Math.ceil(this.size_[1] / ol.DEFAULT_TILE_SIZE) + ]; + this.tierSizeInTiles_ = [tiles]; + this.tierImageSize_ = [imageSize]; + + while (imageSize[0] > ol.DEFAULT_TILE_SIZE || + imageSize[1] > ol.DEFAULT_TILE_SIZE) { + + imageSize = [ + Math.floor(imageSize[0] / 2), + Math.floor(imageSize[1] / 2) + ]; + tiles = [ + Math.ceil(imageSize[0] / ol.DEFAULT_TILE_SIZE), + Math.ceil(imageSize[1] / ol.DEFAULT_TILE_SIZE) + ]; + this.tierSizeInTiles_.push(tiles); + this.tierImageSize_.push(imageSize); + } + + this.tierSizeInTiles_.reverse(); + this.tierImageSize_.reverse(); + this.numberOfTiers_ = this.tierSizeInTiles_.length; + var resolutions = [1]; + this.tileCountUpToTier_ = [0]; + for (var i = 1; i < this.numberOfTiers_; i++) { + resolutions.unshift(Math.pow(2, i)); + this.tileCountUpToTier_.push( + this.tierSizeInTiles_[i - 1][0] * this.tierSizeInTiles_[i - 1][1] + + this.tileCountUpToTier_[i - 1] + ); + } + + + var createFromUrl = function(url) { + var template = url + '{tileIndex}/{z}-{x}-{y}.jpg'; + return ( + /** + * @this {ol.source.TileImage} + * @param {ol.TileCoord} tileCoord Tile Coordinate. + * @param {ol.proj.Projection} projection Projection. + * @return {string|undefined} Tile URL. + */ + function(tileCoord, projection) { + if (goog.isNull(tileCoord)) { + return undefined; + } else { + var tileIndex = tileCoord.x + + (tileCoord.y * this.tierSizeInTiles_[tileCoord.z][0]) + + this.tileCountUpToTier_[tileCoord.z]; + return template.replace('{tileIndex}', 'TileGroup' + + Math.floor((tileIndex) / ol.DEFAULT_TILE_SIZE)) + .replace('{z}', '' + tileCoord.z) + .replace('{x}', '' + tileCoord.x) + .replace('{y}', '' + tileCoord.y); + } + } + ); + }; + + var tileGrid = new ol.tilegrid.Zoomify({ + resolutions: resolutions + }); + var tileUrlFunction = ol.TileUrlFunction.withTileCoordTransform( + tileGrid.createTileCoordTransform(), + createFromUrl(this.url_)); + + + goog.base(this, { + attributions: options.attributions, + crossOrigin: options.crossOrigin, + logo: options.logo, + tileGrid: tileGrid, + tileUrlFunction: tileUrlFunction + }); + +}; +goog.inherits(ol.source.Zoomify, ol.source.TileImage); diff --git a/src/ol/tilegrid/zoomifytilegrid.exports b/src/ol/tilegrid/zoomifytilegrid.exports new file mode 100644 index 0000000000..132b391d5b --- /dev/null +++ b/src/ol/tilegrid/zoomifytilegrid.exports @@ -0,0 +1 @@ +@exportClass ol.tilegrid.Zoomify ol.tilegrid.ZoomifyOptions diff --git a/src/ol/tilegrid/zoomifytilegrid.js b/src/ol/tilegrid/zoomifytilegrid.js new file mode 100644 index 0000000000..a88f54f861 --- /dev/null +++ b/src/ol/tilegrid/zoomifytilegrid.js @@ -0,0 +1,85 @@ +goog.provide('ol.tilegrid.Zoomify'); + +goog.require('goog.math'); +goog.require('ol.TileCoord'); +goog.require('ol.proj'); +goog.require('ol.tilegrid.TileGrid'); + + + +/** + * @constructor + * @extends {ol.tilegrid.TileGrid} + * @param {ol.tilegrid.ZoomifyOptions} options Zoomify options. + * @todo stability experimental + */ +ol.tilegrid.Zoomify = function(options) { + goog.base(this, { + origin: [0, 0], + resolutions: options.resolutions + }); + +}; +goog.inherits(ol.tilegrid.Zoomify, ol.tilegrid.TileGrid); + + +/** + * @inheritDoc + */ +ol.tilegrid.Zoomify.prototype.createTileCoordTransform = function(opt_options) { + var options = goog.isDef(opt_options) ? opt_options : {}; + var minZ = this.minZoom; + var maxZ = this.maxZoom; + var tmpTileCoord = new ol.TileCoord(0, 0, 0); + /** @type {Array.} */ + var tileRangeByZ = null; + if (goog.isDef(options.extent)) { + tileRangeByZ = new Array(maxZ + 1); + var z; + for (z = 0; z <= maxZ; ++z) { + if (z < minZ) { + tileRangeByZ[z] = null; + } else { + tileRangeByZ[z] = this.getTileRangeForExtentAndZ(options.extent, z); + } + } + } + return ( + /** + * @param {ol.TileCoord} tileCoord Tile coordinate. + * @param {ol.proj.Projection} projection Projection. + * @param {ol.TileCoord=} opt_tileCoord Destination tile coordinate. + * @return {ol.TileCoord} Tile coordinate. + */ + function(tileCoord, projection, opt_tileCoord) { + var z = tileCoord.z; + if (z < minZ || maxZ < z) { + return null; + } + var n = Math.pow(2, z); + var x = tileCoord.x; + if (x < 0 || n <= x) { + return null; + } + var y = tileCoord.y; + if (y < -n || -1 < y) { + return null; + } + if (!goog.isNull(tileRangeByZ)) { + tmpTileCoord.z = z; + tmpTileCoord.x = x; + tmpTileCoord.y = y; + if (!tileRangeByZ[z].contains(tmpTileCoord)) { + return null; + } + } + if (goog.isDef(opt_tileCoord)) { + opt_tileCoord.z = z; + opt_tileCoord.x = x; + opt_tileCoord.y = -y - 1; + return opt_tileCoord; + } else { + return new ol.TileCoord(z, x, -y - 1); + } + }); +};