I wrote a small application that renders beautifully in Juypter and shows the dropdown menu with the additional but unused dimension "date".
But when I deploy it to a bokeh app, the widget is not shown.
# Create the main plot
def create_map_figure(data):
import geoviews as gv
import holoviews as hv
import geoviews.tile_sources as gts
from holoviews import dim, opts
from colorcet import rainbow
gv.extension("bokeh")
plot_width = 800
plot_height = 400
tile_opts = dict(height=plot_height, xaxis=None, yaxis=None, show_grid=False)
map_tiles = gts.CartoLight.opts(style=dict(alpha=1), plot=tile_opts)
points_ds = gv.Dataset(data[["lon", "lat", "val", "date"]])
points = (points_ds.to(gv.Points, kdims=["lon", "lat"], vdims=["val"])
.options(active_tools=['wheel_zoom'], tools=["hover"])
.opts(colorbar=True, cmap=rainbow)).opts(opts.Points(color=dim('val'), size=5))
return map_tiles * points.redim.range(change=(0, 10))
def modify_doc(doc):
import holoviews as hv
from bokeh.layouts import column, row
from bokeh.models import Div
conn = get_db_connection()
df = get_data(conn)
plot_map = create_map_figure(df)
renderer = hv.renderer('bokeh').instance(mode='server')
plot_map_rendered = renderer.app(plot_map, doc)
doc.add_root(plot_map_rendered.state)
bokeh_app = Application(FunctionHandler(modify_doc))
server = Server(
{'/': bokeh_app},
io_loop=io_loop,
allow_websocket_origin=ALLOW_WEBSOCKET_ORIGIN,
debug=True,
**{'port': PORT, 'address': HOST}
)
server.start()
if __name__ == '__main__':
io_loop.add_callback(server.show, "/")
io_loop.start()
I'm sorry, after reading http://holoviews.org/user_guide/Plots_and_Renderers.html I realised I have to extract the widget myself.. I tried static_html with no effort. Still got no idea how to do it.
At this point I'd strongly recommend using Panel to deploy the HoloViews app. It also has a more robust implementation of the HoloViews widgets and provides a lot more flexibility in laying things out or even replacing widgets.
No other chance to get this working as this is a simple app? Is it easy to implement panel in this "bokeh server started using the library in tornado" setup?
Yes, pn.panel(hv_obj)._get_server(...). I think I'll make that method public.
Do you have a minimal example in that bokeh server setup?
plot_map = create_map_figure(df)
plot_map_rendered = pn.panel(plot_map)._get_server()
doc.add_root(plot_map_rendered)
Here you go:
import geoviews as gv
import holoviews as hv
import numpy as np
import geoviews.tile_sources as gts
from holoviews import dim, opts
from colorcet import rainbow
gv.extension("bokeh")
plot_width = 800
plot_height = 400
tile_opts = dict(height=plot_height, xaxis=None, yaxis=None, show_grid=False)
map_tiles = gts.CartoLight.opts(style=dict(alpha=1), plot=tile_opts)
point_ds = gv.Dataset(
(np.random.rand(100), np.random.rand(100), np.random.randint(0, 3, 100), np.random.randn(100)),
['lon', 'lat', 'group'], 'val')
points = point_ds.to(gv.Points, ['lon', 'lat']).opts(
active_tools=['wheel_zoom'], tools=["hover"],
colorbar=True, cmap=rainbow, color=dim('val'), size=5)
plot = map_tiles * points.redim.range(change=(0, 10))
server = pn.panel(plot)._get_server(
show=True, port=PORT, websocket_origin=ALLOW_WEBSOCKET_ORIGIN,
address=HOST, debug=True)
server.start()
How can I embed a panel app using params API in that surrounding above?
Not sure I follow, could you expand on this?
Many thanks! I definately have to check this. In the meantime I found that I can serve a panel (took the MPGExplorer() example as it is) row using:
def modify_doc(doc):
row = pn.Row(explorer.param, explorer.plot)
doc.add_root(plot_map_rendered)
complementary to the rest of my app above. Worked beautifully locally, now I try to deploy to Cloud Foundry.
I did this to make your example work, but using python app.py it loads endlessly in the browser and shows nothing.
import os
from tornado.ioloop import IOLoop
# if running locally, listen on port 5000
PORT = int(os.getenv('PORT', '5000'))
HOST = "0.0.0.0"
print("HOST", HOST)
print("PORT", PORT)
# this is set in the manifest
try:
ALLOW_WEBSOCKET_ORIGIN = os.getenv("ALLOW_WEBSOCKET_ORIGIN").split(',')
except:
ALLOW_WEBSOCKET_ORIGIN = ['localhost:{0}'.format(PORT)]
print("ALLOW_WEBSOCKET_ORIGIN:", ALLOW_WEBSOCKET_ORIGIN)
io_loop = IOLoop.current()
import geoviews as gv
import numpy as np
import geoviews.tile_sources as gts
from holoviews import dim, opts
from colorcet import rainbow
import panel as pn
gv.extension("bokeh")
plot_width = 800
plot_height = 400
tile_opts = dict(height=plot_height, xaxis=None, yaxis=None, show_grid=False)
map_tiles = gts.CartoLight.opts(style=dict(alpha=1), plot=tile_opts)
point_ds = gv.Dataset(
(np.random.rand(100), np.random.rand(100), np.random.randint(0, 3, 100), np.random.randn(100)),
['lon', 'lat', 'group'], 'val')
points = point_ds.to(gv.Points, ['lon', 'lat']).opts(
active_tools=['wheel_zoom'], tools=["hover"],
colorbar=True, cmap=rainbow, color=dim('val'), size=5)
plot = map_tiles * points.redim.range(change=(0, 10))
server = pn.panel(plot)._get_server(
show=True, port=PORT, websocket_origin=ALLOW_WEBSOCKET_ORIGIN,
address=HOST, debug=True)
server.start()
if __name__ == '__main__':
io_loop.add_callback(server.show, "/")
io_loop.start()
Ah, yes I tested in a notebook where the IOLoop is already running. I'd probably just change it to:
if __name__ == '__main__':
pn.panel(plot)._get_server(
show=True, port=PORT, websocket_origin=ALLOW_WEBSOCKET_ORIGIN,
address=HOST, debug=True, start=True)
You made someone happy, works like a charm.
import os
from tornado.ioloop import IOLoop
# if running locally, listen on port 5000
PORT = int(os.getenv('PORT', '5000'))
HOST = "0.0.0.0"
print("HOST", HOST)
print("PORT", PORT)
# this is set in the manifest
try:
ALLOW_WEBSOCKET_ORIGIN = os.getenv("ALLOW_WEBSOCKET_ORIGIN").split(',')
except:
ALLOW_WEBSOCKET_ORIGIN = ['localhost:{0}'.format(PORT)]
print("ALLOW_WEBSOCKET_ORIGIN:", ALLOW_WEBSOCKET_ORIGIN)
io_loop = IOLoop.current()
import geoviews as gv
import numpy as np
import geoviews.tile_sources as gts
from holoviews import dim, opts
from colorcet import rainbow
import panel as pn
gv.extension("bokeh")
plot_width = 800
plot_height = 400
tile_opts = dict(height=plot_height, xaxis=None, yaxis=None, show_grid=False)
map_tiles = gts.CartoLight.opts(style=dict(alpha=1), plot=tile_opts)
point_ds = gv.Dataset(
(np.random.rand(100), np.random.rand(100), np.random.randint(0, 3, 100), np.random.randn(100)),
['lon', 'lat', 'group'], 'val')
points = point_ds.to(gv.Points, ['lon', 'lat']).opts(
active_tools=['wheel_zoom'], tools=["hover"],
colorbar=True, cmap=rainbow, color=dim('val'), size=5)
plot = map_tiles * points.redim.range(change=(0, 10))
if __name__ == '__main__':
pn.panel(plot)._get_server(
show=True, port=PORT, websocket_origin=ALLOW_WEBSOCKET_ORIGIN,
address=HOST, debug=True, start=True)
The way you showed me to start the application starts only one server. So if another user connects to the server, they see the same applications and the user 2 sees the changes user 1 makes in the widgets.
Wasn't that true for your previous approach as well? If that's not the case we should open a panel issue.
The way servers are usually launched is by using:
pn.panel(plot).servable()
and then launching the server with panel serve script.py.
Oh, I realize what the issue is. Let me think about it.
Do you have an idea here or did you find a solution?
Closing in favor of https://github.com/pyviz/panel/issues/644. This is something we'll have to solve at the Panel level.
Most helpful comment
Here you go:
Not sure I follow, could you expand on this?