diff --git a/src/ol/renderer/canvas/vectortilelayer.js b/src/ol/renderer/canvas/vectortilelayer.js index 280db28f13..ed0ffead4e 100644 --- a/src/ol/renderer/canvas/vectortilelayer.js +++ b/src/ol/renderer/canvas/vectortilelayer.js @@ -153,6 +153,9 @@ ol.renderer.canvas.VectorTileLayer.prototype.createReplayGroup_ = function( for (var t = 0, tt = tile.tileKeys.length; t < tt; ++t) { var sourceTile = tile.getTile(tile.tileKeys[t]); + if (sourceTile.getState() == ol.TileState.ERROR) { + continue; + } replayState.dirty = false; var sourceTileCoord = sourceTile.tileCoord; @@ -274,6 +277,9 @@ ol.renderer.canvas.VectorTileLayer.prototype.forEachFeatureAtCoordinate = functi } for (var t = 0, tt = tile.tileKeys.length; t < tt; ++t) { var sourceTile = tile.getTile(tile.tileKeys[t]); + if (sourceTile.getState() == ol.TileState.ERROR) { + continue; + } if (sourceTile.getProjection().getUnits() === ol.proj.Units.TILE_PIXELS) { var sourceTileCoord = sourceTile.tileCoord; var sourceTileExtent = sourceTileGrid.getTileCoordExtent(sourceTileCoord, this.tmpExtent); @@ -395,6 +401,9 @@ ol.renderer.canvas.VectorTileLayer.prototype.postCompose = function(context, fra tileGrid.getTileCoordExtent(tile.wrappedTileCoord)[0]; for (var t = 0, tt = tile.tileKeys.length; t < tt; ++t) { var sourceTile = tile.getTile(tile.tileKeys[t]); + if (sourceTile.getState() == ol.TileState.ERROR) { + continue; + } var tilePixelRatio = this.getTilePixelRatio_(source, sourceTile); var replayGroup = sourceTile.getReplayGroup(layer, tileCoord.toString()); if (renderMode != ol.layer.VectorTileRenderType.VECTOR && !replayGroup.hasReplays(replays)) { @@ -493,6 +502,9 @@ ol.renderer.canvas.VectorTileLayer.prototype.renderTileImage_ = function( var tileExtent = tileGrid.getTileCoordExtent(tileCoord); for (var i = 0, ii = tile.tileKeys.length; i < ii; ++i) { var sourceTile = tile.getTile(tile.tileKeys[i]); + if (sourceTile.getState() == ol.TileState.ERROR) { + continue; + } var tilePixelRatio = this.getTilePixelRatio_(source, sourceTile); var sourceTileCoord = sourceTile.tileCoord; var pixelScale = pixelRatio / resolution; diff --git a/src/ol/vectorimagetile.js b/src/ol/vectorimagetile.js index b7f7d3e1bc..5a782fa5d0 100644 --- a/src/ol/vectorimagetile.js +++ b/src/ol/vectorimagetile.js @@ -3,7 +3,6 @@ goog.provide('ol.VectorImageTile'); goog.require('ol'); goog.require('ol.Tile'); goog.require('ol.TileState'); -goog.require('ol.array'); goog.require('ol.dom'); goog.require('ol.events'); goog.require('ol.extent'); @@ -134,10 +133,8 @@ ol.VectorImageTile.prototype.disposeInternal = function() { } this.tileKeys.length = 0; this.sourceTiles_ = null; - if (this.state == ol.TileState.LOADING) { - this.loadListenerKeys_.forEach(ol.events.unlistenByKey); - this.loadListenerKeys_.length = 0; - } + this.loadListenerKeys_.forEach(ol.events.unlistenByKey); + this.loadListenerKeys_.length = 0; if (this.interimTile) { this.interimTile.dispose(); } @@ -212,7 +209,13 @@ ol.VectorImageTile.prototype.getTile = function(tileKey) { * @inheritDoc */ ol.VectorImageTile.prototype.load = function() { + // Source tiles with LOADED state - we just count them because once they are + // loaded, we're no longer listening to state changes. var leftToLoad = 0; + // Source tiles with ERROR state - we track them because they can still have + // an ERROR state after another load attempt. + var errorSourceTiles = {}; + if (this.state == ol.TileState.IDLE) { this.setState(ol.TileState.LOADING); } @@ -228,10 +231,14 @@ ol.VectorImageTile.prototype.load = function() { var state = sourceTile.getState(); if (state == ol.TileState.LOADED || state == ol.TileState.ERROR) { - --leftToLoad; - ol.events.unlistenByKey(key); - ol.array.remove(this.loadListenerKeys_, key); - if (leftToLoad == 0) { + var uid = ol.getUid(sourceTile); + if (state == ol.TileState.ERROR) { + errorSourceTiles[uid] = true; + } else { + --leftToLoad; + delete errorSourceTiles[uid]; + } + if (leftToLoad - Object.keys(errorSourceTiles).length == 0) { this.finishLoading_(); } } @@ -241,7 +248,7 @@ ol.VectorImageTile.prototype.load = function() { } }.bind(this)); } - if (leftToLoad == 0) { + if (leftToLoad - Object.keys(errorSourceTiles).length == 0) { setTimeout(this.finishLoading_.bind(this), 0); } }; @@ -251,21 +258,18 @@ ol.VectorImageTile.prototype.load = function() { * @private */ ol.VectorImageTile.prototype.finishLoading_ = function() { - var errors = false; var loaded = this.tileKeys.length; - var state; for (var i = loaded - 1; i >= 0; --i) { - state = this.getTile(this.tileKeys[i]).getState(); + var state = this.getTile(this.tileKeys[i]).getState(); if (state != ol.TileState.LOADED) { - if (state == ol.TileState.ERROR) { - errors = true; - } --loaded; } } - this.setState(loaded > 0 ? - ol.TileState.LOADED : - (errors ? ol.TileState.ERROR : ol.TileState.EMPTY)); + if (loaded == this.tileKeys.length) { + this.loadListenerKeys_.forEach(ol.events.unlistenByKey); + this.loadListenerKeys_.length = 0; + } + this.setState(loaded > 0 ? ol.TileState.LOADED : ol.TileState.EMPTY); }; diff --git a/test/spec/ol/vectorimagetile.test.js b/test/spec/ol/vectorimagetile.test.js index 8cd14276e8..6d7559f802 100644 --- a/test/spec/ol/vectorimagetile.test.js +++ b/test/spec/ol/vectorimagetile.test.js @@ -29,7 +29,7 @@ describe('ol.VectorImageTile', function() { }); }); - it('sets ERROR state when source tiles fail to load', function(done) { + it('sets LOADED state when source tiles fail to load', function(done) { var format = new ol.format.GeoJSON(); var url = 'spec/ol/data/unavailable.json'; var tile = new ol.VectorImageTile([0, 0, 0], 0, url, format, @@ -41,7 +41,52 @@ describe('ol.VectorImageTile', function() { tile.load(); ol.events.listen(tile, 'change', function(e) { - expect(tile.getState()).to.be(ol.TileState.ERROR); + expect(tile.getState()).to.be(ol.TileState.EMPTY); + done(); + }); + }); + + it('sets LOADED state when previously failed source tiles are loaded', function(done) { + var format = new ol.format.GeoJSON(); + var url = 'spec/ol/data/unavailable.json'; + var sourceTile; + var tile = new ol.VectorImageTile([0, 0, 0], 0, url, format, + function(tile, url) { + sourceTile = tile; + ol.VectorImageTile.defaultLoadFunction(tile, url); + }, [0, 0, 0], function() { + return url; + }, ol.tilegrid.createXYZ(), ol.tilegrid.createXYZ(), {}, + 1, ol.proj.get('EPSG:3857'), ol.VectorTile, function() {}); + + tile.load(); + var calls = 0; + ol.events.listen(tile, 'change', function(e) { + ++calls; + expect(tile.getState()).to.be(calls == 2 ? ol.TileState.LOADED : ol.TileState.EMPTY); + if (calls == 2) { + done(); + } else { + setTimeout(function() { + sourceTile.setState(ol.TileState.LOADED); + }, 0); + } + }); + }); + + it('sets EMPTY state when all source tiles fail to load', function(done) { + var format = new ol.format.GeoJSON(); + var url = 'spec/ol/data/unavailable.json'; + var tile = new ol.VectorImageTile([0, 0, 0], 0, url, format, + ol.VectorImageTile.defaultLoadFunction, [0, 0, 0], function() { + return url; + }, ol.tilegrid.createXYZ(), ol.tilegrid.createXYZ(), {}, + 1, ol.proj.get('EPSG:3857'), ol.VectorTile, function() {}); + + tile.load(); + + ol.events.listen(tile, 'change', function(e) { + expect(tile.getState()).to.be(ol.TileState.EMPTY); done(); }); });