1

I've got an Openlayers map with a custom tileUrlFunction loading tiles from a custom tile server. Sometimes it can happen that the tileUrlFunction returns an adress that, deliberately, does not contain a tile. There can be two use cases for this:

  1. Empty tiles. That would be comparable to a completely blue tile in OpenStreetMap, which shows nothing but the sea.
  2. Default tiles. The tile would look exactly like another tile from some kind of default layer. For example, the map might have two raster tile layers, a default satellite view and a satellite view with certain structures highlighted. If a place has no highlighted structures, the tile from the highlighted view would look exactly like the one from the default view.

To reduce the number of files, my custom tile server does not contain such tiles, i. e. the paths simply do not exist. Whenever I try to load a tile from such an empty path, there is (of course) a tile error. I would like to use a fallback tile from another path. Depending on the adress of the tile that had failed to load, the new tile might use different paths.

Here's my solution so far:

var rasterLayer = new TileLayer({
        source: new XYZ({
          tileGrid: ... ,
          tileUrlFunction: ...,
        }),
        useInterimTilesOnError: false      
      });

// Error handling
rasterLayer_tiles.getSource().on("tileloaderror", errorFunction);
   
function errorFunction(e) {
      console.log("tile:", e.tile);                    // erroneous tile      
      console.log("src:", e.tile.src_);                // path that did not exist
      console.log("tileCoord:", e.tile.tileCoord);     // tile coordinates

      var tileCoord = e.tile.tileCoord;
      var replacement_Tile;
      if( ... ) {             // retrieve the path of fallback tile, potentially using information from e.tile
         // ...
         replacement_Tile = "errorTiles/blank.jpg";
       } else {
         // ...
         replacement_Tile = "defaultTiles/" + tileCoord[0] + "/" + tileCoord[1] + "/" + tileCoord[2] + ".jpg";
       }

      e.tile.src_ = replacement_Tile;
      e.tile.load();
}

This can be used to load the correct tiles; however there are several problems with that method:

  • The initial tile load error will still propagate to the console.
  • Apparently, there's no way to distinguish between errors where the path just didn't contain a tile and actual network errors. Possibly, this could also lead to an endless loop.
  • The solution relies on the private source attribute to be exposed (see also here).

Is there a more elegant solution? I am looking for some kind of customizable errorTileUrlFunction here.

1
3

An xhr based tileLoadFunction will give you access to the error code. You can then set the src to the objectUrl created from a successfully xhr (object urls should be revoked after use to release the memory) or an appropriate error tile, for example

tileLoadFunction: function(tile, src) {
  var xhr = new XMLHttpRequest();
  xhr.responseType = 'blob';
  xhr.addEventListener('loadend', function (evt) {
    var data = this.response;
    if (data !== undefined) {
      var objectUrl = URL.createObjectURL(data);
      tile.getImage().onload  = function(){
        URL.revokeObjectURL(objectUrl);
      }
      tile.getImage().src = objectUrl;
    } else if (xhr.status == 404) {
      tile.getImage().src = 'missing.png';
    } else {
      tile.getImage().src = 'error.png';
    }
  });
  xhr.addEventListener('error', function () {
    if (xhr.status == 404) {
      tile.getImage().src = 'missing.png';
    } else {
      tile.getImage().src = 'error.png';
    }
  });
  xhr.open('GET', src);
  xhr.send();
});
2
  • Thank you for this answer! Unfortunately, using the code in the tileLoadFunction of the source, the error handling doesn't work for me. Normal tiles (including those missing) continue to be loaded. I've checked the tileLoadFunction is called correctly but for some reason, the error case is never reached. Whenever I check xhr.status, it is always 0, even if the 404 error propagates to the console. This problem occurs both when testing locally and testing on the server. Jun 14 at 13:11
  • 1
    I've updated the answer as the 404 status needs to be tested in the success callback, see stackoverflow.com/questions/30426277/catch-a-404-error-for-xhr
    – Mike
    Jun 14 at 14:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.