def load_data(path, itime, ipressure): print("load_data", path, ipressure, itime) step = 100 with netCDF4.Dataset(path) as dataset: var = dataset.variables["x_wind"] if len(var.dimensions) == 4: values = var[itime, ipressure] else: values = var[itime] u = values[::step, ::step] var = dataset.variables["y_wind"] if len(var.dimensions) == 4: values = var[itime, ipressure] else: values = var[itime] v = values[::step, ::step] for d in var.dimensions: if "longitude" in d: lons = dataset.variables[d][::step] if "latitude" in d: lats = dataset.variables[d][::step] gx, _ = geo.web_mercator(lons, np.zeros(len(lons), dtype="d")) _, gy = geo.web_mercator(np.zeros(len(lats), dtype="d"), lats) x, y = np.meshgrid(gx, gy) u = convert_units(u, 'm s-1', 'knots') v = convert_units(v, 'm s-1', 'knots') return { "x": x.flatten(), "y": y.flatten(), "u": u.flatten(), "v": v.flatten() }
def web_mercator(lons, lats): """Similar to forest.geo.web_mercator but preserves array shape""" if (lons.ndim == 1): gx, _ = geo.web_mercator(lons, np.zeros(len(lons), dtype="d")) _, gy = geo.web_mercator(np.zeros(len(lats), dtype="d"), lats) return gx, gy elif (lons.ndim == 2) and (lats.ndim == 2): gx, gy = geo.web_mercator(lons, lats) gx = gx.reshape(lons.shape) gx = np.ma.masked_invalid(gx) gy = gy.reshape(lats.shape) gy = np.ma.masked_invalid(gy) return gx, gy else: raise Exception("Either 1D or 2D lons/lats")
def xs_ys(lines): xs, ys = [], [] for lons, lats in lines: x, y = geo.web_mercator(lons, lats) xs.append(x) ys.append(y) return {"xs": xs, "ys": ys}
def load(path): print(path) with open(path) as stream: rdt = json.load(stream) copy = dict(rdt) for i, feature in enumerate(rdt["features"]): coordinates = feature['geometry']['coordinates'][0] lons, lats = np.asarray(coordinates).T x, y = geo.web_mercator(lons, lats) c = np.array([x, y]).T.tolist() copy["features"][i]['geometry']['coordinates'][0] = c # Hack to use Categorical mapper for i, feature in enumerate(rdt["features"]): p = feature['properties']['PhaseLife'] copy["features"][i]['properties']['PhaseLife'] = str(p) return json.dumps(copy)
def __init__(self, loader): frame = loader.frame if frame is not None: x, y = geo.web_mercator( frame.longitude, frame.latitude) date = frame.date longitude = frame.longitude latitude = frame.latitude flash_type = frame.flash_type else: x, y = [], [] date = [] longitude = [] latitude = [] flash_type = [] self.source = bokeh.models.ColumnDataSource({ "x": x, "y": y, "date": date, "longitude": longitude, "latitude": latitude, "flash_type": flash_type, })
def main(): lon_range = (0, 30) lat_range = (0, 30) x_range, y_range = geo.web_mercator(lon_range, lat_range) figure = bokeh.plotting.figure(x_range=x_range, y_range=y_range, x_axis_type="mercator", y_axis_type="mercator", active_scroll="wheel_zoom") tile = bokeh.models.WMTSTileSource( url="https://maps.wikimedia.org/osm-intl/{Z}/{X}/{Y}.png", attribution="") figures = [figure] for _ in range(2): f = bokeh.plotting.figure(x_range=figure.x_range, y_range=figure.y_range, x_axis_type="mercator", y_axis_type="mercator", active_scroll="wheel_zoom") figures.append(f) for f in figures: f.axis.visible = False f.toolbar.logo = None f.toolbar_location = None f.min_border = 0 f.add_tile(tile) figure_row = bokeh.layouts.row(*figures, sizing_mode="stretch_both") figure_row.children = [figures[0]] # Trick to keep correct sizing modes figure_drop = bokeh.models.Dropdown(label="Figure", menu=[(str(i), str(i)) for i in [1, 2, 3]]) def on_click(value): if int(value) == 1: figure_row.children = [figures[0]] elif int(value) == 2: figure_row.children = [figures[0], figures[1]] elif int(value) == 3: figure_row.children = [figures[0], figures[1], figures[2]] figure_drop.on_click(on_click) color_mapper = bokeh.models.LinearColorMapper( low=0, high=1, palette=bokeh.palettes.Plasma[256]) for figure in figures: colorbar = bokeh.models.ColorBar(color_mapper=color_mapper, orientation="horizontal", background_fill_alpha=0., location="bottom_center", major_tick_line_color="black", bar_line_color="black") figure.add_layout(colorbar, 'center') artist = Artist(figures, color_mapper) renderers = [] for _, r in artist.renderers.items(): renderers += r image_sources = [] for name, viewer in artist.viewers.items(): if isinstance(viewer, (view.UMView, view.GPMView)): image_sources.append(viewer.source) image_loaders = [] for name, loader in data.LOADERS.items(): if isinstance(loader, (data.UMLoader, data.GPM)): image_loaders.append(loader) features = [] for figure in figures: features += [ add_feature(figure, data.COASTLINES), add_feature(figure, data.BORDERS) ] toggle = bokeh.models.CheckboxButtonGroup(labels=["Coastlines"], active=[0], width=135) def on_change(attr, old, new): if len(new) == 1: for feature in features: feature.visible = True else: for feature in features: feature.visible = False toggle.on_change("active", on_change) dropdown = bokeh.models.Dropdown(label="Color", menu=[("Black", "black"), ("White", "white")], width=50) dropdown.on_click(change_label(dropdown)) def on_click(value): for feature in features: feature.glyph.line_color = value dropdown.on_click(on_click) slider = bokeh.models.Slider(start=0, end=1, step=0.1, value=1.0, show_value=False) custom_js = bokeh.models.CustomJS(args=dict(renderers=renderers), code=""" renderers.forEach(function (r) { r.glyph.global_alpha = cb_obj.value }) """) slider.js_on_change("value", custom_js) palettes = { "Viridis": bokeh.palettes.Viridis[256], "Magma": bokeh.palettes.Magma[256], "Inferno": bokeh.palettes.Inferno[256], "Plasma": bokeh.palettes.Plasma[256] } palette_controls = PaletteControls(color_mapper, palettes) mapper_limits = MapperLimits(image_sources, color_mapper) menu = [(n, n) for n in data.FILE_DB.names] image_controls = images.Controls(menu) def on_click(value): if int(value) == 1: image_controls.labels = ["Show"] elif int(value) == 2: image_controls.labels = ["L", "R"] elif int(value) == 3: image_controls.labels = ["L", "C", "R"] figure_drop.on_click(on_click) variables = image_loaders[0].variables pressures = image_loaders[0].pressures pressure_variables = image_loaders[0].pressure_variables field_controls = FieldControls(variables, pressures, pressure_variables) image_controls.subscribe(artist.on_visible) field_controls.subscribe(artist.on_field) div = bokeh.models.Div(text="", width=10) border_row = bokeh.layouts.row(bokeh.layouts.column(toggle), bokeh.layouts.column(div), bokeh.layouts.column(dropdown)) time_controls = TimeControls() time_controls.subscribe(field_controls.on_time_control) tabs = bokeh.models.Tabs(tabs=[ bokeh.models.Panel(child=bokeh.layouts.column( time_controls.layout, bokeh.layouts.row(field_controls.drop), bokeh.layouts.row(field_controls.radio), image_controls.column), title="Data"), bokeh.models.Panel(child=bokeh.layouts.column( bokeh.layouts.row(figure_drop), border_row, bokeh.layouts.row(slider), bokeh.layouts.row(palette_controls.drop), bokeh.layouts.row(mapper_limits.low_input), bokeh.layouts.row(mapper_limits.high_input), bokeh.layouts.row(mapper_limits.checkbox), ), title="Settings") ]) # Series sub-figure widget series_figure = bokeh.plotting.figure(plot_width=400, plot_height=200, x_axis_type="datetime", toolbar_location=None, border_fill_alpha=0) series_figure.toolbar.logo = None series_row = bokeh.layouts.row(series_figure, name="series") def place_marker(figure, source): figure.circle(x="x", y="y", color="red", source=source) def cb(event): source.data = {"x": [event.x], "y": [event.y]} return cb marker_source = bokeh.models.ColumnDataSource({"x": [], "y": []}) series = Series(series_figure) field_controls.subscribe(series.on_field) for f in figures: f.on_event(bokeh.events.Tap, series.on_tap) f.on_event(bokeh.events.Tap, place_marker(f, marker_source)) document = bokeh.plotting.curdoc() document.add_root(bokeh.layouts.column(tabs, name="controls")) document.add_root(series_row) document.add_root(figure_row)
def main(): args = parse_args.parse_args() database = db.Database.connect(args.database) with open(args.config_file) as stream: config = parse_args.load_config(stream) # Access latest files data.FILE_DB.sync() # Full screen map lon_range = (0, 30) lat_range = (0, 30) x_range, y_range = geo.web_mercator( lon_range, lat_range) figure = bokeh.plotting.figure( x_range=x_range, y_range=y_range, x_axis_type="mercator", y_axis_type="mercator", active_scroll="wheel_zoom") tile = bokeh.models.WMTSTileSource( url="https://maps.wikimedia.org/osm-intl/{Z}/{X}/{Y}.png", attribution="" ) figures = [figure] for _ in range(2): f = bokeh.plotting.figure( x_range=figure.x_range, y_range=figure.y_range, x_axis_type="mercator", y_axis_type="mercator", active_scroll="wheel_zoom") figures.append(f) for f in figures: f.axis.visible = False f.toolbar.logo = None f.toolbar_location = None f.min_border = 0 f.add_tile(tile) figure_row = bokeh.layouts.row(*figures, sizing_mode="stretch_both") figure_row.children = [figures[0]] # Trick to keep correct sizing modes figure_drop = bokeh.models.Dropdown( label="Figure", menu=[(str(i), str(i)) for i in [1, 2, 3]]) def on_change(attr, old, new): if int(new) == 1: figure_row.children = [ figures[0]] elif int(new) == 2: figure_row.children = [ figures[0], figures[1]] elif int(new) == 3: figure_row.children = [ figures[0], figures[1], figures[2]] figure_drop.on_change("value", on_change) color_mapper = bokeh.models.LinearColorMapper( low=0, high=1, palette=bokeh.palettes.Plasma[256]) for figure in figures: colorbar = bokeh.models.ColorBar( color_mapper=color_mapper, orientation="horizontal", background_fill_alpha=0., location="bottom_center", major_tick_line_color="black", bar_line_color="black") figure.add_layout(colorbar, 'center') for name, pattern in config.patterns: if name not in data.LOADERS: locator = db.Locator( database.connection, directory=args.directory) loader = data.DBLoader(name, pattern, locator) data.add_loader(name, loader) renderers = {} viewers = {} for name, loader in data.LOADERS.items(): if isinstance(loader, rdt.Loader): viewer = rdt.View(loader) elif isinstance(loader, earth_networks.Loader): viewer = earth_networks.View(loader) elif isinstance(loader, data.GPM): viewer = view.GPMView(loader, color_mapper) elif isinstance(loader, satellite.EIDA50): viewer = view.EIDA50(loader, color_mapper) else: viewer = view.UMView(loader, color_mapper) viewers[name] = viewer renderers[name] = [ viewer.add_figure(f) for f in figures] artist = Artist(viewers, renderers) renderers = [] for _, r in artist.renderers.items(): renderers += r image_sources = [] for name, viewer in artist.viewers.items(): if isinstance(viewer, (view.UMView, view.GPMView, view.EIDA50)): image_sources.append(viewer.source) # image_loaders = [] # for name, loader in data.LOADERS.items(): # if isinstance(loader, (data.UMLoader, data.GPM)): # image_loaders.append(loader) # Lakes for figure in figures: add_feature(figure, data.LAKES, color="lightblue") features = [] for figure in figures: features += [ add_feature(figure, data.COASTLINES), add_feature(figure, data.BORDERS)] # Disputed borders for figure in figures: add_feature(figure, data.DISPUTED, color="red") toggle = bokeh.models.CheckboxButtonGroup( labels=["Coastlines"], active=[0], width=135) def on_change(attr, old, new): if len(new) == 1: for feature in features: feature.visible = True else: for feature in features: feature.visible = False toggle.on_change("active", on_change) dropdown = bokeh.models.Dropdown( label="Color", menu=[ ("Black", "black"), ("White", "white")], width=50) autolabel(dropdown) def on_change(attr, old, new): for feature in features: feature.glyph.line_color = new dropdown.on_change("value", on_change) slider = bokeh.models.Slider( start=0, end=1, step=0.1, value=1.0, show_value=False) custom_js = bokeh.models.CustomJS( args=dict(renderers=renderers), code=""" renderers.forEach(function (r) { r.glyph.global_alpha = cb_obj.value }) """) slider.js_on_change("value", custom_js) colors_controls = colors.Controls( color_mapper, "Plasma", 256) mapper_limits = MapperLimits(image_sources, color_mapper) menu = [(n, n) for n in data.FILE_DB.names] for k, _ in config.patterns: menu.append((k, k)) image_controls = images.Controls(menu) def on_change(attr, old, new): if int(new) == 1: image_controls.labels = ["Show"] elif int(new) == 2: image_controls.labels = ["L", "R"] elif int(new) == 3: image_controls.labels = ["L", "C", "R"] figure_drop.on_change("value", on_change) image_controls.subscribe(artist.on_visible) div = bokeh.models.Div(text="", width=10) border_row = bokeh.layouts.row( bokeh.layouts.column(toggle), bokeh.layouts.column(div), bokeh.layouts.column(dropdown)) # Add prototype database controls controls = db.Controls(database, patterns=config.patterns) locator = db.Locator( database.connection, directory=args.directory) text = db.View(text="", locator=locator) controls.subscribe(text.on_state) controls.subscribe(artist.on_state) tabs = bokeh.models.Tabs(tabs=[ bokeh.models.Panel( child=bokeh.layouts.column( bokeh.models.Div(text="Navigate:"), controls.layout, bokeh.models.Div(text="Compare:"), bokeh.layouts.row(figure_drop), image_controls.column, text.div), title="Control" ), bokeh.models.Panel( child=bokeh.layouts.column( border_row, bokeh.layouts.row(slider), colors_controls.layout, bokeh.layouts.row(mapper_limits.low_input), bokeh.layouts.row(mapper_limits.high_input), bokeh.layouts.row(mapper_limits.checkbox), ), title="Settings") ]) # Series sub-figure widget series_figure = bokeh.plotting.figure( plot_width=400, plot_height=200, x_axis_type="datetime", toolbar_location=None, border_fill_alpha=0) series_figure.toolbar.logo = None series_row = bokeh.layouts.row( series_figure, name="series") def place_marker(figure, source): figure.circle( x="x", y="y", color="red", source=source) def cb(event): source.data = { "x": [event.x], "y": [event.y]} return cb marker_source = bokeh.models.ColumnDataSource({ "x": [], "y": []}) series = Series(series_figure) controls.subscribe(series.on_state) for f in figures: f.on_event(bokeh.events.Tap, series.on_tap) f.on_event(bokeh.events.Tap, place_marker(f, marker_source)) # Minimise controls to ease navigation compact_button = bokeh.models.Button( label="Compact") compact_minus = bokeh.models.Button(label="-", width=50) compact_plus = bokeh.models.Button(label="+", width=50) compact_navigation = bokeh.layouts.column( compact_button, bokeh.layouts.row( compact_minus, compact_plus, width=100)) control_root = bokeh.layouts.column( compact_button, tabs, name="controls") display = "large" def on_compact(): nonlocal display if display == "large": control_root.height = 100 control_root.width = 120 compact_button.width = 100 compact_button.label = "Expand" control_root.children = [ compact_navigation] display = "compact" else: control_root.height = 500 control_root.width = 300 compact_button.width = 300 compact_button.label = "Compact" control_root.children = [compact_button, tabs] display = "large" compact_button.on_click(on_compact) document = bokeh.plotting.curdoc() document.title = "FOREST" document.add_root(control_root) document.add_root(series_row) document.add_root(figure_row)