From 8b83345bd2c52034d65ec7b1657d6e392334bfb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Tue, 22 Jan 2013 17:03:01 +0100 Subject: [PATCH 01/20] Add equals to Rectangle --- src/ol/rectangle.js | 10 ++++++++++ src/ol/tilerange.js | 10 ---------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/ol/rectangle.js b/src/ol/rectangle.js index d15f1d7a18..a459d04603 100644 --- a/src/ol/rectangle.js +++ b/src/ol/rectangle.js @@ -41,6 +41,16 @@ ol.Rectangle = function(minX, minY, maxX, maxY) { }; +/** + * @param {ol.Rectangle} rectangle Rectangle. + * @return {boolean} Equals. + */ +ol.Rectangle.prototype.equals = function(rectangle) { + return this.minX == rectangle.minX && this.minY == rectangle.minY && + this.maxX == rectangle.maxX && this.maxY == rectangle.maxY; +}; + + /** * @param {ol.Rectangle} rectangle Rectangle. */ diff --git a/src/ol/tilerange.js b/src/ol/tilerange.js index 7c402f00cb..e1baa169f8 100644 --- a/src/ol/tilerange.js +++ b/src/ol/tilerange.js @@ -61,16 +61,6 @@ ol.TileRange.prototype.containsTileRange = function(tileRange) { }; -/** - * @param {ol.TileRange} tileRange Tile range. - * @return {boolean} Equals. - */ -ol.TileRange.prototype.equals = function(tileRange) { - return this.minX == tileRange.minX && tileRange.maxX == this.maxX && - this.minY == tileRange.minY && tileRange.minY == this.minY; -}; - - /** * @inheritDoc * @return {number} Height. From 49696390fceecbce5d15e4960fd3c49a64a7367e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Tue, 22 Jan 2013 17:17:08 +0100 Subject: [PATCH 02/20] Add ol.ImageUrlFunction --- src/imageurlfunction.js | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/imageurlfunction.js diff --git a/src/imageurlfunction.js b/src/imageurlfunction.js new file mode 100644 index 0000000000..bb328819eb --- /dev/null +++ b/src/imageurlfunction.js @@ -0,0 +1,40 @@ +goog.provide('ol.ImageUrlFunction'); +goog.provide('ol.ImageUrlFunctionType'); + +goog.require('ol.Extent'); +goog.require('ol.Size'); + + +/** + * @typedef {function(ol.Extent, ol.Size): (string|undefined)} + */ +ol.ImageUrlFunctionType; + + +/** + * @param {string} baseUrl Base URL (may have query data). + * @return {ol.ImageUrlFunctionType} Image URL function. + */ +ol.ImageUrlFunction.createBboxParam = function(baseUrl) { + return function(extent, size) { + // FIXME Projection dependant axis order. + var bboxValue = [ + extent.minX, extent.minY, extent.maxX, extent.maxY + ].join(','); + return goog.uri.utils.appendParams(baseUrl, + 'BBOX', bboxValue, + 'HEIGHT', size.height, + 'WIDTH', size.width); + }; +}; + + +/** + * @param {ol.Extent} extent Extent. + * @param {ol.Size} size Size. + * @return {string|undefined} Image URL. + */ +ol.ImageUrlFunction.nullImageUrlFunction = + function(extent, size) { + return undefined; +}; From cf206a103d43e78d03072e1269428304664531c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Tue, 22 Jan 2013 17:19:01 +0100 Subject: [PATCH 03/20] Add ol.Image class --- src/ol/image.js | 173 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 src/ol/image.js diff --git a/src/ol/image.js b/src/ol/image.js new file mode 100644 index 0000000000..ee81baa348 --- /dev/null +++ b/src/ol/image.js @@ -0,0 +1,173 @@ +goog.provide('ol.Image'); +goog.provide('ol.ImageState'); + +goog.require('goog.array'); +goog.require('goog.events'); +goog.require('ol.Extent'); + + +/** + * @enum {number} + */ +ol.ImageState = { + IDLE: 0, + LOADING: 1, + LOADED: 2, + ERROR: 3 +}; + + + +/** + * @constructor + * @param {ol.Extent} extent Extent. + * @param {number} resolution Resolution. + * @param {string} src Image source URI. + * @param {?string} crossOrigin Cross origin. + */ +ol.Image = function(extent, resolution, src, crossOrigin) { + + /** + * @private + * @type {ol.Extent} + */ + this.extent_ = extent; + + /** + * @private + * @type {string} + */ + this.src_ = src; + + /** + * @private + * @type {number} + */ + this.resolution_ = resolution; + + /** + * @private + * @type {Image} + */ + this.image_ = new Image(); + if (!goog.isNull(crossOrigin)) { + this.image_.crossOrigin = crossOrigin; + } + + /** + * @private + * @type {Object.} + */ + this.imageByContext_ = {}; + + /** + * @private + * @type {Array.} + */ + this.imageListenerKeys_ = null; + + /** + * @protected + * @type {ol.ImageState} + */ + this.state = ol.ImageState.IDLE; +}; + + +/** + * @return {ol.Extent} Extent. + */ +ol.Image.prototype.getExtent = function() { + return this.extent_; +}; + + +/** + * @param {Object=} opt_context Object. + * @return {HTMLCanvasElement|Image|HTMLVideoElement} Image. + */ +ol.Image.prototype.getImageElement = function(opt_context) { + if (goog.isDef(opt_context)) { + var image; + var key = goog.getUid(opt_context); + if (key in this.imageByContext_) { + return this.imageByContext_[key]; + } else if (goog.object.isEmpty(this.imageByContext_)) { + image = this.image_; + } else { + image = /** @type {Image} */ (this.image_.cloneNode(false)); + } + this.imageByContext_[key] = image; + return image; + } else { + return this.image_; + } +}; + + +/** + * @return {number} Resolution. + */ +ol.Image.prototype.getResolution = function() { + return this.resolution_; +}; + + +/** + * @return {ol.ImageState} State. + */ +ol.Image.prototype.getState = function() { + return this.state; +}; + + +/** + * Tracks loading or read errors. + * + * @private + */ +ol.Image.prototype.handleImageError_ = function() { + this.state = ol.ImageState.ERROR; + this.unlistenImage_(); +}; + + +/** + * Tracks successful image load. + * + * @private + */ +ol.Image.prototype.handleImageLoad_ = function() { + this.state = ol.ImageState.LOADED; + this.unlistenImage_(); +}; + + +/** + * Load not yet loaded URI. + */ +ol.Image.prototype.load = function() { + if (this.state == ol.ImageState.IDLE) { + this.state = ol.ImageState.LOADING; + goog.asserts.assert(goog.isNull(this.imageListenerKeys_)); + this.imageListenerKeys_ = [ + goog.events.listenOnce(this.image_, goog.events.EventType.ERROR, + this.handleImageError_, false, this), + goog.events.listenOnce(this.image_, goog.events.EventType.LOAD, + this.handleImageLoad_, false, this) + ]; + this.image_.src = this.src_; + } +}; + + +/** + * Discards event handlers which listen for load completion or errors. + * + * @private + */ +ol.Image.prototype.unlistenImage_ = function() { + goog.asserts.assert(!goog.isNull(this.imageListenerKeys_)); + goog.array.forEach(this.imageListenerKeys_, goog.events.unlistenByKey); + this.imageListenerKeys_ = null; +}; From 27125640ef21ccc377e642f79ca81e3956bcdbb1 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Mon, 21 Jan 2013 17:44:21 +0100 Subject: [PATCH 04/20] Add ol.layer.ImageLayer --- src/ol/layer/imagelayer.exports | 1 + src/ol/layer/imagelayer.js | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 src/ol/layer/imagelayer.exports create mode 100644 src/ol/layer/imagelayer.js diff --git a/src/ol/layer/imagelayer.exports b/src/ol/layer/imagelayer.exports new file mode 100644 index 0000000000..f00c7ec5e5 --- /dev/null +++ b/src/ol/layer/imagelayer.exports @@ -0,0 +1 @@ +@exportClass ol.layer.ImageLayer ol.layer.LayerOptions diff --git a/src/ol/layer/imagelayer.js b/src/ol/layer/imagelayer.js new file mode 100644 index 0000000000..8b61efce0e --- /dev/null +++ b/src/ol/layer/imagelayer.js @@ -0,0 +1,24 @@ +goog.provide('ol.layer.ImageLayer'); + +goog.require('ol.layer.Layer'); +goog.require('ol.source.ImageSource'); + + + +/** + * @constructor + * @extends {ol.layer.Layer} + * @param {ol.layer.LayerOptions} layerOptions Layer options. + */ +ol.layer.ImageLayer = function(layerOptions) { + goog.base(this, layerOptions); +}; +goog.inherits(ol.layer.ImageLayer, ol.layer.Layer); + + +/** + * @return {ol.source.ImageSource} Single image source. + */ +ol.layer.ImageLayer.prototype.getImageSource = function() { + return /** @type {ol.source.ImageSource} */ (this.getSource()); +}; From 5a0adf2345d8fec1361028eeb217b431d80f6e6d Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Mon, 21 Jan 2013 17:44:06 +0100 Subject: [PATCH 05/20] Add ol.source.ImageSource --- src/ol/source/imagesource.js | 111 +++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 src/ol/source/imagesource.js diff --git a/src/ol/source/imagesource.js b/src/ol/source/imagesource.js new file mode 100644 index 0000000000..cd99fc40b1 --- /dev/null +++ b/src/ol/source/imagesource.js @@ -0,0 +1,111 @@ +goog.provide('ol.source.ImageSource'); + +goog.require('goog.array'); +goog.require('ol.Attribution'); +goog.require('ol.Extent'); +goog.require('ol.Image'); +goog.require('ol.ImageUrlFunction'); +goog.require('ol.ImageUrlFunctionType'); +goog.require('ol.Projection'); +goog.require('ol.Size'); +goog.require('ol.array'); +goog.require('ol.source.Source'); + + +/** + * @typedef {{attributions: (Array.|undefined), + * crossOrigin: (null|string|undefined), + * extent: (null|ol.Extent|undefined), + * projection: (ol.Projection|undefined), + * resolutions: (Array.|undefined), + * imageUrlFunction: (ol.ImageUrlFunctionType| + * undefined)}} + */ +ol.source.ImageSourceOptions; + + + +/** + * @constructor + * @extends {ol.source.Source} + * @param {ol.source.ImageSourceOptions} options Single + * image source options. + */ +ol.source.ImageSource = function(options) { + + goog.base(this, { + attributions: options.attributions, + extent: options.extent, + projection: options.projection + }); + + /** + * @protected + * @type {ol.ImageUrlFunctionType} + */ + this.imageUrlFunction = + goog.isDef(options.imageUrlFunction) ? + options.imageUrlFunction : + ol.ImageUrlFunction.nullImageUrlFunction; + + /** + * @private + * @type {?string} + */ + this.crossOrigin_ = + goog.isDef(options.crossOrigin) ? options.crossOrigin : 'anonymous'; + + /** + * @private + * @type {Array.} + */ + this.resolutions_ = goog.isDef(options.resolutions) ? + options.resolutions : null; + if (!goog.isNull(this.resolutions_)) { + goog.array.sort(this.resolutions_, function(a, b) {return b - a; }); + } + +}; +goog.inherits(ol.source.ImageSource, ol.source.Source); + + +/** + * @protected + * @param {ol.Extent} extent Extent. + * @param {number} resolution Resolution. + * @param {ol.Size} size Size. + * @return {ol.Image} Single image. + */ +ol.source.ImageSource.prototype.createImage = + function(extent, resolution, size) { + var image = null; + var imageUrl = this.imageUrlFunction(extent, size); + if (goog.isDef(imageUrl)) { + image = new ol.Image( + extent, resolution, imageUrl, this.crossOrigin_); + } + return image; +}; + + +/** + * @protected + * @param {number} resolution Resolution. + * @return {number} Resolution. + */ +ol.source.ImageSource.prototype.findNearestResolution = + function(resolution) { + if (!goog.isNull(this.resolutions_)) { + var idx = ol.array.linearFindNearest(this.resolutions_, resolution); + resolution = this.resolutions_[idx]; + } + return resolution; +}; + + +/** + * @param {ol.Extent} extent Extent. + * @param {number} resolution Resolution. + * @return {ol.Image} Single image. + */ +ol.source.ImageSource.prototype.getImage = goog.abstractMethod; From f581040b0f40a5c0920cc59f6dfbf7521a6a91a3 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Mon, 21 Jan 2013 17:44:52 +0100 Subject: [PATCH 06/20] Add ol.renderer.canvas.ImageLayer --- .../canvas/canvasimagelayerrenderer.js | 121 ++++++++++++++++++ src/ol/renderer/canvas/canvasmaprenderer.js | 6 +- 2 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 src/ol/renderer/canvas/canvasimagelayerrenderer.js diff --git a/src/ol/renderer/canvas/canvasimagelayerrenderer.js b/src/ol/renderer/canvas/canvasimagelayerrenderer.js new file mode 100644 index 0000000000..1b7857b9e2 --- /dev/null +++ b/src/ol/renderer/canvas/canvasimagelayerrenderer.js @@ -0,0 +1,121 @@ +goog.provide('ol.renderer.canvas.ImageLayer'); + +goog.require('goog.vec.Mat4'); +goog.require('ol.Image'); +goog.require('ol.ImageState'); +goog.require('ol.ViewHint'); +goog.require('ol.layer.ImageLayer'); +goog.require('ol.renderer.Map'); +goog.require('ol.renderer.canvas.Layer'); + + + +/** + * @constructor + * @extends {ol.renderer.canvas.Layer} + * @param {ol.renderer.Map} mapRenderer Map renderer. + * @param {ol.layer.ImageLayer} imageLayer Single image layer. + */ +ol.renderer.canvas.ImageLayer = function(mapRenderer, imageLayer) { + + goog.base(this, mapRenderer, imageLayer); + + /** + * @private + * @type {?ol.Image} + */ + this.image_ = null; + + /** + * @private + * @type {!goog.vec.Mat4.Number} + */ + this.transform_ = goog.vec.Mat4.createNumber(); + +}; +goog.inherits(ol.renderer.canvas.ImageLayer, ol.renderer.canvas.Layer); + + +/** + * @inheritDoc + */ +ol.renderer.canvas.ImageLayer.prototype.getImage = function() { + return goog.isNull(this.image_) ? + null : this.image_.getImageElement(this); +}; + + +/** + * @return {ol.layer.ImageLayer} Single image layer. + */ +ol.renderer.canvas.ImageLayer.prototype.getImageLayer = function() { + return /** @type {ol.layer.ImageLayer} */ (this.getLayer()); +}; + + +/** + * @inheritDoc + */ +ol.renderer.canvas.ImageLayer.prototype.getTransform = function() { + return this.transform_; +}; + + +/** + * @inheritDoc + */ +ol.renderer.canvas.ImageLayer.prototype.renderFrame = + function(frameState, layerState) { + + var view2DState = frameState.view2DState; + var viewCenter = view2DState.center; + var viewResolution = view2DState.resolution; + var viewRotation = view2DState.rotation; + + var image; + var imageLayer = this.getImageLayer(); + var imageSource = imageLayer.getImageSource(); + + var hints = frameState.viewHints; + + if (!hints[ol.ViewHint.ANIMATING] && !hints[ol.ViewHint.PANNING]) { + image = imageSource.getImage( + frameState.extent, viewResolution); + var imageState = image.getState(); + var animate = false; + if (imageState == ol.ImageState.ERROR) { + // pass + } else if (imageState == ol.ImageState.IDLE) { + animate = true; + image.load(); + } else if (imageState == ol.ImageState.LOADING) { + animate = true; + } else if (imageState == ol.ImageState.LOADED) { + this.image_ = image; + } + if (animate) { + frameState.animate = true; + } + } + + if (!goog.isNull(this.image_)) { + image = this.image_; + var imageExtent = image.getExtent(); + var imageResolution = image.getResolution(); + var transform = this.transform_; + goog.vec.Mat4.makeIdentity(transform); + goog.vec.Mat4.translate(transform, + frameState.size.width / 2, frameState.size.height / 2, 0); + goog.vec.Mat4.rotateZ(transform, viewRotation); + goog.vec.Mat4.scale( + transform, + imageResolution / viewResolution, + imageResolution / viewResolution, + 1); + goog.vec.Mat4.translate( + transform, + (imageExtent.minX - viewCenter.x) / imageResolution, + (viewCenter.y - imageExtent.maxY) / imageResolution, + 0); + } +}; diff --git a/src/ol/renderer/canvas/canvasmaprenderer.js b/src/ol/renderer/canvas/canvasmaprenderer.js index bd0b3dbfe3..24ee38307f 100644 --- a/src/ol/renderer/canvas/canvasmaprenderer.js +++ b/src/ol/renderer/canvas/canvasmaprenderer.js @@ -7,8 +7,10 @@ goog.require('goog.dom'); goog.require('goog.style'); goog.require('goog.vec.Mat4'); goog.require('ol.Size'); +goog.require('ol.layer.ImageLayer'); goog.require('ol.layer.TileLayer'); goog.require('ol.renderer.Map'); +goog.require('ol.renderer.canvas.ImageLayer'); goog.require('ol.renderer.canvas.TileLayer'); @@ -59,7 +61,9 @@ goog.inherits(ol.renderer.canvas.Map, ol.renderer.Map); * @inheritDoc */ ol.renderer.canvas.Map.prototype.createLayerRenderer = function(layer) { - if (layer instanceof ol.layer.TileLayer) { + if (layer instanceof ol.layer.ImageLayer) { + return new ol.renderer.canvas.ImageLayer(this, layer); + } else if (layer instanceof ol.layer.TileLayer) { return new ol.renderer.canvas.TileLayer(this, layer); } else { goog.asserts.assert(false); From 35a6cac37f9486c3500576b3e0a197458285a6d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Mon, 28 Jan 2013 12:25:04 +0100 Subject: [PATCH 07/20] Add containsExtent to ol.Extent --- src/ol/extent.js | 18 ++++++++- test/spec/ol/extent.test.js | 77 ++++++++++++++++++++++++------------- 2 files changed, 67 insertions(+), 28 deletions(-) diff --git a/src/ol/extent.js b/src/ol/extent.js index 3d0ab215d6..3606dfc63c 100644 --- a/src/ol/extent.js +++ b/src/ol/extent.js @@ -46,17 +46,31 @@ ol.Extent.boundingExtent = function(var_args) { /** - * Checks if the given coordinate is contained or on the edge of the extent. + * Checks if the passed coordinate is contained or on the edge + * of the extent. * * @param {ol.Coordinate} coordinate Coordinate. * @return {boolean} Contains. */ -ol.Extent.prototype.contains = function(coordinate) { +ol.Extent.prototype.containsCoordinate = function(coordinate) { return this.minX <= coordinate.x && coordinate.x <= this.maxX && this.minY <= coordinate.y && coordinate.y <= this.maxY; }; +/** + * Checks if the passed extent is contained or on the edge of the + * extent. + * + * @param {ol.Extent} extent Extent. + * @return {boolean} Contains. + */ +ol.Extent.prototype.containsExtent = function(extent) { + return this.minX <= extent.minX && extent.maxX <= this.maxX && + this.minY <= extent.minY && extent.maxY <= this.maxY; +}; + + /** * @return {ol.Coordinate} Bottom left coordinate. */ diff --git a/test/spec/ol/extent.test.js b/test/spec/ol/extent.test.js index 2a2d98e9f3..de00b63e18 100644 --- a/test/spec/ol/extent.test.js +++ b/test/spec/ol/extent.test.js @@ -2,42 +2,67 @@ goog.provide('ol.test.Extent'); describe('ol.Extent', function() { - describe('contains', function() { + describe('containsCoordinate', function() { describe('positive', function() { it('returns true', function() { var extent = new ol.Extent(1, 2, 3, 4); - expect(extent.contains(new ol.Coordinate(1, 2))).toBeTruthy(); - expect(extent.contains(new ol.Coordinate(1, 3))).toBeTruthy(); - expect(extent.contains(new ol.Coordinate(1, 4))).toBeTruthy(); - expect(extent.contains(new ol.Coordinate(2, 2))).toBeTruthy(); - expect(extent.contains(new ol.Coordinate(2, 3))).toBeTruthy(); - expect(extent.contains(new ol.Coordinate(2, 4))).toBeTruthy(); - expect(extent.contains(new ol.Coordinate(3, 2))).toBeTruthy(); - expect(extent.contains(new ol.Coordinate(3, 3))).toBeTruthy(); - expect(extent.contains(new ol.Coordinate(3, 4))).toBeTruthy(); + expect(extent.containsCoordinate( + new ol.Coordinate(1, 2))).toBeTruthy(); + expect(extent.containsCoordinate( + new ol.Coordinate(1, 3))).toBeTruthy(); + expect(extent.containsCoordinate( + new ol.Coordinate(1, 4))).toBeTruthy(); + expect(extent.containsCoordinate( + new ol.Coordinate(2, 2))).toBeTruthy(); + expect(extent.containsCoordinate( + new ol.Coordinate(2, 3))).toBeTruthy(); + expect(extent.containsCoordinate( + new ol.Coordinate(2, 4))).toBeTruthy(); + expect(extent.containsCoordinate( + new ol.Coordinate(3, 2))).toBeTruthy(); + expect(extent.containsCoordinate( + new ol.Coordinate(3, 3))).toBeTruthy(); + expect(extent.containsCoordinate( + new ol.Coordinate(3, 4))).toBeTruthy(); }); }); describe('negative', function() { it('returns false', function() { var extent = new ol.Extent(1, 2, 3, 4); - expect(extent.contains(new ol.Coordinate(0, 1))).toBeFalsy(); - expect(extent.contains(new ol.Coordinate(0, 2))).toBeFalsy(); - expect(extent.contains(new ol.Coordinate(0, 3))).toBeFalsy(); - expect(extent.contains(new ol.Coordinate(0, 4))).toBeFalsy(); - expect(extent.contains(new ol.Coordinate(0, 5))).toBeFalsy(); - expect(extent.contains(new ol.Coordinate(1, 1))).toBeFalsy(); - expect(extent.contains(new ol.Coordinate(1, 5))).toBeFalsy(); - expect(extent.contains(new ol.Coordinate(2, 1))).toBeFalsy(); - expect(extent.contains(new ol.Coordinate(2, 5))).toBeFalsy(); - expect(extent.contains(new ol.Coordinate(3, 1))).toBeFalsy(); - expect(extent.contains(new ol.Coordinate(3, 5))).toBeFalsy(); - expect(extent.contains(new ol.Coordinate(4, 1))).toBeFalsy(); - expect(extent.contains(new ol.Coordinate(4, 2))).toBeFalsy(); - expect(extent.contains(new ol.Coordinate(4, 3))).toBeFalsy(); - expect(extent.contains(new ol.Coordinate(4, 4))).toBeFalsy(); - expect(extent.contains(new ol.Coordinate(4, 5))).toBeFalsy(); + expect(extent.containsCoordinate( + new ol.Coordinate(0, 1))).toBeFalsy(); + expect(extent.containsCoordinate( + new ol.Coordinate(0, 2))).toBeFalsy(); + expect(extent.containsCoordinate( + new ol.Coordinate(0, 3))).toBeFalsy(); + expect(extent.containsCoordinate( + new ol.Coordinate(0, 4))).toBeFalsy(); + expect(extent.containsCoordinate( + new ol.Coordinate(0, 5))).toBeFalsy(); + expect(extent.containsCoordinate( + new ol.Coordinate(1, 1))).toBeFalsy(); + expect(extent.containsCoordinate( + new ol.Coordinate(1, 5))).toBeFalsy(); + expect(extent.containsCoordinate( + new ol.Coordinate(2, 1))).toBeFalsy(); + expect(extent.containsCoordinate( + new ol.Coordinate(2, 5))).toBeFalsy(); + expect(extent.containsCoordinate( + new ol.Coordinate(3, 1))).toBeFalsy(); + expect(extent.containsCoordinate( + new ol.Coordinate(3, 5))).toBeFalsy(); + expect(extent.containsCoordinate( + new ol.Coordinate(4, 1))).toBeFalsy(); + expect(extent.containsCoordinate( + new ol.Coordinate(4, 2))).toBeFalsy(); + expect(extent.containsCoordinate( + new ol.Coordinate(4, 3))).toBeFalsy(); + expect(extent.containsCoordinate( + new ol.Coordinate(4, 4))).toBeFalsy(); + expect(extent.containsCoordinate( + new ol.Coordinate(4, 5))).toBeFalsy(); }); }); }); From aa9f820723f696f1b8868d5f1e24884b15b77357 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Tue, 29 Jan 2013 15:41:27 +0100 Subject: [PATCH 08/20] Add scale to ol.Rectangle --- src/ol/rectangle.js | 13 +++++++++++++ test/spec/ol/rectangle.test.js | 11 +++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/ol/rectangle.js b/src/ol/rectangle.js index a459d04603..c7d9cab121 100644 --- a/src/ol/rectangle.js +++ b/src/ol/rectangle.js @@ -124,3 +124,16 @@ ol.Rectangle.prototype.normalize = function(coordinate) { ol.Rectangle.prototype.toString = function() { return '(' + [this.minX, this.minY, this.maxX, this.maxY].join(', ') + ')'; }; + + +/** + * @param {number} value Value. + */ +ol.Rectangle.prototype.scale = function(value) { + var deltaX = (this.getWidth() / 2.0) * (value - 1); + var deltaY = (this.getHeight() / 2.0) * (value - 1); + this.minX -= deltaX; + this.minY -= deltaY; + this.maxX += deltaX; + this.maxY += deltaY; +}; diff --git a/test/spec/ol/rectangle.test.js b/test/spec/ol/rectangle.test.js index 55d8882e54..5f882621ca 100644 --- a/test/spec/ol/rectangle.test.js +++ b/test/spec/ol/rectangle.test.js @@ -98,6 +98,17 @@ describe('ol.Rectangle', function() { }); }); + describe('scale', function() { + it('scales the extent', function() { + var rectangle = new ol.Rectangle(1, 1, 3, 3); + rectangle.scale(2); + expect(rectangle.minX).toEqual(0); + expect(rectangle.minY).toEqual(0); + expect(rectangle.maxX).toEqual(4); + expect(rectangle.maxY).toEqual(4); + }); + }); + }); goog.require('ol.Coordinate'); From 62b10cf87880bc58f50ce0ceab59558104fedefd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Tue, 22 Jan 2013 17:38:50 +0100 Subject: [PATCH 09/20] Add ol.source.SingleImageWMS --- src/objectliterals.exports | 10 +++ src/ol/source/singleimagewms.exports | 1 + src/ol/source/singleimagewmssource.js | 99 +++++++++++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 src/ol/source/singleimagewms.exports create mode 100644 src/ol/source/singleimagewmssource.js diff --git a/src/objectliterals.exports b/src/objectliterals.exports index cf5e830f3f..a6ce462790 100644 --- a/src/objectliterals.exports +++ b/src/objectliterals.exports @@ -92,6 +92,16 @@ @exportObjectLiteralProperty ol.source.DebugTileSourceOptions.projection ol.Projection|undefined @exportObjectLiteralProperty ol.source.DebugTileSourceOptions.tileGrid ol.tilegrid.TileGrid|undefined +@exportObjectLiteral ol.source.SingleImageWMSOptions +@exportObjectLiteralProperty ol.source.SingleImageWMSOptions.attributions Array.|undefined +@exportObjectLiteralProperty ol.source.SingleImageWMSOptions.crossOrigin null|string|undefined +@exportObjectLiteralProperty ol.source.SingleImageWMSOptions.extent ol.Extent|undefined +@exportObjectLiteralProperty ol.source.SingleImageWMSOptions.params Object. +@exportObjectLiteralProperty ol.source.SingleImageWMSOptions.projection ol.Projection|undefined +@exportObjectLiteralProperty ol.source.SingleImageWMSOptions.resolutions Array.|undefined +@exportObjectLiteralProperty ol.source.SingleImageWMSOptions.url string|undefined +@exportObjectLiteralProperty ol.source.SingleImageWMSOptions.version string|undefined + @exportObjectLiteral ol.source.StamenOptions @exportObjectLiteralProperty ol.source.StamenOptions.flavor string|undefined @exportObjectLiteralProperty ol.source.StamenOptions.provider string diff --git a/src/ol/source/singleimagewms.exports b/src/ol/source/singleimagewms.exports new file mode 100644 index 0000000000..4d79ecdabc --- /dev/null +++ b/src/ol/source/singleimagewms.exports @@ -0,0 +1 @@ +@exportSymbol ol.source.SingleImageWMS diff --git a/src/ol/source/singleimagewmssource.js b/src/ol/source/singleimagewmssource.js new file mode 100644 index 0000000000..ec8ce0c329 --- /dev/null +++ b/src/ol/source/singleimagewmssource.js @@ -0,0 +1,99 @@ +goog.provide('ol.source.SingleImageWMS'); + +goog.require('ol.Extent'); +goog.require('ol.Image'); +goog.require('ol.ImageUrlFunction'); +goog.require('ol.Projection'); +goog.require('ol.Size'); +goog.require('ol.source.ImageSource'); + + + +/** + * @constructor + * @extends {ol.source.ImageSource} + * @param {ol.source.SingleImageWMSOptions} options Options. + */ +ol.source.SingleImageWMS = function(options) { + + var projection = ol.Projection.createProjection( + options.projection, 'EPSG:3857'); + var projectionExtent = projection.getExtent(); + + var extent = goog.isDef(options.extent) ? + options.extent : projectionExtent; + + var version = goog.isDef(options.version) ? + options.version : '1.3'; + + var baseParams = { + 'SERVICE': 'WMS', + 'VERSION': version, + 'REQUEST': 'GetMap', + 'STYLES': '', + 'FORMAT': 'image/png', + 'TRANSPARENT': true + }; + baseParams[version >= '1.3' ? 'CRS' : 'SRS'] = projection.getCode(); + goog.object.extend(baseParams, options.params); + + var imageUrlFunction; + if (options.url) { + var url = goog.uri.utils.appendParamsFromMap( + options.url, baseParams); + imageUrlFunction = ol.ImageUrlFunction.createBboxParam(url); + } else { + imageUrlFunction = + ol.ImageUrlFunction.nullImageUrlFunction; + } + + goog.base(this, { + attributions: options.attributions, + crossOrigin: options.crossOrigin, + extent: extent, + projection: projection, + resolutions: options.resolutions, + imageUrlFunction: imageUrlFunction + }); + + /** + * @private + * @type {ol.Image} + */ + this.image_ = null; + + /** + * FIXME configurable? + * @private + * @type {number} + */ + this.ratio_ = 1.5; + +}; +goog.inherits(ol.source.SingleImageWMS, ol.source.ImageSource); + + +/** + * @inheritDoc + */ +ol.source.SingleImageWMS.prototype.getImage = + function(extent, resolution) { + resolution = this.findNearestResolution(resolution); + + var image = this.image_; + if (!goog.isNull(image) && + image.getResolution() == resolution && + image.getExtent().containsExtent(extent)) { + return image; + } + + extent = new ol.Extent(extent.minX, extent.minY, + extent.maxX, extent.maxY); + extent.scale(this.ratio_); + var width = extent.getWidth() / resolution; + var height = extent.getHeight() / resolution; + var size = new ol.Size(width, height); + + this.image_ = this.createImage(extent, resolution, size); + return this.image_; +}; From 8ae25684f9a74cb89c7e65d1950e1da04bce4419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Mon, 28 Jan 2013 13:03:56 +0100 Subject: [PATCH 10/20] Add ol.source.StaticImage --- src/objectliterals.exports | 9 +++++ src/ol/source/staticimage.exports | 1 + src/ol/source/staticimagesource.js | 61 ++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 src/ol/source/staticimage.exports create mode 100644 src/ol/source/staticimagesource.js diff --git a/src/objectliterals.exports b/src/objectliterals.exports index a6ce462790..0e24207a11 100644 --- a/src/objectliterals.exports +++ b/src/objectliterals.exports @@ -106,6 +106,15 @@ @exportObjectLiteralProperty ol.source.StamenOptions.flavor string|undefined @exportObjectLiteralProperty ol.source.StamenOptions.provider string +@exportObjectLiteral ol.source.StaticImageOptions +@exportObjectLiteralProperty ol.source.StaticImageOptions.attributions Array.|undefined +@exportObjectLiteralProperty ol.source.StaticImageOptions.crossOrigin null|string|undefined +@exportObjectLiteralProperty ol.source.StaticImageOptions.extent ol.Extent|undefined +@exportObjectLiteralProperty ol.source.StaticImageOptions.imageExtent ol.Extent|undefined +@exportObjectLiteralProperty ol.source.StaticImageOptions.imageSize ol.Size|undefined +@exportObjectLiteralProperty ol.source.StaticImageOptions.projection ol.Projection|undefined +@exportObjectLiteralProperty ol.source.StaticImageOptions.url string|undefined + @exportObjectLiteral ol.source.TiledWMSOptions @exportObjectLiteralProperty ol.source.TiledWMSOptions.attributions Array.|undefined @exportObjectLiteralProperty ol.source.TiledWMSOptions.params Object diff --git a/src/ol/source/staticimage.exports b/src/ol/source/staticimage.exports new file mode 100644 index 0000000000..3f7b2c98a5 --- /dev/null +++ b/src/ol/source/staticimage.exports @@ -0,0 +1 @@ +@exportSymbol ol.source.StaticImage diff --git a/src/ol/source/staticimagesource.js b/src/ol/source/staticimagesource.js new file mode 100644 index 0000000000..eafa205eb1 --- /dev/null +++ b/src/ol/source/staticimagesource.js @@ -0,0 +1,61 @@ +goog.provide('ol.source.StaticImage'); + +goog.require('ol.Image'); +goog.require('ol.ImageUrlFunctionType'); +goog.require('ol.source.ImageSource'); + + + +/** + * @constructor + * @extends {ol.source.ImageSource} + * @param {ol.source.StaticImageOptions} options Options. + */ +ol.source.StaticImage = function(options) { + + var imageFunction = ol.source.StaticImage.createImageFunction( + options.url); + + var imageExtent = options.imageExtent; + var imageSize = options.imageSize; + var imageResolution = imageExtent.getHeight() / imageSize.height; + + goog.base(this, { + attributions: options.attributions, + crossOrigin: options.crossOrigin, + extent: options.extent, + projection: options.projection, + imageUrlFunction: imageFunction, + resolutions: [imageResolution] + }); + + /** + * @private + * @type {ol.Image} + */ + this.image_ = this.createImage(imageExtent, imageResolution, imageSize); + +}; +goog.inherits(ol.source.StaticImage, ol.source.ImageSource); + + +/** + * @inheritDoc + */ +ol.source.StaticImage.prototype.getImage = function(extent, resolution) { + if (extent.intersects(this.image_.getExtent())) { + return this.image_; + } + return null; +}; + + +/** + * @param {string|undefined} url URL. + * @return {ol.ImageUrlFunctionType} Function. + */ +ol.source.StaticImage.createImageFunction = function(url) { + return function(extent, size) { + return url; + }; +}; From 238c6952650ff0913e83c1cb42d2808c1aa251a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Mon, 28 Jan 2013 13:04:35 +0100 Subject: [PATCH 11/20] Add FIXME to ol.control.Attribution --- src/ol/control/attributioncontrol.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ol/control/attributioncontrol.js b/src/ol/control/attributioncontrol.js index d6f707ea56..731e2b9435 100644 --- a/src/ol/control/attributioncontrol.js +++ b/src/ol/control/attributioncontrol.js @@ -1,4 +1,5 @@ // FIXME handle date line wrap +// FIXME does not handle image sources goog.provide('ol.control.Attribution'); From 17a17b01b66d441044f9ce53cb6400c483bee208 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Mon, 28 Jan 2013 16:24:45 +0100 Subject: [PATCH 12/20] Add ol.renderer.dom.ImageLayer --- src/ol/renderer/dom/domimagelayerrenderer.js | 127 +++++++++++++++++++ src/ol/renderer/dom/dommaprenderer.js | 15 ++- 2 files changed, 136 insertions(+), 6 deletions(-) create mode 100644 src/ol/renderer/dom/domimagelayerrenderer.js diff --git a/src/ol/renderer/dom/domimagelayerrenderer.js b/src/ol/renderer/dom/domimagelayerrenderer.js new file mode 100644 index 0000000000..eda01022e1 --- /dev/null +++ b/src/ol/renderer/dom/domimagelayerrenderer.js @@ -0,0 +1,127 @@ +goog.provide('ol.renderer.dom.ImageLayer'); + +goog.require('goog.dom'); +goog.require('goog.vec.Mat4'); +goog.require('ol.Image'); +goog.require('ol.ImageState'); +goog.require('ol.ViewHint'); +goog.require('ol.dom'); +goog.require('ol.layer.ImageLayer'); +goog.require('ol.renderer.dom.Layer'); + + + +/** + * @constructor + * @extends {ol.renderer.dom.Layer} + * @param {ol.renderer.Map} mapRenderer Map renderer. + * @param {ol.layer.ImageLayer} imageLayer Image layer. + */ +ol.renderer.dom.ImageLayer = function(mapRenderer, imageLayer) { + var target = goog.dom.createElement(goog.dom.TagName.DIV); + target.className = 'ol-layer-image'; + target.style.position = 'absolute'; + + goog.base(this, mapRenderer, imageLayer, target); + + /** + * The last rendered image. + * @private + * @type {?ol.Image} + */ + this.image_ = null; + + /** + * @private + * @type {goog.vec.Mat4.AnyType} + */ + this.transform_ = goog.vec.Mat4.createNumberIdentity(); + +}; +goog.inherits(ol.renderer.dom.ImageLayer, ol.renderer.dom.Layer); + + +/** + * @return {ol.layer.ImageLayer} Image layer. + */ +ol.renderer.dom.ImageLayer.prototype.getImageLayer = function() { + return /** @type {ol.layer.ImageLayer} */ (this.getLayer()); +}; + + +/** + * @inheritDoc + */ +ol.renderer.dom.ImageLayer.prototype.renderFrame = + function(frameState, layerState) { + + var view2DState = frameState.view2DState; + var viewCenter = view2DState.center; + var viewResolution = view2DState.resolution; + var viewRotation = view2DState.rotation; + + var image = this.image_; + var imageLayer = this.getImageLayer(); + var imageSource = imageLayer.getImageSource(); + + var hints = frameState.viewHints; + + if (!hints[ol.ViewHint.ANIMATING] && !hints[ol.ViewHint.PANNING]) { + var image_ = imageSource.getImage(frameState.extent, viewResolution); + var imageState = image_.getState(); + var animate = false; + if (imageState == ol.ImageState.ERROR) { + // pass + } else if (imageState == ol.ImageState.IDLE) { + animate = true; + image_.load(); + } else if (imageState == ol.ImageState.LOADING) { + animate = true; + } else if (imageState == ol.ImageState.LOADED) { + image = image_; + } + if (animate) { + frameState.animate = true; + } + } + + if (!goog.isNull(image)) { + var imageExtent = image.getExtent(); + var imageResolution = image.getResolution(); + var transform = goog.vec.Mat4.createNumber(); + goog.vec.Mat4.makeIdentity(transform); + goog.vec.Mat4.translate(transform, + frameState.size.width / 2, frameState.size.height / 2, 0); + goog.vec.Mat4.rotateZ(transform, viewRotation); + goog.vec.Mat4.scale( + transform, + imageResolution / viewResolution, + imageResolution / viewResolution, + 1); + goog.vec.Mat4.translate( + transform, + (imageExtent.minX - viewCenter.x) / imageResolution, + (viewCenter.y - imageExtent.maxY) / imageResolution, + 0); + if (image != this.image_) { + var imageElement = image.getImageElement(this); + imageElement.style.position = 'absolute'; + goog.dom.removeChildren(this.target); + goog.dom.appendChild(this.target, imageElement); + this.image_ = image; + } + this.setTransform(transform); + } + +}; + + +/** + * @param {goog.vec.Mat4.AnyType} transform Transform. + */ +ol.renderer.dom.ImageLayer.prototype.setTransform = function(transform) { + if (!goog.vec.Mat4.equals(transform, this.transform_)) { + ol.dom.transformElement2D(this.target, transform, 6); + goog.vec.Mat4.setFromArray(this.transform_, transform); + } +}; diff --git a/src/ol/renderer/dom/dommaprenderer.js b/src/ol/renderer/dom/dommaprenderer.js index 18700d9b25..a833d5fd91 100644 --- a/src/ol/renderer/dom/dommaprenderer.js +++ b/src/ol/renderer/dom/dommaprenderer.js @@ -5,8 +5,10 @@ goog.require('goog.asserts'); goog.require('goog.dom'); goog.require('goog.dom.TagName'); goog.require('goog.style'); +goog.require('ol.layer.ImageLayer'); goog.require('ol.layer.TileLayer'); goog.require('ol.renderer.Map'); +goog.require('ol.renderer.dom.ImageLayer'); goog.require('ol.renderer.dom.TileLayer'); @@ -57,14 +59,15 @@ ol.renderer.dom.Map.prototype.addLayer = function(layer) { * @inheritDoc */ ol.renderer.dom.Map.prototype.createLayerRenderer = function(layer) { + var layerRenderer; if (layer instanceof ol.layer.TileLayer) { - var layerRenderer = new ol.renderer.dom.TileLayer(this, layer); - goog.dom.appendChild(this.layersPane_, layerRenderer.getTarget()); - return layerRenderer; - } else { - goog.asserts.assert(false); - return null; + layerRenderer = new ol.renderer.dom.TileLayer(this, layer); + } else if (layer instanceof ol.layer.ImageLayer) { + layerRenderer = new ol.renderer.dom.ImageLayer(this, layer); } + goog.asserts.assert(goog.isDef(layerRenderer)); + goog.dom.appendChild(this.layersPane_, layerRenderer.getTarget()); + return layerRenderer; }; From 02843939e353cee321d100b0163375a2f3a15058 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Wed, 6 Feb 2013 08:13:04 +0100 Subject: [PATCH 13/20] Add ol.renderer.webgl.ImageLayer --- .../renderer/webgl/webglimagelayerrenderer.js | 220 ++++++++++++++++++ src/ol/renderer/webgl/webgllayerrenderer.js | 6 + src/ol/renderer/webgl/webglmaprenderer.js | 19 +- .../renderer/webgl/webgltilelayerrenderer.js | 14 ++ 4 files changed, 255 insertions(+), 4 deletions(-) create mode 100644 src/ol/renderer/webgl/webglimagelayerrenderer.js diff --git a/src/ol/renderer/webgl/webglimagelayerrenderer.js b/src/ol/renderer/webgl/webglimagelayerrenderer.js new file mode 100644 index 0000000000..c259b02b99 --- /dev/null +++ b/src/ol/renderer/webgl/webglimagelayerrenderer.js @@ -0,0 +1,220 @@ +goog.provide('ol.renderer.webgl.ImageLayer'); + +goog.require('goog.vec.Mat4'); +goog.require('ol.Image'); +goog.require('ol.ImageState'); +goog.require('ol.ViewHint'); +goog.require('ol.layer.ImageLayer'); +goog.require('ol.renderer.webgl.Layer'); + + + +/** + * @constructor + * @extends {ol.renderer.webgl.Layer} + * @param {ol.renderer.Map} mapRenderer Map renderer. + * @param {ol.layer.ImageLayer} imageLayer Tile layer. + */ +ol.renderer.webgl.ImageLayer = function(mapRenderer, imageLayer) { + + goog.base(this, mapRenderer, imageLayer); + + /** + * The last rendered image. + * @private + * @type {?ol.Image} + */ + this.image_ = null; + + /** + * The last rendered texture. + * @private + * @type {WebGLTexture} + */ + this.texture_ = null; + + /** + * @private + * @type {!goog.vec.Mat4.Number} + */ + this.texCoordMatrix_ = goog.vec.Mat4.createNumberIdentity(); + + /** + * @private + * @type {!goog.vec.Mat4.Number} + */ + this.vertexCoordMatrix_ = goog.vec.Mat4.createNumber(); + +}; +goog.inherits(ol.renderer.webgl.ImageLayer, ol.renderer.webgl.Layer); + + +/** + * @private + * @param {ol.Image} image Image. + * @return {WebGLTexture} Texture. + */ +ol.renderer.webgl.ImageLayer.prototype.createTexture_ = function(image) { + + // We meet the conditions to work with non-power of two textures. + // http://www.khronos.org/webgl/wiki/WebGL_and_OpenGL_Differences#Non-Power_of_Two_Texture_Support + // http://learningwebgl.com/blog/?p=2101 + + var imageElement = image.getImageElement(this); + var gl = this.getMapRenderer().getGL(); + + var texture = gl.createTexture(); + + gl.bindTexture(goog.webgl.TEXTURE_2D, texture); + gl.texImage2D(goog.webgl.TEXTURE_2D, 0, goog.webgl.RGBA, + goog.webgl.RGBA, goog.webgl.UNSIGNED_BYTE, imageElement); + + gl.texParameteri( + goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_WRAP_S, + goog.webgl.CLAMP_TO_EDGE); + gl.texParameteri( + goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_WRAP_T, + goog.webgl.CLAMP_TO_EDGE); + gl.texParameteri( + goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MIN_FILTER, goog.webgl.LINEAR); + gl.texParameteri( + goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MAG_FILTER, goog.webgl.LINEAR); + + return texture; +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.ImageLayer.prototype.disposeInternal = function() { + var mapRenderer = this.getMapRenderer(); + var gl = mapRenderer.getGL(); + if (!gl.isContextLost()) { + gl.deleteTexture(this.texture_); + } + goog.base(this, 'disposeInternal'); +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.ImageLayer.prototype.getTexCoordMatrix = function() { + return this.texCoordMatrix_; +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.ImageLayer.prototype.getTexture = function() { + return this.texture_; +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.ImageLayer.prototype.getVertexCoordMatrix = function() { + return this.vertexCoordMatrix_; +}; + + +/** + * @return {ol.layer.ImageLayer} Tile layer. + */ +ol.renderer.webgl.ImageLayer.prototype.getImageLayer = function() { + return /** @type {ol.layer.ImageLayer} */ (this.getLayer()); +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.ImageLayer.prototype.handleWebGLContextLost = function() { + this.texture_ = null; +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.ImageLayer.prototype.renderFrame = + function(frameState, layerState) { + + var gl = this.getMapRenderer().getGL(); + + var view2DState = frameState.view2DState; + var viewCenter = view2DState.center; + var viewResolution = view2DState.resolution; + var viewRotation = view2DState.rotation; + + var image = this.image_; + var texture = this.texture_; + var imageLayer = this.getImageLayer(); + var imageSource = imageLayer.getImageSource(); + + var hints = frameState.viewHints; + + if (!hints[ol.ViewHint.ANIMATING] && !hints[ol.ViewHint.PANNING]) { + var image_ = imageSource.getImage(frameState.extent, viewResolution); + var imageState = image_.getState(); + var animate = false; + if (imageState == ol.ImageState.ERROR) { + // pass + } else if (imageState == ol.ImageState.IDLE) { + animate = true; + image_.load(); + } else if (imageState == ol.ImageState.LOADING) { + animate = true; + } else if (imageState == ol.ImageState.LOADED) { + image = image_; + texture = this.createTexture_(image_); + if (!goog.isNull(this.texture_)) { + frameState.postRenderFunctions.push( + goog.partial(function(gl, texture) { + if (!gl.isContextLost()) { + gl.deleteTexture(texture); + } + }, gl, this.texture_)); + } + } + if (animate) { + frameState.animate = true; + } + } + + if (!goog.isNull(image)) { + goog.asserts.assert(!goog.isNull(texture)); + + var canvas = this.getMapRenderer().getCanvas(); + + var canvasExtentWidth = canvas.width * viewResolution; + var canvasExtentHeight = canvas.height * viewResolution; + + var imageExtent = image.getExtent(); + + var vertexCoordMatrix = this.vertexCoordMatrix_; + goog.vec.Mat4.makeIdentity(vertexCoordMatrix); + goog.vec.Mat4.scale(vertexCoordMatrix, + 2 / canvasExtentWidth, 2 / canvasExtentHeight, 1); + goog.vec.Mat4.rotateZ(vertexCoordMatrix, -viewRotation); + goog.vec.Mat4.translate(vertexCoordMatrix, + imageExtent.minX - viewCenter.x, + imageExtent.minY - viewCenter.y, + 0); + goog.vec.Mat4.scale(vertexCoordMatrix, + imageExtent.getWidth() / 2, imageExtent.getHeight() / 2, 1); + goog.vec.Mat4.translate(vertexCoordMatrix, 1, 1, 0); + + // Translate and scale to flip the Y coord. + var texCoordMatrix = this.texCoordMatrix_; + goog.vec.Mat4.makeIdentity(texCoordMatrix); + goog.vec.Mat4.scale(texCoordMatrix, 1, -1, 1); + goog.vec.Mat4.translate(texCoordMatrix, 0, -1, 0); + + this.image_ = image; + this.texture_ = texture; + } +}; diff --git a/src/ol/renderer/webgl/webgllayerrenderer.js b/src/ol/renderer/webgl/webgllayerrenderer.js index 238462410f..067a4e054c 100644 --- a/src/ol/renderer/webgl/webgllayerrenderer.js +++ b/src/ol/renderer/webgl/webgllayerrenderer.js @@ -97,6 +97,12 @@ ol.renderer.webgl.Layer.prototype.getTexCoordMatrix = goog.abstractMethod; ol.renderer.webgl.Layer.prototype.getTexture = goog.abstractMethod; +/** + * @return {!goog.vec.Mat4.Number} Matrix. + */ +ol.renderer.webgl.Layer.prototype.getVertexCoordMatrix = goog.abstractMethod; + + /** * @inheritDoc */ diff --git a/src/ol/renderer/webgl/webglmaprenderer.js b/src/ol/renderer/webgl/webglmaprenderer.js index f7a553f3f2..62b03fc489 100644 --- a/src/ol/renderer/webgl/webglmaprenderer.js +++ b/src/ol/renderer/webgl/webglmaprenderer.js @@ -14,9 +14,11 @@ goog.require('goog.webgl'); goog.require('ol.FrameState'); goog.require('ol.Size'); goog.require('ol.Tile'); +goog.require('ol.layer.ImageLayer'); goog.require('ol.layer.TileLayer'); goog.require('ol.renderer.Map'); goog.require('ol.renderer.webgl.FragmentShader'); +goog.require('ol.renderer.webgl.ImageLayer'); goog.require('ol.renderer.webgl.TileLayer'); goog.require('ol.renderer.webgl.VertexShader'); goog.require('ol.structs.LRUCache'); @@ -79,11 +81,12 @@ ol.renderer.webgl.map.shader.Vertex = function() { 'attribute vec2 aTexCoord;', '', 'uniform mat4 uTexCoordMatrix;', + 'uniform mat4 uVertexCoordMatrix;', '', 'varying vec2 vTexCoord;', '', 'void main(void) {', - ' gl_Position = vec4(aPosition, 0., 1.);', + ' gl_Position = uVertexCoordMatrix * vec4(aPosition, 0., 1.);', ' vTexCoord = (uTexCoordMatrix * vec4(aTexCoord, 0., 1.)).st;', '}' ].join('\n')); @@ -159,7 +162,8 @@ ol.renderer.webgl.Map = function(container, map) { * uColorMatrix: WebGLUniformLocation, * uOpacity: WebGLUniformLocation, * uTexture: WebGLUniformLocation, - * uTexCoordMatrix: WebGLUniformLocation}|null} + * uTexCoordMatrix: WebGLUniformLocation, + * uVertexCoordMatrix: WebGLUniformLocation}|null} */ this.locations_ = null; @@ -270,12 +274,15 @@ ol.renderer.webgl.Map.prototype.bindTileTexture = * @inheritDoc */ ol.renderer.webgl.Map.prototype.createLayerRenderer = function(layer) { + var layerRenderer = null; if (layer instanceof ol.layer.TileLayer) { - return new ol.renderer.webgl.TileLayer(this, layer); + layerRenderer = new ol.renderer.webgl.TileLayer(this, layer); + } else if (layer instanceof ol.layer.ImageLayer) { + layerRenderer = new ol.renderer.webgl.ImageLayer(this, layer); } else { goog.asserts.assert(false); - return null; } + return layerRenderer; }; @@ -519,6 +526,7 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) { aTexCoord: gl.getAttribLocation(program, 'aTexCoord'), uColorMatrix: gl.getUniformLocation(program, 'uColorMatrix'), uTexCoordMatrix: gl.getUniformLocation(program, 'uTexCoordMatrix'), + uVertexCoordMatrix: gl.getUniformLocation(program, 'uVertexCoordMatrix'), uOpacity: gl.getUniformLocation(program, 'uOpacity'), uTexture: gl.getUniformLocation(program, 'uTexture') }; @@ -555,6 +563,9 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) { gl.uniformMatrix4fv( this.locations_.uTexCoordMatrix, false, layerRenderer.getTexCoordMatrix()); + gl.uniformMatrix4fv( + this.locations_.uVertexCoordMatrix, false, + layerRenderer.getVertexCoordMatrix()); gl.uniformMatrix4fv( this.locations_.uColorMatrix, false, layerRenderer.getColorMatrix()); gl.uniform1f(this.locations_.uOpacity, layer.getOpacity()); diff --git a/src/ol/renderer/webgl/webgltilelayerrenderer.js b/src/ol/renderer/webgl/webgltilelayerrenderer.js index 8f1dab302a..360f8f8d82 100644 --- a/src/ol/renderer/webgl/webgltilelayerrenderer.js +++ b/src/ol/renderer/webgl/webgltilelayerrenderer.js @@ -141,6 +141,12 @@ ol.renderer.webgl.TileLayer = function(mapRenderer, tileLayer) { */ this.texCoordMatrix_ = goog.vec.Mat4.createNumber(); + /** + * @private + * @type {!goog.vec.Mat4.Number} + */ + this.vertexCoordMatrix_ = goog.vec.Mat4.createNumberIdentity(); + /** * @private * @type {ol.TileRange} @@ -237,6 +243,14 @@ ol.renderer.webgl.TileLayer.prototype.getTexture = function() { }; +/** + * @inheritDoc + */ +ol.renderer.webgl.TileLayer.prototype.getVertexCoordMatrix = function() { + return this.vertexCoordMatrix_; +}; + + /** * @return {ol.layer.TileLayer} Tile layer. */ From 3d0682a4d49f4ab9f1be8f7cd95bedb77ef82c68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Thu, 14 Feb 2013 23:26:03 +0100 Subject: [PATCH 14/20] Use an ImageLayer in wms-custom-proj example --- examples/wms-custom-proj.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/examples/wms-custom-proj.js b/examples/wms-custom-proj.js index d389451935..fea7b563dd 100644 --- a/examples/wms-custom-proj.js +++ b/examples/wms-custom-proj.js @@ -10,7 +10,9 @@ goog.require('ol.Projection'); goog.require('ol.ProjectionUnits'); goog.require('ol.RendererHints'); goog.require('ol.View2D'); +goog.require('ol.layer.ImageLayer'); goog.require('ol.layer.TileLayer'); +goog.require('ol.source.SingleImageWMS'); goog.require('ol.source.TiledWMS'); @@ -24,6 +26,17 @@ var epsg21781 = new ol.Projection('EPSG:21781', ol.ProjectionUnits.METERS, new ol.Extent(485869.5728, 76443.1884, 837076.5648, 299941.7864)); ol.Projection.addProjection(epsg21781); +// We give the single image source a set of resolutions. This prevents the +// source from requesting images of arbitrary resolutions. +var projectionExtent = epsg21781.getExtent(); +var maxResolution = Math.max( + projectionExtent.maxX - projectionExtent.minX, + projectionExtent.maxY - projectionExtent.minY) / 256; +var resolutions = new Array(10); +for (var i = 0; i < 10; ++i) { + resolutions[i] = maxResolution / Math.pow(2.0, i); +} + var extent = new ol.Extent(420000, 30000, 900000, 350000); var layers = new ol.Collection([ new ol.layer.TileLayer({ @@ -41,8 +54,8 @@ var layers = new ol.Collection([ extent: extent }) }), - new ol.layer.TileLayer({ - source: new ol.source.TiledWMS({ + new ol.layer.ImageLayer({ + source: new ol.source.SingleImageWMS({ url: 'http://wms.geo.admin.ch/', attributions: [new ol.Attribution( '© ' + @@ -50,7 +63,7 @@ var layers = new ol.Collection([ 'National parks / geo.admin.ch')], params: {'LAYERS': 'ch.bafu.schutzgebiete-paerke_nationaler_bedeutung'}, projection: epsg21781, - extent: extent + resolutions: resolutions }) }) ]); From e1505abe0db6c8253d318e9fcc675859fcabb24a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Fri, 15 Feb 2013 17:43:27 +0100 Subject: [PATCH 15/20] Rename ol.Rectangle scale to scaleFromCenter --- src/ol/rectangle.js | 2 +- src/ol/source/singleimagewmssource.js | 2 +- test/spec/ol/rectangle.test.js | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ol/rectangle.js b/src/ol/rectangle.js index c7d9cab121..9d7a27af5e 100644 --- a/src/ol/rectangle.js +++ b/src/ol/rectangle.js @@ -129,7 +129,7 @@ ol.Rectangle.prototype.toString = function() { /** * @param {number} value Value. */ -ol.Rectangle.prototype.scale = function(value) { +ol.Rectangle.prototype.scaleFromCenter = function(value) { var deltaX = (this.getWidth() / 2.0) * (value - 1); var deltaY = (this.getHeight() / 2.0) * (value - 1); this.minX -= deltaX; diff --git a/src/ol/source/singleimagewmssource.js b/src/ol/source/singleimagewmssource.js index ec8ce0c329..3529678443 100644 --- a/src/ol/source/singleimagewmssource.js +++ b/src/ol/source/singleimagewmssource.js @@ -89,7 +89,7 @@ ol.source.SingleImageWMS.prototype.getImage = extent = new ol.Extent(extent.minX, extent.minY, extent.maxX, extent.maxY); - extent.scale(this.ratio_); + extent.scaleFromCenter(this.ratio_); var width = extent.getWidth() / resolution; var height = extent.getHeight() / resolution; var size = new ol.Size(width, height); diff --git a/test/spec/ol/rectangle.test.js b/test/spec/ol/rectangle.test.js index 5f882621ca..fe7ee9a1c3 100644 --- a/test/spec/ol/rectangle.test.js +++ b/test/spec/ol/rectangle.test.js @@ -98,10 +98,10 @@ describe('ol.Rectangle', function() { }); }); - describe('scale', function() { - it('scales the extent', function() { + describe('scaleFromCenter', function() { + it('scales the extent from its center', function() { var rectangle = new ol.Rectangle(1, 1, 3, 3); - rectangle.scale(2); + rectangle.scaleFromCenter(2); expect(rectangle.minX).toEqual(0); expect(rectangle.minY).toEqual(0); expect(rectangle.maxX).toEqual(4); From 1e4229497d17f04585a8b4792c998bfc6daf1e80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Fri, 15 Feb 2013 17:52:06 +0100 Subject: [PATCH 16/20] Do not sort resolutions array We assert that the resolutions array is sorted instead of sorting it ourselves. This is to consistent with ol.TileGrid. --- src/ol/source/imagesource.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ol/source/imagesource.js b/src/ol/source/imagesource.js index cd99fc40b1..caad04b5e9 100644 --- a/src/ol/source/imagesource.js +++ b/src/ol/source/imagesource.js @@ -61,9 +61,11 @@ ol.source.ImageSource = function(options) { */ this.resolutions_ = goog.isDef(options.resolutions) ? options.resolutions : null; - if (!goog.isNull(this.resolutions_)) { - goog.array.sort(this.resolutions_, function(a, b) {return b - a; }); - } + goog.asserts.assert(goog.isNull(this.resolutions_) || + goog.array.isSorted(this.resolutions_, + function(a, b) { + return b - a; + }, true)); }; goog.inherits(ol.source.ImageSource, ol.source.Source); From a3c65978d1a319121fab33c7dd95cb4ec8607d79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Fri, 15 Feb 2013 17:57:05 +0100 Subject: [PATCH 17/20] Make ol.Image emit change events --- src/ol/image.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/ol/image.js b/src/ol/image.js index ee81baa348..9f7cbe0c47 100644 --- a/src/ol/image.js +++ b/src/ol/image.js @@ -3,6 +3,8 @@ goog.provide('ol.ImageState'); goog.require('goog.array'); goog.require('goog.events'); +goog.require('goog.events.EventTarget'); +goog.require('goog.events.EventType'); goog.require('ol.Extent'); @@ -20,6 +22,7 @@ ol.ImageState = { /** * @constructor + * @extends {goog.events.EventTarget} * @param {ol.Extent} extent Extent. * @param {number} resolution Resolution. * @param {string} src Image source URI. @@ -72,6 +75,15 @@ ol.Image = function(extent, resolution, src, crossOrigin) { */ this.state = ol.ImageState.IDLE; }; +goog.inherits(ol.Image, goog.events.EventTarget); + + +/** + * @protected + */ +ol.Image.prototype.dispatchChangeEvent = function() { + this.dispatchEvent(goog.events.EventType.CHANGE); +}; /** @@ -129,6 +141,7 @@ ol.Image.prototype.getState = function() { ol.Image.prototype.handleImageError_ = function() { this.state = ol.ImageState.ERROR; this.unlistenImage_(); + this.dispatchChangeEvent(); }; @@ -140,6 +153,7 @@ ol.Image.prototype.handleImageError_ = function() { ol.Image.prototype.handleImageLoad_ = function() { this.state = ol.ImageState.LOADED; this.unlistenImage_(); + this.dispatchChangeEvent(); }; From 85eec4e9b85512b8f096ee8617491d932f48baa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Fri, 15 Feb 2013 18:02:15 +0100 Subject: [PATCH 18/20] Avoid busy-waiting while images load This is to be in conformance with the work done with #184. --- .../renderer/canvas/canvasimagelayerrenderer.js | 13 +++---------- src/ol/renderer/dom/domimagelayerrenderer.js | 13 +++---------- src/ol/renderer/layerrenderer.js | 15 +++++++++++++++ src/ol/renderer/webgl/webglimagelayerrenderer.js | 13 +++---------- 4 files changed, 24 insertions(+), 30 deletions(-) diff --git a/src/ol/renderer/canvas/canvasimagelayerrenderer.js b/src/ol/renderer/canvas/canvasimagelayerrenderer.js index 1b7857b9e2..33558a9c51 100644 --- a/src/ol/renderer/canvas/canvasimagelayerrenderer.js +++ b/src/ol/renderer/canvas/canvasimagelayerrenderer.js @@ -82,20 +82,13 @@ ol.renderer.canvas.ImageLayer.prototype.renderFrame = image = imageSource.getImage( frameState.extent, viewResolution); var imageState = image.getState(); - var animate = false; - if (imageState == ol.ImageState.ERROR) { - // pass - } else if (imageState == ol.ImageState.IDLE) { - animate = true; + if (imageState == ol.ImageState.IDLE) { + goog.events.listenOnce(image, goog.events.EventType.CHANGE, + this.handleImageChange, false, this); image.load(); - } else if (imageState == ol.ImageState.LOADING) { - animate = true; } else if (imageState == ol.ImageState.LOADED) { this.image_ = image; } - if (animate) { - frameState.animate = true; - } } if (!goog.isNull(this.image_)) { diff --git a/src/ol/renderer/dom/domimagelayerrenderer.js b/src/ol/renderer/dom/domimagelayerrenderer.js index eda01022e1..1e32a94775 100644 --- a/src/ol/renderer/dom/domimagelayerrenderer.js +++ b/src/ol/renderer/dom/domimagelayerrenderer.js @@ -69,20 +69,13 @@ ol.renderer.dom.ImageLayer.prototype.renderFrame = if (!hints[ol.ViewHint.ANIMATING] && !hints[ol.ViewHint.PANNING]) { var image_ = imageSource.getImage(frameState.extent, viewResolution); var imageState = image_.getState(); - var animate = false; - if (imageState == ol.ImageState.ERROR) { - // pass - } else if (imageState == ol.ImageState.IDLE) { - animate = true; + if (imageState == ol.ImageState.IDLE) { + goog.events.listenOnce(image_, goog.events.EventType.CHANGE, + this.handleImageChange, false, this); image_.load(); - } else if (imageState == ol.ImageState.LOADING) { - animate = true; } else if (imageState == ol.ImageState.LOADED) { image = image_; } - if (animate) { - frameState.animate = true; - } } if (!goog.isNull(image)) { diff --git a/src/ol/renderer/layerrenderer.js b/src/ol/renderer/layerrenderer.js index fab64889fc..bfe98600c4 100644 --- a/src/ol/renderer/layerrenderer.js +++ b/src/ol/renderer/layerrenderer.js @@ -3,6 +3,8 @@ goog.provide('ol.renderer.Layer'); goog.require('goog.events'); goog.require('goog.events.EventType'); goog.require('ol.FrameState'); +goog.require('ol.Image'); +goog.require('ol.ImageState'); goog.require('ol.Object'); goog.require('ol.Tile'); goog.require('ol.TileCoord'); @@ -119,6 +121,19 @@ ol.renderer.Layer.prototype.handleLayerContrastChange = goog.nullFunction; ol.renderer.Layer.prototype.handleLayerHueChange = goog.nullFunction; +/** + * Handle changes in image state. + * @param {goog.events.Event} event Image change event. + * @protected + */ +ol.renderer.Layer.prototype.handleImageChange = function(event) { + var image = /** @type {ol.Image} */ (event.target); + if (image.getState() === ol.ImageState.LOADED) { + this.getMap().requestRenderFrame(); + } +}; + + /** * @protected */ diff --git a/src/ol/renderer/webgl/webglimagelayerrenderer.js b/src/ol/renderer/webgl/webglimagelayerrenderer.js index c259b02b99..f7c7e3dc9c 100644 --- a/src/ol/renderer/webgl/webglimagelayerrenderer.js +++ b/src/ol/renderer/webgl/webglimagelayerrenderer.js @@ -160,14 +160,10 @@ ol.renderer.webgl.ImageLayer.prototype.renderFrame = if (!hints[ol.ViewHint.ANIMATING] && !hints[ol.ViewHint.PANNING]) { var image_ = imageSource.getImage(frameState.extent, viewResolution); var imageState = image_.getState(); - var animate = false; - if (imageState == ol.ImageState.ERROR) { - // pass - } else if (imageState == ol.ImageState.IDLE) { - animate = true; + if (imageState == ol.ImageState.IDLE) { + goog.events.listenOnce(image_, goog.events.EventType.CHANGE, + this.handleImageChange, false, this); image_.load(); - } else if (imageState == ol.ImageState.LOADING) { - animate = true; } else if (imageState == ol.ImageState.LOADED) { image = image_; texture = this.createTexture_(image_); @@ -180,9 +176,6 @@ ol.renderer.webgl.ImageLayer.prototype.renderFrame = }, gl, this.texture_)); } } - if (animate) { - frameState.animate = true; - } } if (!goog.isNull(image)) { From be6c1a1a278ce56624d1d444b89a267156cae321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Fri, 15 Feb 2013 23:17:30 +0100 Subject: [PATCH 19/20] Test the image layer renderer's transform matrix --- .../renderer/webgl/webglimagelayerrenderer.js | 53 +++++++---- .../spec/ol/renderer/webgl/imagelayer.test.js | 87 +++++++++++++++++++ 2 files changed, 123 insertions(+), 17 deletions(-) create mode 100644 test/spec/ol/renderer/webgl/imagelayer.test.js diff --git a/src/ol/renderer/webgl/webglimagelayerrenderer.js b/src/ol/renderer/webgl/webglimagelayerrenderer.js index f7c7e3dc9c..7d11ff53e5 100644 --- a/src/ol/renderer/webgl/webglimagelayerrenderer.js +++ b/src/ol/renderer/webgl/webglimagelayerrenderer.js @@ -1,6 +1,8 @@ goog.provide('ol.renderer.webgl.ImageLayer'); goog.require('goog.vec.Mat4'); +goog.require('ol.Coordinate'); +goog.require('ol.Extent'); goog.require('ol.Image'); goog.require('ol.ImageState'); goog.require('ol.ViewHint'); @@ -183,23 +185,8 @@ ol.renderer.webgl.ImageLayer.prototype.renderFrame = var canvas = this.getMapRenderer().getCanvas(); - var canvasExtentWidth = canvas.width * viewResolution; - var canvasExtentHeight = canvas.height * viewResolution; - - var imageExtent = image.getExtent(); - - var vertexCoordMatrix = this.vertexCoordMatrix_; - goog.vec.Mat4.makeIdentity(vertexCoordMatrix); - goog.vec.Mat4.scale(vertexCoordMatrix, - 2 / canvasExtentWidth, 2 / canvasExtentHeight, 1); - goog.vec.Mat4.rotateZ(vertexCoordMatrix, -viewRotation); - goog.vec.Mat4.translate(vertexCoordMatrix, - imageExtent.minX - viewCenter.x, - imageExtent.minY - viewCenter.y, - 0); - goog.vec.Mat4.scale(vertexCoordMatrix, - imageExtent.getWidth() / 2, imageExtent.getHeight() / 2, 1); - goog.vec.Mat4.translate(vertexCoordMatrix, 1, 1, 0); + this.updateVertexCoordMatrix_(canvas.width, canvas.height, + viewCenter, viewResolution, viewRotation, image.getExtent()); // Translate and scale to flip the Y coord. var texCoordMatrix = this.texCoordMatrix_; @@ -211,3 +198,35 @@ ol.renderer.webgl.ImageLayer.prototype.renderFrame = this.texture_ = texture; } }; + + +/** + * @private + * @param {number} canvasWidth Canvas width. + * @param {number} canvasHeight Canvas height. + * @param {ol.Coordinate} viewCenter View center. + * @param {number} viewResolution View resolution. + * @param {number} viewRotation View rotation. + * @param {ol.Extent} imageExtent Image extent. + */ +ol.renderer.webgl.ImageLayer.prototype.updateVertexCoordMatrix_ = + function(canvasWidth, canvasHeight, viewCenter, + viewResolution, viewRotation, imageExtent) { + + var canvasExtentWidth = canvasWidth * viewResolution; + var canvasExtentHeight = canvasHeight * viewResolution; + + var vertexCoordMatrix = this.vertexCoordMatrix_; + goog.vec.Mat4.makeIdentity(vertexCoordMatrix); + goog.vec.Mat4.scale(vertexCoordMatrix, + 2 / canvasExtentWidth, 2 / canvasExtentHeight, 1); + goog.vec.Mat4.rotateZ(vertexCoordMatrix, -viewRotation); + goog.vec.Mat4.translate(vertexCoordMatrix, + imageExtent.minX - viewCenter.x, + imageExtent.minY - viewCenter.y, + 0); + goog.vec.Mat4.scale(vertexCoordMatrix, + imageExtent.getWidth() / 2, imageExtent.getHeight() / 2, 1); + goog.vec.Mat4.translate(vertexCoordMatrix, 1, 1, 0); + +}; diff --git a/test/spec/ol/renderer/webgl/imagelayer.test.js b/test/spec/ol/renderer/webgl/imagelayer.test.js new file mode 100644 index 0000000000..62f3038827 --- /dev/null +++ b/test/spec/ol/renderer/webgl/imagelayer.test.js @@ -0,0 +1,87 @@ +goog.provide('ol.test.renderer.webgl.ImageLayer'); + +describe('ol.renderer.webgl.ImageLayer', function() { + describe('updateVertexCoordMatrix_', function() { + var map; + var renderer; + var canvasWidth; + var canvasHeight; + var viewExtent; + var viewResolution; + var viewRotation; + var imageExtent; + + beforeEach(function() { + map = new ol.Map({ + target: 'map' + }); + var layer = new ol.layer.ImageLayer({ + source: new ol.source.ImageSource({ + extent: new ol.Extent(0, 0, 1, 1) + }) + }); + renderer = new ol.renderer.webgl.ImageLayer(map.getRenderer(), layer); + + // input params + canvasWidth = 512; + canvasHeight = 256; + viewResolution = 10; + viewRotation = 0; + viewCenter = new ol.Coordinate(7680, 3840); + // view extent is 512O, 2560, 10240, 5120 + + // image size is 1024, 768 + // image resolution is 10 + imageExtent = new ol.Extent(0, 0, 10240, 7680); + }); + + afterEach(function() { + map.dispose(); + }); + + it('produces a correct matrix', function() { + + renderer.updateVertexCoordMatrix_(canvasWidth, canvasHeight, + viewCenter, viewResolution, viewRotation, imageExtent); + var matrix = renderer.getVertexCoordMatrix(); + + var input; + var output = goog.vec.Vec4.createNumber(); + + input = goog.vec.Vec4.createFromValues(-1, -1, 0, 1); + goog.vec.Mat4.multVec4(matrix, input, output); + expect(output[0]).toEqual(-3); + expect(output[1]).toEqual(-3); + + input = goog.vec.Vec4.createFromValues(1, -1, 0, 1); + goog.vec.Mat4.multVec4(matrix, input, output); + expect(output[0]).toEqual(1); + expect(output[1]).toEqual(-3); + + input = goog.vec.Vec4.createFromValues(-1, 1, 0, 1); + goog.vec.Mat4.multVec4(matrix, input, output); + expect(output[0]).toEqual(-3); + expect(output[1]).toEqual(3); + + input = goog.vec.Vec4.createFromValues(1, 1, 0, 1); + goog.vec.Mat4.multVec4(matrix, input, output); + expect(output[0]).toEqual(1); + expect(output[1]).toEqual(3); + + input = goog.vec.Vec4.createFromValues(0, 0, 0, 1); + goog.vec.Mat4.multVec4(matrix, input, output); + expect(output[0]).toEqual(-1); + expect(output[1]).toEqual(0); + }); + }); +}); + +goog.require('goog.vec.Mat4'); +goog.require('goog.vec.Vec4'); +goog.require('ol.Extent'); +goog.require('ol.Image'); +goog.require('ol.Map'); +goog.require('ol.layer.ImageLayer'); +goog.require('ol.source.ImageSource'); +goog.require('ol.renderer.Map'); +goog.require('ol.renderer.webgl.ImageLayer'); From d713c9b8ed1356302c6ef9a88710fd7815dcc481 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Sun, 17 Feb 2013 23:07:58 +0100 Subject: [PATCH 20/20] Deal with null images --- .../canvas/canvasimagelayerrenderer.js | 19 +++++------ src/ol/renderer/dom/domimagelayerrenderer.js | 16 ++++++---- .../renderer/webgl/webglimagelayerrenderer.js | 32 ++++++++++--------- 3 files changed, 36 insertions(+), 31 deletions(-) diff --git a/src/ol/renderer/canvas/canvasimagelayerrenderer.js b/src/ol/renderer/canvas/canvasimagelayerrenderer.js index 33558a9c51..273f22d49f 100644 --- a/src/ol/renderer/canvas/canvasimagelayerrenderer.js +++ b/src/ol/renderer/canvas/canvasimagelayerrenderer.js @@ -79,15 +79,16 @@ ol.renderer.canvas.ImageLayer.prototype.renderFrame = var hints = frameState.viewHints; if (!hints[ol.ViewHint.ANIMATING] && !hints[ol.ViewHint.PANNING]) { - image = imageSource.getImage( - frameState.extent, viewResolution); - var imageState = image.getState(); - if (imageState == ol.ImageState.IDLE) { - goog.events.listenOnce(image, goog.events.EventType.CHANGE, - this.handleImageChange, false, this); - image.load(); - } else if (imageState == ol.ImageState.LOADED) { - this.image_ = image; + image = imageSource.getImage(frameState.extent, viewResolution); + if (!goog.isNull(image)) { + var imageState = image.getState(); + if (imageState == ol.ImageState.IDLE) { + goog.events.listenOnce(image, goog.events.EventType.CHANGE, + this.handleImageChange, false, this); + image.load(); + } else if (imageState == ol.ImageState.LOADED) { + this.image_ = image; + } } } diff --git a/src/ol/renderer/dom/domimagelayerrenderer.js b/src/ol/renderer/dom/domimagelayerrenderer.js index 1e32a94775..b3f5f9f224 100644 --- a/src/ol/renderer/dom/domimagelayerrenderer.js +++ b/src/ol/renderer/dom/domimagelayerrenderer.js @@ -68,13 +68,15 @@ ol.renderer.dom.ImageLayer.prototype.renderFrame = if (!hints[ol.ViewHint.ANIMATING] && !hints[ol.ViewHint.PANNING]) { var image_ = imageSource.getImage(frameState.extent, viewResolution); - var imageState = image_.getState(); - if (imageState == ol.ImageState.IDLE) { - goog.events.listenOnce(image_, goog.events.EventType.CHANGE, - this.handleImageChange, false, this); - image_.load(); - } else if (imageState == ol.ImageState.LOADED) { - image = image_; + if (!goog.isNull(image_)) { + var imageState = image_.getState(); + if (imageState == ol.ImageState.IDLE) { + goog.events.listenOnce(image_, goog.events.EventType.CHANGE, + this.handleImageChange, false, this); + image_.load(); + } else if (imageState == ol.ImageState.LOADED) { + image = image_; + } } } diff --git a/src/ol/renderer/webgl/webglimagelayerrenderer.js b/src/ol/renderer/webgl/webglimagelayerrenderer.js index 7d11ff53e5..6449a7c178 100644 --- a/src/ol/renderer/webgl/webglimagelayerrenderer.js +++ b/src/ol/renderer/webgl/webglimagelayerrenderer.js @@ -161,21 +161,23 @@ ol.renderer.webgl.ImageLayer.prototype.renderFrame = if (!hints[ol.ViewHint.ANIMATING] && !hints[ol.ViewHint.PANNING]) { var image_ = imageSource.getImage(frameState.extent, viewResolution); - var imageState = image_.getState(); - if (imageState == ol.ImageState.IDLE) { - goog.events.listenOnce(image_, goog.events.EventType.CHANGE, - this.handleImageChange, false, this); - image_.load(); - } else if (imageState == ol.ImageState.LOADED) { - image = image_; - texture = this.createTexture_(image_); - if (!goog.isNull(this.texture_)) { - frameState.postRenderFunctions.push( - goog.partial(function(gl, texture) { - if (!gl.isContextLost()) { - gl.deleteTexture(texture); - } - }, gl, this.texture_)); + if (!goog.isNull(image_)) { + var imageState = image_.getState(); + if (imageState == ol.ImageState.IDLE) { + goog.events.listenOnce(image_, goog.events.EventType.CHANGE, + this.handleImageChange, false, this); + image_.load(); + } else if (imageState == ol.ImageState.LOADED) { + image = image_; + texture = this.createTexture_(image_); + if (!goog.isNull(this.texture_)) { + frameState.postRenderFunctions.push( + goog.partial(function(gl, texture) { + if (!gl.isContextLost()) { + gl.deleteTexture(texture); + } + }, gl, this.texture_)); + } } } }