How to parametrize extra_url_vars in Tile source?

I want a slider / select for updating the time variable

from bokeh.models.tiles import BBoxTileSource
from bokeh.plotting import figure
from bokeh.tile_providers import get_provider, Vendors
from bokeh.io import show

p = figure(x_range=(-2000000, 6000000), y_range=(-1000000, 7000000),
           x_axis_type="mercator", y_axis_type="mercator")
url = "https://nowcoast.noaa.gov/arcgis/rest/services/nowcoast/analysis_meteohydro_sfc_rtma_time/MapServer/export?transparent=true&format=png8&layers=show%3A7&bboxSR=3857&imageSR=3857&size=256,256&f=image&time={TIME}&bbox={XMIN},{YMIN},{XMAX},{YMAX}"
tile = BBoxTileSource(url=url, extra_url_vars={"TIME": "1627412400000"})
p.add_tile(tile)
show(p)

@andhuang what have you attempted? There are lots of examples of CustomJS and even specifically sliders with CustomJS callbacks in the docs and examples repo. Have you tried to emulate any of those? If so please share the code and describe the problems you encountered.

Thanks for the suggestion.

I have tried

import requests
from bokeh.models import Select, CustomJS
from bokeh.models.tiles import BBoxTileSource
from bokeh.plotting import figure
from bokeh.tile_providers import get_provider, Vendors
from bokeh.io import notebook, show
from bokeh.layouts import Row

p = figure(x_range=(-2000000, 4000000), y_range=(-1000000, 7000000),
           x_axis_type="mercator", y_axis_type="mercator")
time_stops = requests.get("https://nowcoast.noaa.gov/layerinfo?request=timestops&service=analysis_meteohydro_sfc_rtma_time&layers=7&format=json").json()["layers"][0]["timeStops"]
url = "https://nowcoast.noaa.gov/arcgis/rest/services/nowcoast/analysis_meteohydro_sfc_rtma_time/MapServer/export?transparent=true&format=png8&layers=show%3A7&bboxSR=3857&imageSR=3857&size=256,256&f=image&time={TIME}&bbox={XMIN},{YMIN},{XMAX},{YMAX}"
tile = BBoxTileSource(url=url, extra_url_vars={"TIME": str(time_stops[0])})
select = Select(options=list(map(str, time_stops)))
callback = CustomJS(args=dict(source=tile), code="""
    var time = cb_obj.value
    console.log(source.extra_url_vars)
    source.extra_url_vars = {"TIME": time}
    source.change.emit();
""")
select.js_on_change('value', callback)
p.add_tile(tile)
layout = Row(select, p)
show(layout)

Is there a way to prevent it from flickering / flashing white between changes, e.g. finish loading the data then display rather than clearing the canvas, wait for data to load, then display? Ideally, it would look something like Forecast Models - Tropical Tidbits (press left right arrow keys). Or how do I make {TIME} act like {XMIN},{YMIN}, etc (show a pixelated version until data arrives)

@andhuang that looks roughly correct (tho naming the tile renderer “source” is a little confusing). Is there documentation on the expected URL format? When I try your code and look in the browser network pane, I see requests like

https://nowcoast.noaa.gov/arcgis/rest/services/nowcoast/analysis_meteohydro_sfc_rtma_time/MapServer/export?transparent=true&format=png8&layers=show%3A7&bboxSR=3857&imageSR=3857&size=256,256&f=image&time=1627473600000&bbox=5635549.224198718,3757032.817062229,6261721.359910883,4383204.952774391

which just show blank white images when I load them. Is this actually the right format for the URLs? (i.e. are those the correct units for the timestamp?)

Is there a way to prevent it from flickering / flashing white between changes, e.g. finish loading the data then display rather than clearing the canvas, wait for data to load, then display?

The tile renderer is currently fairly simplistic. If the tile server responses are slow, then AFAIK there are not currently any mitigations on the Bokeh side, e.g. progressive refinement. There is a large grant proposal out regarding making GIS related improvements to Bokeh, that we will possibly hear good news about soon.

Yes that is the correct URL; forgot to mention if you drag and view to the left, you should see some imagery

Thanks! I look forward to the good news, but if there’s some way I can help with that, please let me know!

@andhuang I see now. I don’t think there is anything to be done about the flicker when changing time stamps. IIRC, for the geo coords, the tile renderer does keep an internal cache of tiles surrounding the current view (both in space an resolution) which helps keep transitions smooth. But the caching does not know anything about the extra URL params. Changing them requires loading in an entirely new set of tiles at once, hence the “flicker”. Caching for the geo part of tiles seems reasonable because assumptions can be made about how transitions will occur. I don’t think it would be possible to extend caching to arbitrary user-supplied parameters that can jump around randomly (i.e. you would have to “cache” sets of every possibility, all the time)

if there’s some way I can help with that, please let me know!

We are still just waiting to hear the results of the application, hopefully in the next month or so. If the proposal is funded I am sure it would be useful to have users participate in discussions and trying out changes.

"Changing them requires loading in an entirely new set of tiles at once, hence the “flicker”.

Right, I was hoping that going down to the bokeh level and using extra_url_vars would work similarly as {XMIN},{XMAX}, and prevent the flicker since panel + geoviews didn’t have that implemented Expose the `"extra_url_vars"` param from bokeh · Issue #509 · holoviz/geoviews · GitHub and How to prevent flicker on geoviews WMTS? or block events until WMTS has been loaded? · Issue #510 · holoviz/geoviews · GitHub

It’d be nice to have a temporal dimension, e.g. {TIME} cache supported someday!

I have to be honest and say this seems unlikely. It’s not a standard tile service input, and there is no way to assume up front what the acceptable timesteps and values are for a specific given service. Even more difficult, while the standard patterns of panning and zooming mean that the tile source can assume what “nearby” tiles are needed to cache, it seems like timestamps could jump rather arbitrarily since they might come from a slider, but just as easily could come from a dropdown or a buttons that just correspond to any set of arbitrary timestamps at all. The page would have to cache everything which is not really a cache, it’s just duplicating all the data in the browser. In general that would use a prohibitive amount of memory.

Do you know if there’s a way to build a custom tile source cache for personal use? Or would that require re-implementing the entire model from scratch

I think you could probably use the existing implementation as a basis, or possibly even subclass from it to specialize it. I do think level of effort would be required, though, in order to bake-in the appropriate assumptions (that are specific and unique to our use-case) about how caching along the time dimension should be done. You can find information about creating Bokeh custom extensions here: