Activating save option with WMTS Overlay

Hi guys,
ususally when one creates a holoviews DynamicMap rendered via bokeh the save button is a default tool. But when I overlay the rasterized data with a WMTS Tile service the save tool does not appear anymore and I would like to know the reason why I am not able to activate it.
Does anyone has an idea how to solve this problem?

Code snippet :

import panel as pn
import holoviews as hv
tiles = hv.Tiles('{Z}/{X}/{Y}@2x.png', name="Wikipedia").opts(width=800, height=500, xaxis=None, yaxis=None, tools=['save'])
tiles = tiles.redim(
        x=hv.Dimension('x', range=(775968.6623360633, 785207.6721131478)), 
        y=hv.Dimension('y', range=(6596932.96870053, 6603273.942845271)))
plot = hv.DynamicMap(tiles)

Sadly due to browser security restrictions (cross-origin policies to be precise) it is currently not possible to save WMTS tiles with the save tool and there does not appear to be a workaround here. The best you might be able to do is build a solution that saves the output server side using and then use a FileDownload button to trigger that and download it in the browser.

1 Like

Thanks for replying @philippjfr . I hoped their would be a workaround… Do you know any examle outlining the way with and FileDownload?
Anyway, in future I would love to impement a feature that puts the tiles and rasterized data together with additional meta information on one sheet. Maybe the option with and FileDownload is a step in the right direction.

Here’s a pretty basic example:

from io import BytesIO

import holoviews as hv
import numpy as np
import panel as pn


img = hv.Image(np.random.rand(100, 100), bounds=(-10e5, -10e5, 10e5, 10e5))
plot = hv.element.tiles.Wikipedia() * img

def callback():
    b = BytesIO(), b, fmt='png')
    return b

download = pn.widgets.FileDownload(callback=callback, filename='export.png')
pn.Row(plot, download)


May have to add a RangeXY stream to track the current zoom extents and apply those before the save.

the Callback function with data download is exactly what I need. Downloading the tile element with overlaying geoviews objects works very well. But when I try to download the rasterized DynamicMap it does not work anymore. The downloaded .png file is not accessible and the comuputer gives me the message, that its not possible to open it because the file is damaged and or “Cannot open image. Not a valid bitmap file or its format is not currently supported.”.
Could it be an Issue that its a DynamicMap?

I think it might be forcing it to be an HTML which would be a bug in the save code. You could try simply getting the latest output from the DynamicMap by accessing .last and saving that for now.

the .last thing did not work for me (maybe I did it wrong) but I put the rasterized function into hv.HoloMap() That did the Job.

def callback_png():
    b = BytesIO()
    out = OV.simulation()['til']*hv.HoloMap(OV.simulation()['tri'])*OV.simulation()['poly_']*OV.simulation()['rinne1']*OV.simulation()['rinne2'], b, fmt = 'png', backend='bokeh',)
    return b

However I did not manage applying the RangeXY part to the Tiles. Means that the rasterized data is saved within the zoom extension and the WMTS Tile stays with the full extension.
How can I apply the RangeXY to the tiles? It seems to me that the Tile opts does not accept streams…
Another probem is that I did not see through the hole Streams topic at all.
Therefore sry for my dumb questions.