Merge remote-tracking branch 'openlayers/master' into vector-api

This commit is contained in:
Tom Payne
2014-01-02 18:28:30 +01:00
12 changed files with 389 additions and 19 deletions

50
examples/zoomify.html Normal file
View File

@@ -0,0 +1,50 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
<link rel="stylesheet" href="../css/ol.css" type="text/css">
<link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
<link rel="stylesheet" href="../resources/layout.css" type="text/css">
<link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
<title>Zoomify example</title>
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
</div>
</div>
</div>
<div class="container-fluid">
<div class="row-fluid">
<div class="span12">
<div id="map" class="map"></div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<h4 id="title">Zoomify example</h4>
<p id="shortdesc">Example of a Zoomify source.</p>
<div id="docs">
<p>See the <a href="zoomify.js" target="_blank">zoomify.js source</a> to see how this is done.</p>
</div>
<div id="tags">zoomify</div>
</div>
</div>
</div>
<script src="loader.js?id=zoomify" type="text/javascript"></script>
<script src="../resources/example-behaviour.js" type="text/javascript"></script>
</body>
</html>

52
examples/zoomify.js Normal file
View File

@@ -0,0 +1,52 @@
goog.require('ol.Map');
goog.require('ol.RendererHints');
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');
// This server does not support CORS, and so is incompatible with WebGL.
//var imgWidth = 8001;
//var imgHeight = 6943;
//var url = 'http://mapy.mzk.cz/AA22/0103/';
//var crossOrigin = undefined;
var imgWidth = 9911;
var imgHeight = 6100;
var url = 'http://vips.vtech.fr/cgi-bin/iipsrv.fcgi?zoomify=' +
'/mnt/MD1/AD00/plan_CHU-4HD-01/FOND.TIF/';
var crossOrigin = 'anonymous';
var imgCenter = [imgWidth / 2, - imgHeight / 2];
// Maps always need a projection, but Zoomify layers are not geo-referenced, and
// are only measured in pixels. So, we create a fake projection that the map
// can use to properly display the layer.
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],
crossOrigin: crossOrigin
});
var map = new ol.Map({
layers: [
new ol.layer.Tile({
source: source
})
],
renderers: ol.RendererHints.createFromQueryData(),
target: 'map',
view: new ol.View2D({
projection: proj,
center: imgCenter,
zoom: 0
})
});

View File

@@ -758,6 +758,17 @@
* @todo stability experimental * @todo stability experimental
*/ */
/**
* @typedef {Object} olx.source.ZoomifyOptions
* @property {Array.<ol.Attribution>|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.CircleOptions * @typedef {Object} olx.style.CircleOptions
* @property {ol.style.Fill|undefined} fill Fill style. * @property {ol.style.Fill|undefined} fill Fill style.
@@ -841,3 +852,9 @@
* @property {number} maxZoom Maximum zoom. * @property {number} maxZoom Maximum zoom.
* @todo stability experimental * @todo stability experimental
*/ */
/**
* @typedef {Object} olx.tilegrid.ZoomifyOptions
* @property {!Array.<number>} resolutions Resolutions.
* @todo stability experimental
*/

View File

@@ -1203,7 +1203,7 @@ ol.Map.prototype.updateSize = function() {
if (goog.isNull(targetElement)) { if (goog.isNull(targetElement)) {
this.setSize(undefined); this.setSize(undefined);
} else { } else {
var size = goog.style.getSize(targetElement); var size = goog.style.getContentBoxSize(targetElement);
this.setSize([size.width, size.height]); this.setSize([size.width, size.height]);
} }
}; };

View File

@@ -1,3 +1,4 @@
@exportSymbol ol.Observable
@exportProperty ol.Observable.prototype.on @exportProperty ol.Observable.prototype.on
@exportProperty ol.Observable.prototype.once @exportProperty ol.Observable.prototype.once
@exportProperty ol.Observable.prototype.un @exportProperty ol.Observable.prototype.un

View File

@@ -7,6 +7,7 @@
@exportProperty ol.proj.Units.DEGREES @exportProperty ol.proj.Units.DEGREES
@exportProperty ol.proj.Units.FEET @exportProperty ol.proj.Units.FEET
@exportProperty ol.proj.Units.METERS @exportProperty ol.proj.Units.METERS
@exportProperty ol.proj.Units.PIXELS
@exportSymbol ol.proj.addProjection @exportSymbol ol.proj.addProjection
@exportSymbol ol.proj.get @exportSymbol ol.proj.get

View File

@@ -40,7 +40,8 @@ ol.proj.ProjectionLike;
ol.proj.Units = { ol.proj.Units = {
DEGREES: 'degrees', DEGREES: 'degrees',
FEET: 'ft', FEET: 'ft',
METERS: 'm' METERS: 'm',
PIXELS: 'pixels'
}; };

View File

@@ -22,6 +22,9 @@ goog.require('ol.tilegrid.TileGrid');
* logo: (string|undefined), * logo: (string|undefined),
* opaque: (boolean|undefined), * opaque: (boolean|undefined),
* projection: ol.proj.ProjectionLike, * projection: ol.proj.ProjectionLike,
* tileClass: (function(new: ol.ImageTile, ol.TileCoord,
* ol.TileState, string, ?string,
* ol.TileLoadFunctionType)|undefined),
* tileGrid: (ol.tilegrid.TileGrid|undefined), * tileGrid: (ol.tilegrid.TileGrid|undefined),
* tileLoadFunction: (ol.TileLoadFunctionType|undefined), * tileLoadFunction: (ol.TileLoadFunctionType|undefined),
* tileUrlFunction: (ol.TileUrlFunctionType|undefined)}} * tileUrlFunction: (ol.TileUrlFunctionType|undefined)}}
@@ -57,25 +60,33 @@ ol.source.TileImage = function(options) {
ol.TileUrlFunction.nullTileUrlFunction; ol.TileUrlFunction.nullTileUrlFunction;
/** /**
* @private * @protected
* @type {?string} * @type {?string}
*/ */
this.crossOrigin_ = this.crossOrigin =
goog.isDef(options.crossOrigin) ? options.crossOrigin : null; goog.isDef(options.crossOrigin) ? options.crossOrigin : null;
/** /**
* @private * @protected
* @type {ol.TileCache} * @type {ol.TileCache}
*/ */
this.tileCache_ = new ol.TileCache(); this.tileCache = new ol.TileCache();
/** /**
* @private * @protected
* @type {ol.TileLoadFunctionType} * @type {ol.TileLoadFunctionType}
*/ */
this.tileLoadFunction_ = goog.isDef(options.tileLoadFunction) ? this.tileLoadFunction = goog.isDef(options.tileLoadFunction) ?
options.tileLoadFunction : ol.source.TileImage.defaultTileLoadFunction; options.tileLoadFunction : ol.source.TileImage.defaultTileLoadFunction;
/**
* @protected
* @type {function(new: ol.ImageTile, ol.TileCoord, ol.TileState, string,
* ?string, ol.TileLoadFunctionType)}
*/
this.tileClass = goog.isDef(options.tileClass) ?
options.tileClass : ol.ImageTile;
}; };
goog.inherits(ol.source.TileImage, ol.source.Tile); goog.inherits(ol.source.TileImage, ol.source.Tile);
@@ -93,7 +104,7 @@ ol.source.TileImage.defaultTileLoadFunction = function(imageTile, src) {
* @inheritDoc * @inheritDoc
*/ */
ol.source.TileImage.prototype.canExpireCache = function() { ol.source.TileImage.prototype.canExpireCache = function() {
return this.tileCache_.canExpireCache(); return this.tileCache.canExpireCache();
}; };
@@ -101,7 +112,7 @@ ol.source.TileImage.prototype.canExpireCache = function() {
* @inheritDoc * @inheritDoc
*/ */
ol.source.TileImage.prototype.expireCache = function(usedTiles) { ol.source.TileImage.prototype.expireCache = function(usedTiles) {
this.tileCache_.expireCache(usedTiles); this.tileCache.expireCache(usedTiles);
}; };
@@ -110,19 +121,19 @@ ol.source.TileImage.prototype.expireCache = function(usedTiles) {
*/ */
ol.source.TileImage.prototype.getTile = function(z, x, y, projection) { ol.source.TileImage.prototype.getTile = function(z, x, y, projection) {
var tileCoordKey = this.getKeyZXY(z, x, y); var tileCoordKey = this.getKeyZXY(z, x, y);
if (this.tileCache_.containsKey(tileCoordKey)) { if (this.tileCache.containsKey(tileCoordKey)) {
return /** @type {!ol.Tile} */ (this.tileCache_.get(tileCoordKey)); return /** @type {!ol.Tile} */ (this.tileCache.get(tileCoordKey));
} else { } else {
goog.asserts.assert(projection); goog.asserts.assert(projection);
var tileCoord = new ol.TileCoord(z, x, y); var tileCoord = new ol.TileCoord(z, x, y);
var tileUrl = this.tileUrlFunction(tileCoord, projection); var tileUrl = this.tileUrlFunction(tileCoord, projection);
var tile = new ol.ImageTile( var tile = new this.tileClass(
tileCoord, tileCoord,
goog.isDef(tileUrl) ? ol.TileState.IDLE : ol.TileState.EMPTY, goog.isDef(tileUrl) ? ol.TileState.IDLE : ol.TileState.EMPTY,
goog.isDef(tileUrl) ? tileUrl : '', goog.isDef(tileUrl) ? tileUrl : '',
this.crossOrigin_, this.crossOrigin,
this.tileLoadFunction_); this.tileLoadFunction);
this.tileCache_.set(tileCoordKey, tile); this.tileCache.set(tileCoordKey, tile);
return tile; return tile;
} }
}; };
@@ -135,7 +146,7 @@ ol.source.TileImage.prototype.setTileUrlFunction = function(tileUrlFunction) {
// FIXME It should be possible to be more intelligent and avoid clearing the // FIXME It should be possible to be more intelligent and avoid clearing the
// FIXME cache. The tile URL function would need to be incorporated into the // FIXME cache. The tile URL function would need to be incorporated into the
// FIXME cache key somehow. // FIXME cache key somehow.
this.tileCache_.clear(); this.tileCache.clear();
this.tileUrlFunction = tileUrlFunction; this.tileUrlFunction = tileUrlFunction;
this.dispatchChangeEvent(); this.dispatchChangeEvent();
}; };
@@ -146,7 +157,7 @@ ol.source.TileImage.prototype.setTileUrlFunction = function(tileUrlFunction) {
*/ */
ol.source.TileImage.prototype.useTile = function(z, x, y) { ol.source.TileImage.prototype.useTile = function(z, x, y) {
var tileCoordKey = this.getKeyZXY(z, x, y); var tileCoordKey = this.getKeyZXY(z, x, y);
if (this.tileCache_.containsKey(tileCoordKey)) { if (this.tileCache.containsKey(tileCoordKey)) {
this.tileCache_.get(tileCoordKey); this.tileCache.get(tileCoordKey);
} }
}; };

View File

@@ -0,0 +1 @@
@exportSymbol ol.source.Zoomify

View File

@@ -0,0 +1,149 @@
goog.provide('ol.source.Zoomify');
goog.require('goog.array');
goog.require('goog.dom');
goog.require('goog.dom.TagName');
goog.require('ol.ImageTile');
goog.require('ol.TileCoord');
goog.require('ol.TileState');
goog.require('ol.TileUrlFunction');
goog.require('ol.proj');
goog.require('ol.source.TileImage');
goog.require('ol.tilegrid.Zoomify');
/**
* @constructor
* @extends {ol.source.TileImage}
* @param {olx.source.ZoomifyOptions=} opt_options Options.
* @todo stability experimental
*/
ol.source.Zoomify = function(opt_options) {
var options = goog.isDef(opt_options) ? opt_options : {};
var size = options.size;
var imageWidth = size[0];
var imageHeight = size[1];
var tierSizeInTiles = [];
var tileSize = ol.DEFAULT_TILE_SIZE;
while (imageWidth > tileSize || imageHeight > tileSize) {
tierSizeInTiles.push([
Math.ceil(imageWidth / tileSize),
Math.ceil(imageHeight / tileSize)
]);
tileSize += tileSize;
}
tierSizeInTiles.push([1, 1]);
tierSizeInTiles.reverse();
var resolutions = [1];
var tileCountUpToTier = [0];
var i, ii;
for (i = 1, ii = tierSizeInTiles.length; i < ii; i++) {
resolutions.push(1 << i);
tileCountUpToTier.push(
tierSizeInTiles[i - 1][0] * tierSizeInTiles[i - 1][1] +
tileCountUpToTier[i - 1]
);
}
resolutions.reverse();
var tileGrid = new ol.tilegrid.Zoomify({
resolutions: resolutions
});
var url = options.url;
var tileUrlFunction = ol.TileUrlFunction.withTileCoordTransform(
tileGrid.createTileCoordTransform({extent: [0, 0, size[0], size[1]]}),
/**
* @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 * tierSizeInTiles[tileCoord.z][0] +
tileCountUpToTier[tileCoord.z];
var tileGroup = (tileIndex / ol.DEFAULT_TILE_SIZE) | 0;
return url + 'TileGroup' + tileGroup + '/' +
tileCoord.z + '-' + tileCoord.x + '-' + tileCoord.y + '.jpg';
}
});
goog.base(this, {
attributions: options.attributions,
crossOrigin: options.crossOrigin,
logo: options.logo,
tileClass: ol.source.ZoomifyTile_,
tileGrid: tileGrid,
tileUrlFunction: tileUrlFunction
});
};
goog.inherits(ol.source.Zoomify, ol.source.TileImage);
/**
* @constructor
* @extends {ol.ImageTile}
* @param {ol.TileCoord} tileCoord Tile coordinate.
* @param {ol.TileState} state State.
* @param {string} src Image source URI.
* @param {?string} crossOrigin Cross origin.
* @param {ol.TileLoadFunctionType} tileLoadFunction Tile load function.
* @private
*/
ol.source.ZoomifyTile_ = function(
tileCoord, state, src, crossOrigin, tileLoadFunction) {
goog.base(this, tileCoord, state, src, crossOrigin, tileLoadFunction);
/**
* @private
* @type {Object.<string,
* HTMLCanvasElement|HTMLImageElement|HTMLVideoElement>}
*/
this.zoomifyImageByContext_ = {};
};
goog.inherits(ol.source.ZoomifyTile_, ol.ImageTile);
/**
* @inheritDoc
*/
ol.source.ZoomifyTile_.prototype.getImage = function(opt_context) {
var key = goog.isDef(opt_context) ? goog.getUid(opt_context).toString() : '';
if (key in this.zoomifyImageByContext_) {
return this.zoomifyImageByContext_[key];
} else {
var image = goog.base(this, 'getImage', opt_context);
if (this.state == ol.TileState.LOADED) {
if (image.width == ol.DEFAULT_TILE_SIZE &&
image.height == ol.DEFAULT_TILE_SIZE) {
this.zoomifyImageByContext_[key] = image;
return image;
} else {
var canvas = /** @type {HTMLCanvasElement} */
(goog.dom.createElement(goog.dom.TagName.CANVAS));
canvas.width = ol.DEFAULT_TILE_SIZE;
canvas.height = ol.DEFAULT_TILE_SIZE;
var context = /** @type {CanvasRenderingContext2D} */
(canvas.getContext('2d'));
context.drawImage(image, 0, 0);
this.zoomifyImageByContext_[key] = canvas;
return canvas;
}
} else {
return image;
}
}
};

View File

@@ -0,0 +1 @@
@exportSymbol ol.tilegrid.Zoomify

View File

@@ -0,0 +1,86 @@
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 {olx.tilegrid.ZoomifyOptions=} opt_options Options.
* @todo stability experimental
*/
ol.tilegrid.Zoomify = function(opt_options) {
var options = goog.isDef(opt_options) ? opt_options : 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.<ol.TileRange>} */
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 - 1;
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);
}
});
};