I have a map in OpenLayers with TileWMS source that's not very reliable. At almost every full page load there are a few tiles that fail to load with status 503 - Service Unavailable
. When I try to load failed tile a bit later 'manually', it loads without a problem. That lead me to the idea of reloading failed tile if first try is not successful.
I couldn't find any OpenLayers 'official' way of doing that, so I tried to achieve that using tileLoadFunction
, where I initiate reload of the tile with 2 seconds delay and before reload check if tile was already loaded successfully. Reload is triggered by first assigning dummy image source for tile and then the original one. Successful loading of the tile is checked by hooking on tileloadend
event and setting custom flag evt.tile._loaded = true
.
Here is the relevant code:
proj4.defs('EPSG:3912',
'+proj=tmerc +lat_0=0 +lon_0=15 +k=0.9999 +x_0=500000 +y_0=-5000000 +ellps=bessel +towgs84=682,-203,480,0,0,0,0 +units=m +no_defs');
var proj3912 = ol.proj.get('EPSG:3912');
proj3912.setExtent([374371.84, 30513.32, 624119.18, 195517.48]);
var tileGridResolutions = [1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1, 0.5];
var dofLayer = new ol.layer.Tile({
source: new ol.source.TileWMS({
url: 'https://prostor4.gov.si/ows2-m-pub/wms',
attributions: ['<a href="http://www.gu.gov.si/">GURS</a>'],
params: {
'LAYERS': 'SI.GURS.ZPDZ:DOF050_D48',
'FORMAT': 'image/png',
'VERSION': '1.3.0'
},
tileGrid: new ol.tilegrid.TileGrid({
extent: proj3912.getExtent(),
resolutions: tileGridResolutions,
tileSize: 256
}),
tileLoadFunction: tileLoadFunction,
projection: proj3912
}),
title: 'Ortofoto',
type: 'base'
});
function tileLoadFunction(imageTile, src) {
imageTile.getImage().src = src;
if (typeof imageTile._retry == 'undefined') {
imageTile._retry = 0;
imageTile._loaded = false;
}
if (imageTile._retry < 3) {
imageTile._retry++;
setTimeout(function() {
if (!imageTile._loaded) {
imageTile.getImage().src = 'img/empty.png';
tileLoadFunction(imageTile, src);
}
}, 2000);
}
}
dofLayer.getSource().on('tileloadend', function(evt) {
evt.tile._loaded = true;
});
Unluckily this method works only in old venerable IE11, but not in Firefox and Chrome. There is no error, it just seems like image is not transferred from image element to canvas. Since I work in ES5 JS, I cannot debug OpenLayers internals to see where the problem is.
Is there any way to make the above method work in Firefox and Chrome or some other approach to try reloading failed TileWMS
tile?
1 Answer 1
You would get more control loading via xhr as in https://openlayers.org/en/main/apidoc/module-ol_Tile.html#~LoadFunction and scheduling a retry from the the xhr's error/timeout handler.
No error handling should be needed once you have an object url, but to avoid memory leaks
tile.getImage().src = URL.createObjectURL(data);
should be replaced by
var url = URL.createObjectURL(data);
tile.getImage().onload = function(){
URL.revokeObjectURL(url);
}
tile.getImage().src = url;
UPDATE
This sample code using a source very prone to errors is giving some success at a 2nd attempt after 5 seconds, while some tiles still fail.
var map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({
source: new ol.source.OSM({
url: 'https://{a-c}.tile.opentopomap.org/{z}/{x}/{y}.png',
maxZoom: 17,
tileLoadFunction: function (tile, src) {
function attemptLoad(attempt) {
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.addEventListener('load', function (evt) {
var data = this.response;
if (data !== undefined) {
var url = URL.createObjectURL(data);
if (attempt > 1) {
console.log('success at 2nd attempt ' + src);
}
tile.getImage().addEventListener('load', function () {
URL.revokeObjectURL(url);
});
tile.getImage().src = url;
} else {
console.log('no data ' + src);
tile.setState(3);
}
});
xhr.addEventListener('error', function () {
if (attempt > 1) {
console.log('fail at 2nd attempt ' + src);
tile.setState(3);
} else {
setTimeout(function () {
console.log('retry ' + src);
attemptLoad(attempt + 1);
}, 5000);
}
});
xhr.open('GET', src);
xhr.send();
}
attemptLoad(1);
}
})
})
],
view: new ol.View({
center: [0, 0],
zoom: 2
})
});
-
Thanks! I was getting close with your response to question gis.stackexchange.com/questions/364005/…, but failed to do 'mental jump' that
.src
can also be final content, not just url.TomazicM– TomazicM2020年07月18日 17:56:42 +00:00Commented Jul 18, 2020 at 17:56 -
Now I bumped into famous CORS error nad have no idea of how to get rid of it.TomazicM– TomazicM2020年07月18日 17:57:58 +00:00Commented Jul 18, 2020 at 17:57
-
You cannot make cross-origin xhr requests unless the server allows it (with a direct image load it works but you get tainted canvas). To get around it you would need a proxy on your server or localhost.Mike– Mike2020年07月18日 18:15:05 +00:00Commented Jul 18, 2020 at 18:15
-
Yes, I solved CORS problem with a simple local .php proxy page I have just for this purpose:
$content = file_get_contents($_GET["site"]); echo $content;
TomazicM– TomazicM2020年07月18日 18:22:13 +00:00Commented Jul 18, 2020 at 18:22 -
Unluckily this does not help in reloading failed tiles either. Normal first loading works, there is indeed greater control over error processing, but reload of failed tiles does not work.TomazicM– TomazicM2020年07月18日 19:41:55 +00:00Commented Jul 18, 2020 at 19:41