diff --git a/src/ol/layer/TileLayer.js b/src/ol/layer/TileLayer.js index fb34627a21..deadaaec98 100644 --- a/src/ol/layer/TileLayer.js +++ b/src/ol/layer/TileLayer.js @@ -351,6 +351,9 @@ ol.layer.TileLayer.prototype.getTile = function(url, bounds) { * @param {number} z */ ol.layer.TileLayer.prototype.getTileForXYZ = function(x, y, z) { + if (!this.validXY(x, y, z)) { + return null; + } var tileUrl = this.getTileUrl(x, y, z); var tile = this.cache_.get(tileUrl); if (!goog.isDef(tile)) { @@ -360,6 +363,48 @@ ol.layer.TileLayer.prototype.getTileForXYZ = function(x, y, z) { return tile; }; +/** + * Determine if the tile x/y/z is with the layer extent. Return + * true if the layer has no extent. + * @param {number} x + * @param {number} y + * @param {number} z + * @return {boolean} + */ +ol.layer.TileLayer.prototype.validXY = function(x, y, z) { + var extent = this.getExtent(); + if (goog.isNull(extent)) { + return true; + } + var extentMinX = extent.getMinX(), + extentMinY = extent.getMinY(), + extentMaxX = extent.getMaxX(), + extentMaxY = extent.getMaxY(); + var tileOrigin = this.getTileOrigin(), + tileOriginX = tileOrigin[0], + tileOriginY = tileOrigin[1]; + var resolution = this.getResolutions()[z]; + var tileWidth = this.tileWidth_ * resolution, + tileHeight = this.tileHeight_ * resolution; + var minX, maxX; + if (this.xRight_) { + minX = Math.floor((extentMinX - tileOriginX) / tileWidth); + maxX = Math.ceil((extentMaxX - tileOriginX) / tileWidth) - 1; + } else { + minX = Math.floor((tileOriginX - extentMaxX) / tileWidth); + maxX = Math.ceil((tileOriginX - extentMinX) / tileWidth) - 1; + } + var minY, maxY; + if (this.yDown_) { + minY = Math.floor((tileOriginY - extentMaxY) / tileHeight); + maxY = Math.ceil((tileOriginY - extentMinY) / tileHeight) - 1; + } else { + minY = Math.floor((extentMinY - tileOriginY) / tileHeight); + maxY = Math.ceil((extentMaxY - tileOriginY) / tileHeight) - 1; + } + return x >= minX && x <= maxX && y >= minY && y <= maxY; +}; + /** * Get data from the layer. This is the layer's main API function. * @param {ol.Bounds} bounds diff --git a/src/ol/renderer/TileLayerRenderer.js b/src/ol/renderer/TileLayerRenderer.js index e588c989d6..0a01910432 100644 --- a/src/ol/renderer/TileLayerRenderer.js +++ b/src/ol/renderer/TileLayerRenderer.js @@ -215,24 +215,29 @@ ol.renderer.TileLayerRenderer.prototype.draw = function(center, resolution) { } else { pxTileBottom = pxTileTop + pxTileHeight; } + img = null; tile = this.renderedTiles_[xyz]; if (!tile) { tile = this.layer_.getTileForXYZ(tileX, tileY, tileZ); - if (!tile.isLoaded() && !tile.isLoading()) { - tile.load(); + if (tile) { + if (!tile.isLoaded() && !tile.isLoading()) { + tile.load(); + } + this.renderedTiles_[xyz] = tile; + img = tile.getImg(); + goog.dom.appendChild(fragment, img); + newTiles = true; } - this.renderedTiles_[xyz] = tile; - img = tile.getImg(); - goog.dom.appendChild(fragment, img); - newTiles = true; } else { img = tile.getImg(); } - img.style.top = pxTileTop + "px"; - img.style.left = pxTileLeft + "px"; - if (scale !== 1) { - img.style.height = (pxTileRight - pxTileLeft) + "px"; - img.style.width = (pxTileBottom - pxTileTop) + "px"; + if (img) { + img.style.top = pxTileTop + "px"; + img.style.left = pxTileLeft + "px"; + if (scale !== 1) { + img.style.height = (pxTileRight - pxTileLeft) + "px"; + img.style.width = (pxTileBottom - pxTileTop) + "px"; + } } pxTileTop = pxTileBottom; } diff --git a/test/spec/ol/layer/TileLayer.test.js b/test/spec/ol/layer/TileLayer.test.js index 4c21263eda..c4d46ca3a6 100644 --- a/test/spec/ol/layer/TileLayer.test.js +++ b/test/spec/ol/layer/TileLayer.test.js @@ -397,4 +397,487 @@ describe('ol.layer.TileLayer', function() { }); }); + + describe('test x y validity', function() { + var layer; + + beforeEach(function() { + layer = new ol.layer.TileLayer(); + layer.setTileOrigin(0, 0); + layer.setResolutions([1, 0.5, 0.25]); + }); + + describe('tile index goes right/down', function() { + + beforeEach(function() { + layer.setXRight(true); + layer.setYDown(true); + }); + + describe('extent is right/down of origin', function() { + + describe('extent and tile boundaries match', function() { + + beforeEach(function() { + layer.setExtent(new ol.Bounds(128, -384, 384, -128)); + }); + + it('detects valid x y', function() { + var v; + v = layer.validXY(0, 0, 1); + expect(v).toBeFalsy(); + v = layer.validXY(1, 0, 1); + expect(v).toBeFalsy(); + v = layer.validXY(2, 0, 1); + expect(v).toBeFalsy(); + v = layer.validXY(3, 0, 1); + expect(v).toBeFalsy(); + + v = layer.validXY(0, 1, 1); + expect(v).toBeFalsy(); + v = layer.validXY(1, 1, 1); + expect(v).toBeTruthy(); + v = layer.validXY(2, 1, 1); + expect(v).toBeTruthy(); + v = layer.validXY(3, 1, 1); + expect(v).toBeFalsy(); + + v = layer.validXY(0, 2, 1); + expect(v).toBeFalsy(); + v = layer.validXY(1, 2, 1); + expect(v).toBeTruthy(); + v = layer.validXY(2, 2, 1); + expect(v).toBeTruthy(); + v = layer.validXY(3, 2, 1); + expect(v).toBeFalsy(); + + v = layer.validXY(0, 3, 1); + expect(v).toBeFalsy(); + v = layer.validXY(1, 3, 1); + expect(v).toBeFalsy(); + v = layer.validXY(2, 3, 1); + expect(v).toBeFalsy(); + v = layer.validXY(3, 3, 1); + expect(v).toBeFalsy(); + }); + }); + + describe('extent and tile boundaries do not match', function() { + + beforeEach(function() { + layer.setExtent(new ol.Bounds(192, -448, 448, -192)); + }); + + it('detects valid x y', function() { + var v; + v = layer.validXY(0, 0, 1); + expect(v).toBeFalsy(); + v = layer.validXY(1, 0, 1); + expect(v).toBeFalsy(); + v = layer.validXY(2, 0, 1); + expect(v).toBeFalsy(); + v = layer.validXY(3, 0, 1); + expect(v).toBeFalsy(); + v = layer.validXY(4, 0, 1); + expect(v).toBeFalsy(); + + v = layer.validXY(0, 1, 1); + expect(v).toBeFalsy(); + v = layer.validXY(1, 1, 1); + expect(v).toBeTruthy(); + v = layer.validXY(2, 1, 1); + expect(v).toBeTruthy(); + v = layer.validXY(3, 1, 1); + expect(v).toBeTruthy(); + v = layer.validXY(4, 1, 1); + expect(v).toBeFalsy(); + + v = layer.validXY(0, 2, 1); + expect(v).toBeFalsy(); + v = layer.validXY(1, 2, 1); + expect(v).toBeTruthy(); + v = layer.validXY(2, 2, 1); + expect(v).toBeTruthy(); + v = layer.validXY(3, 2, 1); + expect(v).toBeTruthy(); + v = layer.validXY(4, 2, 1); + expect(v).toBeFalsy(); + + v = layer.validXY(0, 3, 1); + expect(v).toBeFalsy(); + v = layer.validXY(1, 3, 1); + expect(v).toBeTruthy(); + v = layer.validXY(2, 3, 1); + expect(v).toBeTruthy(); + v = layer.validXY(3, 3, 1); + expect(v).toBeTruthy(); + v = layer.validXY(4, 3, 1); + expect(v).toBeFalsy(); + + v = layer.validXY(0, 4, 1); + expect(v).toBeFalsy(); + v = layer.validXY(1, 4, 1); + expect(v).toBeFalsy(); + v = layer.validXY(2, 4, 1); + expect(v).toBeFalsy(); + v = layer.validXY(3, 4, 1); + expect(v).toBeFalsy(); + v = layer.validXY(4, 4, 1); + expect(v).toBeFalsy(); + }); + }); + + }); + + describe('extent is left/up of origin', function() { + + describe('extent and tile boundaries match', function() { + + beforeEach(function() { + layer.setExtent(new ol.Bounds(-384, 128, -128, 384)); + }); + + it('detects valid x y', function() { + var v; + v = layer.validXY(-1, -1, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-2, -1, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-3, -1, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-4, -1, 1); + expect(v).toBeFalsy(); + + v = layer.validXY(-1, -2, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-2, -2, 1); + expect(v).toBeTruthy(); + v = layer.validXY(-3, -2, 1); + expect(v).toBeTruthy(); + v = layer.validXY(-4, -2, 1); + expect(v).toBeFalsy(); + + v = layer.validXY(-1, -3, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-2, -3, 1); + expect(v).toBeTruthy(); + v = layer.validXY(-3, -3, 1); + expect(v).toBeTruthy(); + v = layer.validXY(-4, -3, 1); + expect(v).toBeFalsy(); + + v = layer.validXY(-1, -4, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-2, -4, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-3, -4, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-4, -4, 1); + expect(v).toBeFalsy(); + }); + }); + + describe('extent and tile boundaries do not match', function() { + + beforeEach(function() { + layer.setExtent(new ol.Bounds(-448, 192, -192, 448)); + }); + + it('detects valid x y', function() { + var v; + v = layer.validXY(-1, -1, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-2, -1, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-3, -1, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-4, -1, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-5, -1, 1); + expect(v).toBeFalsy(); + + v = layer.validXY(-1, -2, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-2, -2, 1); + expect(v).toBeTruthy(); + v = layer.validXY(-3, -2, 1); + expect(v).toBeTruthy(); + v = layer.validXY(-4, -2, 1); + expect(v).toBeTruthy(); + v = layer.validXY(-5, -2, 1); + expect(v).toBeFalsy(); + + v = layer.validXY(-1, -3, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-2, -3, 1); + expect(v).toBeTruthy(); + v = layer.validXY(-3, -3, 1); + expect(v).toBeTruthy(); + v = layer.validXY(-4, -3, 1); + expect(v).toBeTruthy(); + v = layer.validXY(-5, -3, 1); + expect(v).toBeFalsy(); + + v = layer.validXY(-1, -4, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-2, -4, 1); + expect(v).toBeTruthy(); + v = layer.validXY(-3, -4, 1); + expect(v).toBeTruthy(); + v = layer.validXY(-4, -4, 1); + expect(v).toBeTruthy(); + v = layer.validXY(-5, -4, 1); + expect(v).toBeFalsy(); + + v = layer.validXY(-1, -5, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-2, -5, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-3, -5, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-4, -5, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-5, -5, 1); + expect(v).toBeFalsy(); + }); + }); + }); + }); + + describe('tile index goes left/up', function() { + + beforeEach(function() { + layer.setXRight(false); + layer.setYDown(false); + }); + + describe('extent is right/down of origin', function() { + + describe('extent and tile boundaries match', function() { + + beforeEach(function() { + layer.setExtent(new ol.Bounds(128, -384, 384, -128)); + }); + + it('detects valid x y', function() { + var v; + v = layer.validXY(-1, -1, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-2, -1, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-3, -1, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-4, -1, 1); + expect(v).toBeFalsy(); + + v = layer.validXY(-1, -2, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-2, -2, 1); + expect(v).toBeTruthy(); + v = layer.validXY(-3, -2, 1); + expect(v).toBeTruthy(); + v = layer.validXY(-4, -2, 1); + expect(v).toBeFalsy(); + + v = layer.validXY(-1, -3, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-2, -3, 1); + expect(v).toBeTruthy(); + v = layer.validXY(-3, -3, 1); + expect(v).toBeTruthy(); + v = layer.validXY(-4, -3, 1); + expect(v).toBeFalsy(); + + v = layer.validXY(-1, -4, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-2, -4, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-3, -4, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-4, -4, 1); + expect(v).toBeFalsy(); + }); + }); + + describe('extent and tile boundaries do not match', function() { + + beforeEach(function() { + layer.setExtent(new ol.Bounds(192, -448, 448, -192)); + }); + + it('detects valid x y', function() { + var v; + v = layer.validXY(-1, -1, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-2, -1, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-3, -1, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-4, -1, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-5, -1, 1); + expect(v).toBeFalsy(); + + v = layer.validXY(-1, -2, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-2, -2, 1); + expect(v).toBeTruthy(); + v = layer.validXY(-3, -2, 1); + expect(v).toBeTruthy(); + v = layer.validXY(-4, -2, 1); + expect(v).toBeTruthy(); + v = layer.validXY(-5, -2, 1); + expect(v).toBeFalsy(); + + v = layer.validXY(-1, -3, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-2, -3, 1); + expect(v).toBeTruthy(); + v = layer.validXY(-3, -3, 1); + expect(v).toBeTruthy(); + v = layer.validXY(-4, -3, 1); + expect(v).toBeTruthy(); + v = layer.validXY(-5, -3, 1); + expect(v).toBeFalsy(); + + v = layer.validXY(-1, -4, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-2, -4, 1); + expect(v).toBeTruthy(); + v = layer.validXY(-3, -4, 1); + expect(v).toBeTruthy(); + v = layer.validXY(-4, -4, 1); + expect(v).toBeTruthy(); + v = layer.validXY(-5, -4, 1); + expect(v).toBeFalsy(); + + v = layer.validXY(-1, -5, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-2, -5, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-3, -5, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-4, -5, 1); + expect(v).toBeFalsy(); + v = layer.validXY(-5, -5, 1); + expect(v).toBeFalsy(); + }); + }); + }); + + describe('extent is left/up of origin', function() { + + describe('extent and tile boundaries match', function() { + + beforeEach(function() { + layer.setExtent(new ol.Bounds(-384, 128, -128, 384)); + }); + + it('detects valid x y', function() { + var v; + v = layer.validXY(0, 0, 1); + expect(v).toBeFalsy(); + v = layer.validXY(1, 0, 1); + expect(v).toBeFalsy(); + v = layer.validXY(2, 0, 1); + expect(v).toBeFalsy(); + v = layer.validXY(3, 0, 1); + expect(v).toBeFalsy(); + + v = layer.validXY(0, 1, 1); + expect(v).toBeFalsy(); + v = layer.validXY(1, 1, 1); + expect(v).toBeTruthy(); + v = layer.validXY(2, 1, 1); + expect(v).toBeTruthy(); + v = layer.validXY(3, 1, 1); + expect(v).toBeFalsy(); + + v = layer.validXY(0, 2, 1); + expect(v).toBeFalsy(); + v = layer.validXY(1, 2, 1); + expect(v).toBeTruthy(); + v = layer.validXY(2, 2, 1); + expect(v).toBeTruthy(); + v = layer.validXY(3, 2, 1); + expect(v).toBeFalsy(); + + v = layer.validXY(0, 3, 1); + expect(v).toBeFalsy(); + v = layer.validXY(1, 3, 1); + expect(v).toBeFalsy(); + v = layer.validXY(2, 3, 1); + expect(v).toBeFalsy(); + v = layer.validXY(3, 3, 1); + expect(v).toBeFalsy(); + }); + }); + + describe('extent and tile boundaries do not match', function() { + + beforeEach(function() { + layer.setExtent(new ol.Bounds(-448, 192, -192, 448)); + }); + + it('detects valid x y', function() { + var v; + v = layer.validXY(0, 0, 1); + expect(v).toBeFalsy(); + v = layer.validXY(1, 0, 1); + expect(v).toBeFalsy(); + v = layer.validXY(2, 0, 1); + expect(v).toBeFalsy(); + v = layer.validXY(3, 0, 1); + expect(v).toBeFalsy(); + v = layer.validXY(4, 0, 1); + expect(v).toBeFalsy(); + + v = layer.validXY(0, 1, 1); + expect(v).toBeFalsy(); + v = layer.validXY(1, 1, 1); + expect(v).toBeTruthy(); + v = layer.validXY(2, 1, 1); + expect(v).toBeTruthy(); + v = layer.validXY(3, 1, 1); + expect(v).toBeTruthy(); + v = layer.validXY(4, 1, 1); + expect(v).toBeFalsy(); + + v = layer.validXY(0, 2, 1); + expect(v).toBeFalsy(); + v = layer.validXY(1, 2, 1); + expect(v).toBeTruthy(); + v = layer.validXY(2, 2, 1); + expect(v).toBeTruthy(); + v = layer.validXY(3, 2, 1); + expect(v).toBeTruthy(); + v = layer.validXY(4, 2, 1); + expect(v).toBeFalsy(); + + v = layer.validXY(0, 3, 1); + expect(v).toBeFalsy(); + v = layer.validXY(1, 3, 1); + expect(v).toBeTruthy(); + v = layer.validXY(2, 3, 1); + expect(v).toBeTruthy(); + v = layer.validXY(3, 3, 1); + expect(v).toBeTruthy(); + v = layer.validXY(4, 3, 1); + expect(v).toBeFalsy(); + + v = layer.validXY(0, 4, 1); + expect(v).toBeFalsy(); + v = layer.validXY(1, 4, 1); + expect(v).toBeFalsy(); + v = layer.validXY(2, 4, 1); + expect(v).toBeFalsy(); + v = layer.validXY(3, 4, 1); + expect(v).toBeFalsy(); + v = layer.validXY(4, 4, 1); + expect(v).toBeFalsy(); + }); + }); + }); + }); + }); });