From 979342091ac6dae4cbb8e5e8c187a6286ed10a28 Mon Sep 17 00:00:00 2001 From: Bart van den Eijnden Date: Fri, 30 Oct 2015 15:01:01 +0100 Subject: [PATCH] Support different resolutions in x and y direction for ol.source.ImageStatic --- src/ol/image.js | 7 +++- src/ol/imagebase.js | 8 ++-- src/ol/imagecanvas.js | 3 +- .../canvas/canvasimagelayerrenderer.js | 12 ++++-- src/ol/reproj/image.js | 5 ++- src/ol/reproj/reproj.js | 16 ++++---- src/ol/reproj/tile.js | 2 +- src/ol/source/imagemapguidesource.js | 2 +- src/ol/source/imagestaticsource.js | 12 +++--- src/ol/source/imagewmssource.js | 4 +- test_rendering/spec/ol/data/dem.jpg | Bin 0 -> 10901 bytes .../ol/layer/expected/image-canvas-resxy.png | Bin 0 -> 3200 bytes test_rendering/spec/ol/layer/image.test.js | 35 ++++++++++++++++-- 13 files changed, 75 insertions(+), 31 deletions(-) create mode 100644 test_rendering/spec/ol/data/dem.jpg create mode 100644 test_rendering/spec/ol/layer/expected/image-canvas-resxy.png diff --git a/src/ol/image.js b/src/ol/image.js index 39bfcfe5ac..e0b41e2e20 100644 --- a/src/ol/image.js +++ b/src/ol/image.js @@ -14,7 +14,7 @@ goog.require('ol.extent'); * @constructor * @extends {ol.ImageBase} * @param {ol.Extent} extent Extent. - * @param {number|undefined} resolution Resolution. + * @param {Array.|undefined} resolution Resolution. * @param {number} pixelRatio Pixel ratio. * @param {Array.} attributions Attributions. * @param {string} src Image source URI. @@ -114,7 +114,10 @@ ol.Image.prototype.handleImageError_ = function() { */ ol.Image.prototype.handleImageLoad_ = function() { if (this.resolution === undefined) { - this.resolution = ol.extent.getHeight(this.extent) / this.image_.height; + this.resolution = [ + ol.extent.getWidth(this.extent) / this.image_.width, + ol.extent.getHeight(this.extent) / this.image_.height + ]; } this.state = ol.ImageState.LOADED; this.unlistenImage_(); diff --git a/src/ol/imagebase.js b/src/ol/imagebase.js index e188221977..8f32c0ac9f 100644 --- a/src/ol/imagebase.js +++ b/src/ol/imagebase.js @@ -24,7 +24,9 @@ ol.ImageState = { * @constructor * @extends {goog.events.EventTarget} * @param {ol.Extent} extent Extent. - * @param {number|undefined} resolution Resolution. + * @param {Array.|undefined} resolution Resolution, first value + * is the resolution in the x direction, second value is the resolution + * in the y direction. * @param {number} pixelRatio Pixel ratio. * @param {ol.ImageState} state State. * @param {Array.} attributions Attributions. @@ -53,7 +55,7 @@ ol.ImageBase = function(extent, resolution, pixelRatio, state, attributions) { /** * @protected - * @type {number|undefined} + * @type {Array.|undefined} */ this.resolution = resolution; @@ -107,7 +109,7 @@ ol.ImageBase.prototype.getPixelRatio = function() { /** - * @return {number} Resolution. + * @return {Array.} Resolution. */ ol.ImageBase.prototype.getResolution = function() { goog.asserts.assert(this.resolution !== undefined, 'resolution not yet set'); diff --git a/src/ol/imagecanvas.js b/src/ol/imagecanvas.js index 743af73486..1ee96866c1 100644 --- a/src/ol/imagecanvas.js +++ b/src/ol/imagecanvas.js @@ -30,7 +30,8 @@ ol.ImageCanvas = function(extent, resolution, pixelRatio, attributions, var state = opt_loader !== undefined ? ol.ImageState.IDLE : ol.ImageState.LOADED; - goog.base(this, extent, resolution, pixelRatio, state, attributions); + goog.base(this, extent, [resolution, resolution], pixelRatio, state, + attributions); /** * @private diff --git a/src/ol/renderer/canvas/canvasimagelayerrenderer.js b/src/ol/renderer/canvas/canvasimagelayerrenderer.js index 9346fbc8f2..1afc4ef5b6 100644 --- a/src/ol/renderer/canvas/canvasimagelayerrenderer.js +++ b/src/ol/renderer/canvas/canvasimagelayerrenderer.js @@ -193,15 +193,19 @@ ol.renderer.canvas.ImageLayer.prototype.prepareFrame = var imageExtent = image.getExtent(); var imageResolution = image.getResolution(); var imagePixelRatio = image.getPixelRatio(); - var scale = pixelRatio * imageResolution / + var xImageResolution = imageResolution[0]; + var yImageResolution = imageResolution[1]; + var xScale = pixelRatio * xImageResolution / + (viewResolution * imagePixelRatio); + var yScale = pixelRatio * yImageResolution / (viewResolution * imagePixelRatio); ol.vec.Mat4.makeTransform2D(this.imageTransform_, pixelRatio * frameState.size[0] / 2, pixelRatio * frameState.size[1] / 2, - scale, scale, + xScale, yScale, viewRotation, - imagePixelRatio * (imageExtent[0] - viewCenter[0]) / imageResolution, - imagePixelRatio * (viewCenter[1] - imageExtent[3]) / imageResolution); + imagePixelRatio * (imageExtent[0] - viewCenter[0]) / xImageResolution, + imagePixelRatio * (viewCenter[1] - imageExtent[3]) / yImageResolution); this.imageTransformInv_ = null; this.updateAttributions(frameState.attributions, image.getAttributions()); this.updateLogos(frameState, imageSource); diff --git a/src/ol/reproj/image.js b/src/ol/reproj/image.js index 0292d7168e..9a19424697 100644 --- a/src/ol/reproj/image.js +++ b/src/ol/reproj/image.js @@ -116,8 +116,9 @@ ol.reproj.Image = function(sourceProj, targetProj, attributions = this.sourceImage_.getAttributions(); } - goog.base(this, targetExtent, targetResolution, this.sourcePixelRatio_, - state, attributions); + goog.base(this, targetExtent, [targetResolution, targetResolution], + this.sourcePixelRatio_, state, attributions); + }; goog.inherits(ol.reproj.Image, ol.ImageBase); diff --git a/src/ol/reproj/reproj.js b/src/ol/reproj/reproj.js index fbca15fa90..6ea19f43f4 100644 --- a/src/ol/reproj/reproj.js +++ b/src/ol/reproj/reproj.js @@ -93,7 +93,7 @@ ol.reproj.enlargeClipPoint_ = function(centroidX, centroidY, x, y) { * @param {number} width Width of the canvas. * @param {number} height Height of the canvas. * @param {number} pixelRatio Pixel ratio. - * @param {number} sourceResolution Source resolution. + * @param {Array.} sourceResolution Source resolution. * @param {ol.Extent} sourceExtent Extent of the data source. * @param {number} targetResolution Target resolution. * @param {ol.Extent} targetExtent Target extent. @@ -124,12 +124,14 @@ ol.reproj.render = function(width, height, pixelRatio, var canvasWidthInUnits = ol.extent.getWidth(sourceDataExtent); var canvasHeightInUnits = ol.extent.getHeight(sourceDataExtent); + var sourceResolutionX = sourceResolution[0]; + var sourceResolutionY = sourceResolution[1]; var stitchContext = ol.dom.createCanvasContext2D( - Math.round(pixelRatio * canvasWidthInUnits / sourceResolution), - Math.round(pixelRatio * canvasHeightInUnits / sourceResolution)); + Math.round(pixelRatio * canvasWidthInUnits / sourceResolutionX), + Math.round(pixelRatio * canvasHeightInUnits / sourceResolutionY)); - stitchContext.scale(pixelRatio / sourceResolution, - pixelRatio / sourceResolution); + stitchContext.scale(pixelRatio / sourceResolutionX, + pixelRatio / sourceResolutionY); stitchContext.translate(-sourceDataExtent[0], sourceDataExtent[3]); sources.forEach(function(src, i, arr) { @@ -222,8 +224,8 @@ ol.reproj.render = function(width, height, pixelRatio, context.translate(sourceDataExtent[0] - sourceNumericalShiftX, sourceDataExtent[3] - sourceNumericalShiftY); - context.scale(sourceResolution / pixelRatio, - -sourceResolution / pixelRatio); + context.scale(sourceResolutionX / pixelRatio, + -sourceResolutionY / pixelRatio); context.drawImage(stitchContext.canvas, 0, 0); context.restore(); diff --git a/src/ol/reproj/tile.js b/src/ol/reproj/tile.js index a1c819d620..3324a41cc3 100644 --- a/src/ol/reproj/tile.js +++ b/src/ol/reproj/tile.js @@ -258,7 +258,7 @@ ol.reproj.Tile.prototype.reproject_ = function() { var targetExtent = this.targetTileGrid_.getTileCoordExtent(tileCoord); this.canvas_ = ol.reproj.render(width, height, this.pixelRatio_, - sourceResolution, this.sourceTileGrid_.getExtent(), + [sourceResolution, sourceResolution], this.sourceTileGrid_.getExtent(), targetResolution, targetExtent, this.triangulation_, sources, this.renderEdges_); diff --git a/src/ol/source/imagemapguidesource.js b/src/ol/source/imagemapguidesource.js index 2ff6a92701..efa1de5e73 100644 --- a/src/ol/source/imagemapguidesource.js +++ b/src/ol/source/imagemapguidesource.js @@ -150,7 +150,7 @@ ol.source.ImageMapGuide.prototype.getImageInternal = var imageUrl = this.imageUrlFunction_(extent, size, projection); if (imageUrl !== undefined) { - image = new ol.Image(extent, resolution, pixelRatio, + image = new ol.Image(extent, [resolution, resolution], pixelRatio, this.getAttributions(), imageUrl, this.crossOrigin_, this.imageLoadFunction_); goog.events.listen(image, goog.events.EventType.CHANGE, diff --git a/src/ol/source/imagestaticsource.js b/src/ol/source/imagestaticsource.js index f6e57ce82d..2de9c20945 100644 --- a/src/ol/source/imagestaticsource.js +++ b/src/ol/source/imagestaticsource.js @@ -26,10 +26,12 @@ ol.source.ImageStatic = function(options) { var imageExtent = options.imageExtent; - var resolution, resolutions; + var xResolution, yResolution, resolutions, imgResolution; if (options.imageSize !== undefined) { - resolution = ol.extent.getHeight(imageExtent) / options.imageSize[1]; - resolutions = [resolution]; + xResolution = ol.extent.getWidth(imageExtent) / options.imageSize[0]; + yResolution = ol.extent.getHeight(imageExtent) / options.imageSize[1]; + imgResolution = [xResolution, yResolution]; + resolutions = [yResolution]; } var crossOrigin = options.crossOrigin !== undefined ? @@ -50,8 +52,8 @@ ol.source.ImageStatic = function(options) { * @private * @type {ol.Image} */ - this.image_ = new ol.Image(imageExtent, resolution, 1, attributions, - options.url, crossOrigin, imageLoadFunction); + this.image_ = new ol.Image(imageExtent, imgResolution, 1, + attributions, options.url, crossOrigin, imageLoadFunction); goog.events.listen(this.image_, goog.events.EventType.CHANGE, this.handleImageChange, false, this); diff --git a/src/ol/source/imagewmssource.js b/src/ol/source/imagewmssource.js index 1d63972269..3efc41c936 100644 --- a/src/ol/source/imagewmssource.js +++ b/src/ol/source/imagewmssource.js @@ -217,7 +217,7 @@ ol.source.ImageWMS.prototype.getImageInternal = var image = this.image_; if (image && this.renderedRevision_ == this.getRevision() && - image.getResolution() == resolution && + image.getResolution()[0] == resolution && image.getPixelRatio() == pixelRatio && ol.extent.containsExtent(image.getExtent(), extent)) { return image; @@ -247,7 +247,7 @@ ol.source.ImageWMS.prototype.getImageInternal = var url = this.getRequestUrl_(extent, this.imageSize_, pixelRatio, projection, params); - this.image_ = new ol.Image(extent, resolution, pixelRatio, + this.image_ = new ol.Image(extent, [resolution, resolution], pixelRatio, this.getAttributions(), url, this.crossOrigin_, this.imageLoadFunction_); this.renderedRevision_ = this.getRevision(); diff --git a/test_rendering/spec/ol/data/dem.jpg b/test_rendering/spec/ol/data/dem.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ddd2b6b990772183d28a87fa2b1212e274f4ce41 GIT binary patch literal 10901 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�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 literal 0 HcmV?d00001 diff --git a/test_rendering/spec/ol/layer/expected/image-canvas-resxy.png b/test_rendering/spec/ol/layer/expected/image-canvas-resxy.png new file mode 100644 index 0000000000000000000000000000000000000000..a11beb752c4e65ff3bf76f970e243ded2fb91bc1 GIT binary patch literal 3200 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 m-q)Lt+nN4P=lR6A)_(v@6n1_rw@Q=%0000