Dynamically replacing overlaid Image()-s on a tile map without resetting the whole map to default zoom

I’m trying to create a simple functionality where a user can select image layers (regridded by datashader) to be shown on a tile map by checking checkboxes. Also the images have customizable parameters, not just some opts() but something that specifies their contents.

I can create a customizable colormap for a specific image all right by tiles * hd.regrid(hv.DynamicMap(load_image)) where load_image() is a function that @pn.depends() on options coming from widgets. It works like a charm, even when the user is zoomed in, if they change the value of the colormap Select(), the image on the map changes without the whole map resetting.

But of course, I have multiple images all with their own colormap settings and any number of them should be visible. However, I can not figure out how to do this.

Imagine this setup:
tiles = hv.element.tiles.OSM()

pn_layers_all = pn.widgets.CheckBoxGroup(options=['Layer01', 'Layer02'])
pn_image01_cmaps = pn.widgets.Select(options=['inferno', 'viridis'])
pn_image02_cmaps = pn.widgets.Select(options=['inferno', 'viridis'])
pn_image01_ages = pn.widgets.IntSlider(start=0, end=80, step=1)
pn_image02_ages = pn.widgets.IntSlider(start=0, end=80, step=1)

@pn.depends(
    pn_layers = pn_layers_all.param.value, 
    pn_image01_cmap = pn_image01_cmaps.param.value,
    pn_image01_age = pn_image01_ages.param.value,
)
def load_image01(pn_layers, pn_image01_cmap, pn_image01_age):
    image = bla01(pn_image01_age) # some function that loads and gives back a hv.Image()
    image.opts(cmap=pn_image01_cmap)
    return image

@pn.depends(
    pn_layers = pn_layers_all.param.value, 
    pn_image02_cmap = pn_image02_cmaps.param.value,
    pn_image02_age = pn_image02_ages.param.value,
)
def load_image02(pn_layers, pn_image02_cmap, pn_image02_age):
    image = bla02(pn_image02_age) # some function that loads and gives back a hv.Image()
    image.opts(cmap=pn_image02_cmap)
    return image

So now this works:

dynmap01 = hd.regrid(hv.DynamicMap(load_image01))
combined = tiles * dynmap01
pn.Column(
    pn_layers_all, pn_image01_cmaps, pn_image02_cmaps, pn_image01_ages, pn_image02_ages, combined
)

The map shows up, and it reacts to changes in the widgets without resetting even when zoomed in.
The map shows up and reacts to changes in the widgets. combined = tiles * dynmap01 * dynmap02 also works.

However, the only way I could integrate the layer selection is by putting the hd.regrid(hv.DynamicMap()) part into the body of a @pn.depends function and returning tiles * dynmap01 * dynmap02 and of course that resets the whole map every time the layer selection changes, including the zoom level. So it is impossible for example to zoom in on an area of interest and quickly toggle a layer a few times to compare it to a layer below, or just to go through every relevant layer for an area without having to rezoom.

I went through all the relevant HV documentation I could find and I also tried any possible combination I could imagine to reload everything without reloading the tile map itself without any luck. I can not use combined = tiles * load_needed_regridded_images where the latter is a function because I get “Iteration on Elements is not supported.” error. I can not use combined = tiles * hv.DynamicMap(load_needed_regridded_images) as by regridding I already had DynamicMaps, so I get a “Nesting a DynamicMap inside a DynamicMap is not supported.” error. Etc etc.

Does anybody have an idea or similar example I could check? Thanks!

Maybe

I don’t understand what you’re referring to, could you please elaborate a bit?

Sorry, maybe I misunderstood. Can you elaborate on your goal? You want to overlay two images and keep zoom level when ticking off some box?

I want to overlay n images by selecting their corresponding checkboxes. I know that I can combine a tile-map with previously known images this way: tilemap * image01 * image02. This works the same way if imageXX are DynamicMaps, like what hd.regrid() returns. The question is how to overlay previously not known images, like those a user will be able to select from a list, without reloading the tilemap and thus zooming out.

In the Earth Engine web IDE this is how it looks like:


(Just the functionality of the checkboxes is relevant here, and imagine this with many more selectable layers from which the user will choose only a few, so preloading every layer is not an option. Also disregard the slow loading on zoom as that is the limitation of the Earth Engine IDE and not relevant here.)

I’m pretty sure there should be a load_needed_regridded_images function that @pn.depends on the pn_layers_all variable and returns the requested hd.regrid()-ded images combined with each other to be combined with the tilemap. I just can’t figure out how to combine a tilemap with a callable function that returns the regridded images. Usually I would do something like tilemap * hv.DynamicMap(load_needed_regridded_images), but as I mentioned above, I can not use DynamicMap here as each of the returned items are already wrapped in a DynamicMap by hd.regrid, and Nesting dynamic maps is not supported.