From 0d6c54847bb6d3556455dd57c6016a9d5ffac74b Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 18 Feb 2013 15:55:27 -0700 Subject: [PATCH 1/4] Removing logic that assumes occasionally stretched tiles When the dom renderer included logic to stretch tiles so that gaps were properly filled for fractional zoom levels, we needed to take this into account when getting a tile coordinate for a given coordinate and resolution. This was never the proper logic for a renderer that wasn't stretching occassional tiles (e.g. the WebGL renderer, the Canvas renderer, or the new DOM renderer). --- src/ol/tilegrid/tilegrid.js | 24 +------- test/spec/ol/tilegrid.test.js | 100 ---------------------------------- 2 files changed, 3 insertions(+), 121 deletions(-) diff --git a/src/ol/tilegrid/tilegrid.js b/src/ol/tilegrid/tilegrid.js index 58dfaacfda..ba9e6ff441 100644 --- a/src/ol/tilegrid/tilegrid.js +++ b/src/ol/tilegrid/tilegrid.js @@ -251,28 +251,10 @@ ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndResolution = function( tileSize = new ol.Size(tileSize.width / scale, tileSize.height / scale); - var x, y; - x = Math.floor(offsetFromOrigin.x / tileSize.width); - y = Math.floor(offsetFromOrigin.y / tileSize.height); + var x = Math.floor(offsetFromOrigin.x / tileSize.width); + var y = Math.floor(offsetFromOrigin.y / tileSize.height); - var tileCoord = new ol.TileCoord(z, x, y); - var tileCoordPixelBounds = this.getPixelBoundsForTileCoordAndResolution( - tileCoord, resolution); - - // adjust x to allow for stretched tiles - if (offsetFromOrigin.x < tileCoordPixelBounds.minX) { - tileCoord.x -= 1; - } else if (offsetFromOrigin.x >= tileCoordPixelBounds.maxX) { - tileCoord.x += 1; - } - // adjust y to allow for stretched tiles - if (offsetFromOrigin.y < tileCoordPixelBounds.minY) { - tileCoord.y -= 1; - } else if (offsetFromOrigin.y >= tileCoordPixelBounds.maxY) { - tileCoord.y += 1; - } - - return tileCoord; + return new ol.TileCoord(z, x, y); }; diff --git a/test/spec/ol/tilegrid.test.js b/test/spec/ol/tilegrid.test.js index bcaba344f6..63451a1668 100644 --- a/test/spec/ol/tilegrid.test.js +++ b/test/spec/ol/tilegrid.test.js @@ -258,106 +258,6 @@ describe('ol.tilegrid.TileGrid', function() { }); }); - describe('getTileCoordForCoordAndResolution fractional', function() { - it('returns the expected TileCoord', function() { - var tileSize = new ol.Size(256, 256); - var tileGrid = new ol.tilegrid.TileGrid({ - resolutions: [1 / 3], - extent: extent, - origin: origin, - tileSize: tileSize - }); - - var coordinate; - var tileCoord; - - // These tests render at a resolution of 1. Because the layer's - // closest resolution is 1/3, the images are scaled by 1/3. - // In this scenario, every third tile will be one pixel wider when - // rendered (0,0 is normal; 1,0 is wider; 0,1 is taller; etc.) - - // gets the first tile at the origin - coordinate = new ol.Coordinate(0, 0); - tileCoord = tileGrid.getTileCoordForCoordAndResolution( - coordinate, 1); - expect(tileCoord.z).toEqual(0); - expect(tileCoord.x).toEqual(0); - expect(tileCoord.y).toEqual(0); - - // gets the 1,0 tile at 256/3,0 - coordinate = new ol.Coordinate(256 / 3, 0); - tileCoord = tileGrid.getTileCoordForCoordAndResolution( - coordinate, 1); - expect(tileCoord.z).toEqual(0); - expect(tileCoord.x).toEqual(1); - expect(tileCoord.y).toEqual(0); - - // still gets the 1,0 tile at 512/3,0 - wider tile - coordinate = new ol.Coordinate(512 / 3, 0); - tileCoord = tileGrid.getTileCoordForCoordAndResolution( - coordinate, 1); - expect(tileCoord.z).toEqual(0); - expect(tileCoord.x).toEqual(1); - expect(tileCoord.y).toEqual(0); - - // gets the 2,0 tile at 513/3,0 - coordinate = new ol.Coordinate(513 / 3, 0); - tileCoord = tileGrid.getTileCoordForCoordAndResolution( - coordinate, 1); - expect(tileCoord.z).toEqual(0); - expect(tileCoord.x).toEqual(2); - expect(tileCoord.y).toEqual(0); - - // gets the 3,0 tile at 768/3,0 - coordinate = new ol.Coordinate(768 / 3, 0); - tileCoord = tileGrid.getTileCoordForCoordAndResolution( - coordinate, 1); - expect(tileCoord.z).toEqual(0); - expect(tileCoord.x).toEqual(3); - expect(tileCoord.y).toEqual(0); - - // gets the 4,0 tile at 1024/3,0 - coordinate = new ol.Coordinate(1024 / 3, 0); - tileCoord = tileGrid.getTileCoordForCoordAndResolution( - coordinate, 1); - expect(tileCoord.z).toEqual(0); - expect(tileCoord.x).toEqual(4); - expect(tileCoord.y).toEqual(0); - - // still gets the 4,0 tile at 1280/3,0 - wider tile - coordinate = new ol.Coordinate(1280 / 3, 0); - tileCoord = tileGrid.getTileCoordForCoordAndResolution( - coordinate, 1); - expect(tileCoord.z).toEqual(0); - expect(tileCoord.x).toEqual(4); - expect(tileCoord.y).toEqual(0); - - // gets the 5,0 tile at 1281/3,0 - coordinate = new ol.Coordinate(1281 / 3, 0); - tileCoord = tileGrid.getTileCoordForCoordAndResolution( - coordinate, 1); - expect(tileCoord.z).toEqual(0); - expect(tileCoord.x).toEqual(5); - expect(tileCoord.y).toEqual(0); - - // gets the 0,1 tile at 0,-256/3 - coordinate = new ol.Coordinate(0, -256 / 3); - tileCoord = tileGrid.getTileCoordForCoordAndResolution( - coordinate, 1); - expect(tileCoord.z).toEqual(0); - expect(tileCoord.x).toEqual(0); - expect(tileCoord.y).toEqual(-2); - - // still gets the 0,1 tile at 0,-512/3 - taller tile - coordinate = new ol.Coordinate(0, -512 / 3); - tileCoord = tileGrid.getTileCoordForCoordAndResolution( - coordinate, 1); - expect(tileCoord.z).toEqual(0); - expect(tileCoord.x).toEqual(0); - expect(tileCoord.y).toEqual(-2); - }); - }); - describe('getTileCoordCenter', function() { it('returns the expected center', function() { var tileGrid = new ol.tilegrid.TileGrid({ From 8b1975126401dd9ea97c34c500be7be8907d1ca6 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 18 Feb 2013 17:07:31 -0700 Subject: [PATCH 2/4] Making it explicit that this is a range of tiles With tile range extending rectangle, a tile range that represented a single tile was not allowed. --- src/ol/tilerange.js | 75 +++++++++++++++++++++++++++++++--- test/spec/ol/tilerange.test.js | 67 ++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+), 6 deletions(-) diff --git a/src/ol/tilerange.js b/src/ol/tilerange.js index e1baa169f8..752716a4dc 100644 --- a/src/ol/tilerange.js +++ b/src/ol/tilerange.js @@ -1,23 +1,44 @@ goog.provide('ol.TileRange'); goog.require('goog.asserts'); -goog.require('ol.Rectangle'); +goog.require('ol.Size'); goog.require('ol.TileCoord'); /** + * A representation of a contiguous block of tiles. A tile range is specified + * by its min/max tile coordinates and is inclusive of coordinates. + * * @constructor - * @extends {ol.Rectangle} * @param {number} minX Minimum X. * @param {number} minY Minimum Y. * @param {number} maxX Maximum X. * @param {number} maxY Maximum Y. */ ol.TileRange = function(minX, minY, maxX, maxY) { - goog.base(this, minX, minY, maxX, maxY); + + /** + * @type {number} + */ + this.minX = minX; + + /** + * @type {number} + */ + this.minY = minY; + + /** + * @type {number} + */ + this.maxX = maxX; + + /** + * @type {number} + */ + this.maxY = maxY; + }; -goog.inherits(ol.TileRange, ol.Rectangle); /** @@ -62,7 +83,28 @@ ol.TileRange.prototype.containsTileRange = function(tileRange) { /** - * @inheritDoc + * @param {ol.TileRange} range Other range. + * @return {boolean} The two ranges are equivalent. + */ +ol.TileRange.prototype.equals = function(range) { + return this.minX == range.minX && this.minY == range.minY && + this.maxX == range.maxX && this.maxY == range.maxY; +}; + + +/** + * Extend this range so it includes the other. + * @param {ol.TileRange} other Other range. + */ +ol.TileRange.prototype.extend = function(other) { + this.minX = Math.min(this.minX, other.minX); + this.minY = Math.min(this.minY, other.minY); + this.maxX = Math.max(this.maxX, other.maxX); + this.maxY = Math.max(this.maxY, other.maxY); +}; + + +/** * @return {number} Height. */ ol.TileRange.prototype.getHeight = function() { @@ -71,9 +113,30 @@ ol.TileRange.prototype.getHeight = function() { /** - * @inheritDoc + * @return {ol.Size} Size. + */ +ol.TileRange.prototype.getSize = function() { + return new ol.Size(this.getWidth(), this.getHeight()); +}; + + +/** * @return {number} Width. */ ol.TileRange.prototype.getWidth = function() { return this.maxX - this.minX + 1; }; + + +/** + * Test for range intersection. + * @param {ol.TileRange} other Other range. + * @return {boolean} The two ranges intersect. + */ +ol.TileRange.prototype.intersects = function(other) { + return this.minX <= other.maxX && + this.maxX >= other.minX && + this.minY <= other.maxY && + this.maxY >= other.minY; +}; + diff --git a/test/spec/ol/tilerange.test.js b/test/spec/ol/tilerange.test.js index fd59068325..6906c57e78 100644 --- a/test/spec/ol/tilerange.test.js +++ b/test/spec/ol/tilerange.test.js @@ -2,6 +2,20 @@ goog.provide('ol.test.TileRange'); describe('ol.TileRange', function() { + describe('constructor', function() { + it('creates a range', function() { + var range = new ol.TileRange(1, 2, 3, 4); + expect(range).toBeA(ol.TileRange); + }); + + it('can represent a range of one tile', function() { + var range = new ol.TileRange(2, 3, 2, 3); + expect(range).toBeA(ol.TileRange); + expect(range.getHeight()).toBe(1); + expect(range.getWidth()).toBe(1); + }); + }); + describe('contains', function() { it('returns the expected value', function() { var tileRange = new ol.TileRange(1, 1, 3, 3); @@ -53,6 +67,36 @@ describe('ol.TileRange', function() { }); }); + describe('equals', function() { + it('determines equivalence of two ranges', function() { + var one = new ol.TileRange(0, 1, 2, 4); + var same = new ol.TileRange(0, 1, 2, 4); + var diff1 = new ol.TileRange(0, 1, 2, 5); + var diff2 = new ol.TileRange(0, 1, 3, 4); + var diff3 = new ol.TileRange(0, 2, 2, 4); + var diff4 = new ol.TileRange(1, 1, 2, 4); + expect(one.equals(same)).toBe(true); + expect(one.equals(diff1)).toBe(false); + expect(one.equals(diff2)).toBe(false); + expect(one.equals(diff3)).toBe(false); + expect(one.equals(diff4)).toBe(false); + }); + }); + + describe('extent', function() { + it('modifies range so it includes another', function() { + var one = new ol.TileRange(0, 1, 2, 4); + var other = new ol.TileRange(-1, 10, -3, 12); + one.extend(other); + + expect(one.minX).toBe(-1); + expect(one.minY).toBe(1); + expect(one.maxX).toBe(2); + expect(one.maxY).toBe(12); + + }); + }); + describe('getSize', function() { it('returns the expected size', function() { var tileRange = new ol.TileRange(0, 1, 2, 4); @@ -62,6 +106,29 @@ describe('ol.TileRange', function() { }); }); + describe('intersects', function() { + it('determines if two ranges overlap', function() { + var one = new ol.TileRange(0, 1, 2, 4); + var overlapsRight = new ol.TileRange(2, 1, 4, 4); + var overlapsLeft = new ol.TileRange(-3, 1, 0, 4); + var overlapsTop = new ol.TileRange(0, 4, 2, 5); + var overlapsBottom = new ol.TileRange(0, -3, 2, 1); + expect(one.intersects(overlapsLeft)).toBe(true); + expect(one.intersects(overlapsRight)).toBe(true); + expect(one.intersects(overlapsTop)).toBe(true); + expect(one.intersects(overlapsBottom)).toBe(true); + + var right = new ol.TileRange(3, 1, 5, 4); + var left = new ol.TileRange(-3, 1, -1, 4); + var above = new ol.TileRange(0, 5, 2, 6); + var below = new ol.TileRange(0, -3, 2, 0); + expect(one.intersects(right)).toBe(false); + expect(one.intersects(left)).toBe(false); + expect(one.intersects(above)).toBe(false); + expect(one.intersects(below)).toBe(false); + }); + }); + }); goog.require('ol.TileRange'); From 797dba2cdbabd8fa0a78c32a7b3bfcdbff29eea8 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 18 Feb 2013 17:09:33 -0700 Subject: [PATCH 3/4] Correct range for extent and resolution Tile ranges are inclusive. When getting the tile range for an extent, the top-right corner of the extent should be considered with a different intersection policy than the bottom-left corner. --- src/ol/tilegrid/tilegrid.js | 46 ++++++++++----- test/spec/ol/tilegrid.test.js | 102 ++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 13 deletions(-) diff --git a/src/ol/tilegrid/tilegrid.js b/src/ol/tilegrid/tilegrid.js index ba9e6ff441..7786581932 100644 --- a/src/ol/tilegrid/tilegrid.js +++ b/src/ol/tilegrid/tilegrid.js @@ -183,10 +183,10 @@ ol.tilegrid.TileGrid.prototype.getTileRangeExtent = function(z, tileRange) { */ ol.tilegrid.TileGrid.prototype.getTileRangeForExtentAndResolution = function( extent, resolution) { - var min = this.getTileCoordForCoordAndResolution( + var min = this.getTileCoordForCoordAndResolution_( new ol.Coordinate(extent.minX, extent.minY), resolution); - var max = this.getTileCoordForCoordAndResolution( - new ol.Coordinate(extent.maxX, extent.maxY), resolution); + var max = this.getTileCoordForCoordAndResolution_( + new ol.Coordinate(extent.maxX, extent.maxY), resolution, true); return new ol.TileRange(min.x, min.y, max.x, max.y); }; @@ -233,26 +233,46 @@ ol.tilegrid.TileGrid.prototype.getTileCoordExtent = function(tileCoord) { /** + * Get the tile coordinate for the given map coordinate and resolution. This + * method considers that coordinates that intersect tile boundaries should be + * assigned the higher tile coordinate. + * * @param {ol.Coordinate} coordinate Coordinate. * @param {number} resolution Resolution. * @return {ol.TileCoord} Tile coordinate. */ ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndResolution = function( coordinate, resolution) { + return this.getTileCoordForCoordAndResolution_(coordinate, resolution); +}; + + +/** + * @param {ol.Coordinate} coordinate Coordinate. + * @param {number} resolution Resolution. + * @param {boolean=} opt_reverseIntersectionPolicy Instead of letting edge + * intersections go to the higher tile coordinate, let edge intersections + * go to the lower tile coordinate. + * @return {ol.TileCoord} Tile coordinate. + * @private + */ +ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndResolution_ = function( + coordinate, resolution, opt_reverseIntersectionPolicy) { var z = this.getZForResolution(resolution); var scale = resolution / this.getResolution(z); var origin = this.getOrigin(z); - - var offsetFromOrigin = new ol.Coordinate( - Math.floor((coordinate.x - origin.x) / resolution), - Math.floor((coordinate.y - origin.y) / resolution)); - var tileSize = this.getTileSize(); - tileSize = new ol.Size(tileSize.width / scale, - tileSize.height / scale); - var x = Math.floor(offsetFromOrigin.x / tileSize.width); - var y = Math.floor(offsetFromOrigin.y / tileSize.height); + var x = scale * (coordinate.x - origin.x) / (resolution * tileSize.width); + var y = scale * (coordinate.y - origin.y) / (resolution * tileSize.height); + + if (!opt_reverseIntersectionPolicy) { + x = Math.floor(x); + y = Math.floor(y); + } else { + x = Math.ceil(x) - 1; + y = Math.ceil(y) - 1; + } return new ol.TileCoord(z, x, y); }; @@ -266,7 +286,7 @@ ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndResolution = function( ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndZ = function(coordinate, z) { var resolution = this.getResolution(z); - return this.getTileCoordForCoordAndResolution(coordinate, resolution); + return this.getTileCoordForCoordAndResolution_(coordinate, resolution); }; diff --git a/test/spec/ol/tilegrid.test.js b/test/spec/ol/tilegrid.test.js index 63451a1668..abcb2c358a 100644 --- a/test/spec/ol/tilegrid.test.js +++ b/test/spec/ol/tilegrid.test.js @@ -258,6 +258,68 @@ describe('ol.tilegrid.TileGrid', function() { }); }); + + describe('getTileCoordForCoordAndResolution_', function() { + it('returns higher tile coord for intersections by default', function() { + var tileGrid = new ol.tilegrid.TileGrid({ + resolutions: resolutions, + extent: extent, + origin: origin, + tileSize: tileSize + }); + + var coordinate; + var tileCoord; + + // gets higher tile for edge intersection + coordinate = new ol.Coordinate(0, 0); + tileCoord = tileGrid.getTileCoordForCoordAndResolution_( + coordinate, 100); + expect(tileCoord.z).toEqual(3); + expect(tileCoord.x).toEqual(0); + expect(tileCoord.y).toEqual(0); + + // gets higher tile for edge intersection + coordinate = new ol.Coordinate(100000, 100000); + tileCoord = tileGrid.getTileCoordForCoordAndResolution_( + coordinate, 100); + expect(tileCoord.z).toEqual(3); + expect(tileCoord.x).toEqual(10); + expect(tileCoord.y).toEqual(10); + + }); + + it('handles alt intersection policy', function() { + var tileGrid = new ol.tilegrid.TileGrid({ + resolutions: resolutions, + extent: extent, + origin: origin, + tileSize: tileSize + }); + + var coordinate; + var tileCoord; + + // can get lower tile for edge intersection + coordinate = new ol.Coordinate(0, 0); + tileCoord = tileGrid.getTileCoordForCoordAndResolution_( + coordinate, 100, true); + expect(tileCoord.z).toEqual(3); + expect(tileCoord.x).toEqual(-1); + expect(tileCoord.y).toEqual(-1); + + // gets higher tile for edge intersection + coordinate = new ol.Coordinate(100000, 100000); + tileCoord = tileGrid.getTileCoordForCoordAndResolution_( + coordinate, 100, true); + expect(tileCoord.z).toEqual(3); + expect(tileCoord.x).toEqual(9); + expect(tileCoord.y).toEqual(9); + + }); + + }); + describe('getTileCoordCenter', function() { it('returns the expected center', function() { var tileGrid = new ol.tilegrid.TileGrid({ @@ -312,6 +374,46 @@ describe('ol.tilegrid.TileGrid', function() { }); }); + describe('getTileRangeForExtentAndResolution', function() { + it('returns the expected TileRange', function() { + var tileGrid = new ol.tilegrid.TileGrid({ + resolutions: resolutions, + extent: extent, + origin: origin, + tileSize: tileSize + }); + var tileRange; + + tileRange = tileGrid.getTileRangeForExtentAndResolution(extent, + resolutions[0]); + expect(tileRange.minY).toEqual(0); + expect(tileRange.minX).toEqual(0); + expect(tileRange.maxX).toEqual(0); + expect(tileRange.maxY).toEqual(0); + + tileRange = tileGrid.getTileRangeForExtentAndResolution(extent, + resolutions[1]); + expect(tileRange.minX).toEqual(0); + expect(tileRange.minY).toEqual(0); + expect(tileRange.maxX).toEqual(1); + expect(tileRange.maxY).toEqual(1); + + tileRange = tileGrid.getTileRangeForExtentAndResolution(extent, + resolutions[2]); + expect(tileRange.minX).toEqual(0); + expect(tileRange.minY).toEqual(0); + expect(tileRange.maxX).toEqual(3); + expect(tileRange.maxY).toEqual(3); + + tileRange = tileGrid.getTileRangeForExtentAndResolution(extent, + resolutions[3]); + expect(tileRange.minX).toEqual(0); + expect(tileRange.minY).toEqual(0); + expect(tileRange.maxX).toEqual(9); + expect(tileRange.maxY).toEqual(9); + }); + }); + describe('getTileRangeForExtentAndZ', function() { it('returns the expected TileRange', function() { var tileGrid = new ol.tilegrid.TileGrid({ From 5148ad038c58482d4eec83b8f925ad26bf7ffb5a Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 19 Feb 2013 07:30:54 -0700 Subject: [PATCH 4/4] Sharing code with rectangle We cannot use the rectangle constructor here because a rectangle makes an assertion about min being less than max and a range may have equal min/max. --- src/ol/tilerange.js | 50 +++++---------------------------------------- 1 file changed, 5 insertions(+), 45 deletions(-) diff --git a/src/ol/tilerange.js b/src/ol/tilerange.js index 752716a4dc..76d3d7437c 100644 --- a/src/ol/tilerange.js +++ b/src/ol/tilerange.js @@ -1,7 +1,7 @@ goog.provide('ol.TileRange'); goog.require('goog.asserts'); -goog.require('ol.Size'); +goog.require('ol.Rectangle'); goog.require('ol.TileCoord'); @@ -11,6 +11,7 @@ goog.require('ol.TileCoord'); * by its min/max tile coordinates and is inclusive of coordinates. * * @constructor + * @extends {ol.Rectangle} * @param {number} minX Minimum X. * @param {number} minY Minimum Y. * @param {number} maxX Maximum X. @@ -39,6 +40,7 @@ ol.TileRange = function(minX, minY, maxX, maxY) { this.maxY = maxY; }; +goog.inherits(ol.TileRange, ol.Rectangle); /** @@ -83,28 +85,7 @@ ol.TileRange.prototype.containsTileRange = function(tileRange) { /** - * @param {ol.TileRange} range Other range. - * @return {boolean} The two ranges are equivalent. - */ -ol.TileRange.prototype.equals = function(range) { - return this.minX == range.minX && this.minY == range.minY && - this.maxX == range.maxX && this.maxY == range.maxY; -}; - - -/** - * Extend this range so it includes the other. - * @param {ol.TileRange} other Other range. - */ -ol.TileRange.prototype.extend = function(other) { - this.minX = Math.min(this.minX, other.minX); - this.minY = Math.min(this.minY, other.minY); - this.maxX = Math.max(this.maxX, other.maxX); - this.maxY = Math.max(this.maxY, other.maxY); -}; - - -/** + * @inheritDoc * @return {number} Height. */ ol.TileRange.prototype.getHeight = function() { @@ -113,30 +94,9 @@ ol.TileRange.prototype.getHeight = function() { /** - * @return {ol.Size} Size. - */ -ol.TileRange.prototype.getSize = function() { - return new ol.Size(this.getWidth(), this.getHeight()); -}; - - -/** + * @inheritDoc * @return {number} Width. */ ol.TileRange.prototype.getWidth = function() { return this.maxX - this.minX + 1; }; - - -/** - * Test for range intersection. - * @param {ol.TileRange} other Other range. - * @return {boolean} The two ranges intersect. - */ -ol.TileRange.prototype.intersects = function(other) { - return this.minX <= other.maxX && - this.maxX >= other.minX && - this.minY <= other.maxY && - this.maxY >= other.minY; -}; -