maplibre / maplibre-gl-js

MapLibre GL JS - Interactive vector tile maps in the browser
https://maplibre.org/maplibre-gl-js/docs/
Other
6.72k stars 718 forks source link

How to updating raster tile image #4836

Open Seungup opened 1 month ago

Seungup commented 1 month ago

User Story

It seems that using the method where the user directly draws to the framebuffer and creates the final image for each tile, then provides the data to the map, is the best approach to correctly render the map along with the terrain when the terrain is being displayed via the map.setTerrain function.

However, this method causes flickering when dynamic objects change, as the layer and source need to be removed and recreated for updates.

If there is a way to manually update the texture directly on the tile without recreating the layer and source, I would appreciate any guidance on how to achieve this.

Code Example

const createTileImageDataFromLocal = async (props): Promise<ImageData | null> => {

   // some extra logic here.

   const pixels = new Uint8ClampedArray(512 * 512 * 4);
   gl.bindFramebuffer( gl.FRAMEBUFFER, /*MY_FBO*/ );
   gl.readPixels(dx, dy, 512, 512, gl.RGBA, gl.UNSIGNED_BYTE, pixels); 
   gl.bindFramebuffer( gl.FRAMEBUFFER, null );

   return new ImageData(pixels, 512, 512);
}

maplibregl.addProtocol('custom', async (params) => {
  if (params.type === 'json') {
    return {
      data: {
        tiles: [`${params.url}/{z}/{x}/{y}`],
        maxzoom: 19
      }
    };
  }

  const re = new RegExp(/custom:\/\/(.+)\/(\d+)\/(\d+)\/(\d+)/);

  const result = params.url.match(re);

  if (!result) {
    throw new Error('Invalid protocol URL');
  }

  const z = Number(result[2]).valueOf();
  const x = Number(result[3]).valueOf();
  const y = Number(result[4]).valueOf();

  const data: ImageData | null = await createTileImageDataFromLocal({ z, x, y });

  if (data) {
    const bitmap = await createImageBitmap(data);
    return { data: bitmap };
  }

  return { data: null };
});

map.addSource('custom-tile', {
  type: 'raster',
  url: 'custom://tiles',
  tileSize: 512
});

map.addLayer({
  id: 'custom-layer',
  source: 'custom-tile',
  type: 'raster'
});

map.addSource('terrain', {
  type: 'raster-dem',
  url: /* URL */ 
});

map.setTerrain({ source: 'terrain' });

// draw all renew tile with flickering 
const refetch = () => {
  // remove all source and layer
  map.removeLayer('custom-layer');
  map.removeSource('custom-tile');

  // create again
  map.addSource('custom-tile', {
    type: 'raster',
    url: 'custom://tiles',
    tileSize: 512
  });

  map.addLayer({
    id: 'custom-layer',
    source: 'custom-tile',
    type: 'raster'
  });
}
HarelM commented 1 month ago

Thanks for taking the time to report this issue. I think there are two different issues reported here. I would advise to split this issue into two in order to address each one separately.

Seungup commented 1 month ago

Yes, I will change it to a more appropriate title and content. Thank you.