Search code examples
openlayers

TileSource XYZ: asynchronous tileUrlFunction


I am building an openlayers app which allows user to use offline tile images stored in IndexedDB as blobs:

export async function getOfflineTileURL(coordinates) {
  // input: coordinates[zoom, column, tile]
  let row = await db.tiles.get({
    zoom: coordinates[0],
    column: coordinates[1],
    tile: coordinates[2]
  })

  if (row) {
    // if tile image exists in database, return its URL
    return URL.createObjectURL(row.blob)
  } else {
    return
    // TODO: return URL to online source as backup
  }
}

db is Dexie.org database (wrapper for IndexedDB).

I want to use tileUrlFunction to return URL for the requested tile:

new TileLayer({
          source: new XYZ({
            tileUrlFunction: async(tileCoord) => {
              return await getOfflineTileURL(tileCoord)
            }
          })
        })

I cannot use url property in XYZ source because I cannot control output of URL.createObjectURL, it's just random string (ex. blob:http://localhost:5173/a0cdebd5-df03-4134-9e93-b4d8ad888d6d

And I cannot use titleURLFunction, because my code has to be async (Dexie and IndexedDB are asynchronous) and openlayers function only works synchronously (titleURLFunction from the code above returns http://localhost:5173/[object%20Promise])

Is there a way I can make it work?


Solution

  • It can be done via the tileLoadFunction, which is asynchronous

    tileLoadFunction: async function (tile, src) {
      const coordinates = tile.getTileCoord();
      let row = await db.tiles.get({
        zoom: coordinates[0],
        column: coordinates[1],
        tile: coordinates[2]
      })
    
      const image = tile.getImage();
      if (row) {
        // if tile image exists in database, return its URL
        const result = URL.createObjectURL(row.blob);
        image.addEventListener('load', function() {
           URL.revokeObjectURL(result);
        });
        image.src = result;
      } else {
        image.src = src;
      }
    }