From c401f7fd325327bfb5fa02e37044197d71ade35a Mon Sep 17 00:00:00 2001 From: Martijn Visser Date: Wed, 13 Nov 2024 11:51:57 +0100 Subject: [PATCH] Remove app and web_app folders --- app/__init__.py | 0 app/main.py | 240 ------------------------ app/static/favicon.ico | Bin 4286 -> 0 bytes app/templates/css/base.css | 97 ---------- app/templates/index.html | 5 - app/templates/js/dragbar.js | 58 ------ app/templates/js/mapopt.js | 13 -- app/widgets/__init__.py | 0 app/widgets/date_time_slider.py | 86 --------- app/widgets/map_figure.py | 133 ------------- pixi.toml | 2 - web_app/main.py | 23 --- web_app/old/__init__.py | 0 web_app/old/main.py | 240 ------------------------ web_app/old/static/favicon.ico | Bin 4286 -> 0 bytes web_app/old/templates/css/base.css | 97 ---------- web_app/old/templates/index.html | 5 - web_app/old/templates/js/dragbar.js | 58 ------ web_app/old/templates/js/mapopt.js | 13 -- web_app/old/widgets/__init__.py | 0 web_app/old/widgets/date_time_slider.py | 86 --------- web_app/old/widgets/map_figure.py | 133 ------------- 22 files changed, 1289 deletions(-) delete mode 100644 app/__init__.py delete mode 100644 app/main.py delete mode 100644 app/static/favicon.ico delete mode 100644 app/templates/css/base.css delete mode 100644 app/templates/index.html delete mode 100644 app/templates/js/dragbar.js delete mode 100644 app/templates/js/mapopt.js delete mode 100644 app/widgets/__init__.py delete mode 100644 app/widgets/date_time_slider.py delete mode 100644 app/widgets/map_figure.py delete mode 100644 web_app/main.py delete mode 100644 web_app/old/__init__.py delete mode 100644 web_app/old/main.py delete mode 100644 web_app/old/static/favicon.ico delete mode 100644 web_app/old/templates/css/base.css delete mode 100644 web_app/old/templates/index.html delete mode 100644 web_app/old/templates/js/dragbar.js delete mode 100644 web_app/old/templates/js/mapopt.js delete mode 100644 web_app/old/widgets/__init__.py delete mode 100644 web_app/old/widgets/date_time_slider.py delete mode 100644 web_app/old/widgets/map_figure.py diff --git a/app/__init__.py b/app/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/app/main.py b/app/main.py deleted file mode 100644 index 4d2c711..0000000 --- a/app/main.py +++ /dev/null @@ -1,240 +0,0 @@ -# %% -import math -import sys -from itertools import cycle -from pathlib import Path - -import pandas as pd -import pytz -import ribasim -from bokeh.io import curdoc -from bokeh.layouts import column, row -from bokeh.models import ( - CheckboxGroup, - ColorBar, - ColumnDataSource, - Div, - FixedTicker, - Legend, - LinearColorMapper, - RadioGroup, - RangeSlider, - Span, -) -from bokeh.palettes import Category20_20 -from bokeh.plotting import figure - -try: - from widgets.date_time_slider import DatetimeSlider, get_formatter - from widgets.map_figure import make_map -except ImportError: - sys.path.insert(0, Path(__file__).parent.as_posix()) - from widgets.date_time_slider import DatetimeSlider, get_formatter - from widgets.map_figure import make_map - -pd.options.mode.chained_assignment = None - -INIT_VARIABLE = "outflow_rate" - - -def get_colors(): - return cycle(Category20_20) - - -def get_columns(active_variables): - return basin_results_df.columns[active_variables].to_list() - - -def get_basin_variables(basin_results_df): - return [i for i in basin_results_df.columns if i not in ["node_id", "time"]] - - -def add_lines(attr, old, new): - # remove renderers and legend items - time_fig.renderers = [] - legend.items = [] - - # populate graph - colors = get_colors() - node_ids = locations_source.data["node_id"][locations_source.selected.indices] - min_y = [] - max_y = [] - for node_id in node_ids: - for active_variable in time_fig_variables.active: - variable_name = basin_variable_columns[active_variable] - df = basin_results_df[basin_results_df.node_id == node_id][["time", variable_name]] - max_y += [df[variable_name].max()] - min_y += [df[variable_name].min()] - source = ColumnDataSource(df) - time_fig.line( - x="time", - y=variable_name, - source=source, - color=next(colors), - legend_label=f"{node_id}_{variable_name}", - ) - - if min_y and max_y: - max_y = max(max_y) - min_y = min(min_y) - if (max_y - min_y) > 1: - time_fig.y_range.start = min_y * 0.95 - time_fig.y_range.end = max_y * 1.05 - - -def move_time_line(attr, old, new): - now = date_time_slider.value_as_datetime - time_line.location = now - - -def update_map_values(attr, old, new): - # get selected variable - variable = basin_variable_columns[map_fig_variable.active] - - # slice results - df_results_select = basin_results_df[ - basin_results_df["time"] == date_time_slider.value_as_datetime.date().isoformat() - ].set_index("node_id")[variable] - - # update locations source - locations_source.data["value"] = df_select.node_id.apply(lambda x: abs(df_results_select.at[x])).to_list() - - # update map_fig_variable_range - map_fig_variable_range.start = 0 - map_fig_variable_range.end = 100 - map_fig_variable_range.title = f"{variable} range" - - # update color bar title - color_bar.title = variable - - -def update_cm(attr, old, new): - low, high = map_fig_variable_range.value - cm.low = low - cm.high = high - nb_ticks = len(color_bar.ticker.ticks) - interval = (high - low) / (nb_ticks - 1) - color_bar.ticker.ticks = [low + (i * interval) for i in range(nb_ticks)] - - -# read model -toml_file = next((i for i in sys.argv if i.lower().endswith(".toml")), None) -if toml_file is None: - toml_file = r"d:\projecten\D2306.LHM_RIBASIM\02.brongegevens\Rijkswaterstaat\modellen\hws_2024_4_1\hws.toml" -model = ribasim.Model.read(toml_file) -results_dir = model.filepath.parent / model.results_dir - -# basin results -basin_results_df = pd.read_feather(results_dir / "basin.arrow") -basin_results_df.reset_index(inplace=True) -basin_variable_columns = [i for i in basin_results_df.columns if i not in ["time", "node_id"]] -if INIT_VARIABLE in basin_variable_columns: - actives = [basin_variable_columns.index(INIT_VARIABLE)] -else: - actives = [0] - -# prepare nodes -# nodes_df = model.network.node.df -# support older versions -if hasattr(model, "network"): - nodes_df = model.network.node.df -else: - nodes_df = model.node_table().df -nodes_df["x"] = nodes_df.geometry.x -nodes_df["y"] = nodes_df.geometry.y - -# map fig -map_fig = make_map(bounds=nodes_df.total_bounds) -df_select = nodes_df[nodes_df["node_type"] == "Basin"][["x", "y", "name", "node_id"]] - -locations_source = ColumnDataSource(df_select) - -palette = ["green", "yellow", "orange", "red"] -cm = LinearColorMapper(palette=palette, low=0, high=100, nan_color="grey") - -locations_source.selected.on_change("indices", add_lines) - -map_fig.scatter( - x="x", - y="y", - size=8, - source=locations_source, - line_color=None, - fill_color={"field": "value", "transform": cm}, - selection_fill_alpha=1, - selection_line_color="yellow", - selection_line_width=2, - nonselection_fill_alpha=0.6, - nonselection_line_alpha=0.5, -) - -color_bar = ColorBar( - color_mapper=cm, - location=(0, 0), - ticker=FixedTicker(ticks=[0, 25, 50, 75, 100]), - title=INIT_VARIABLE, -) - -map_fig.add_layout(color_bar, "left") - - -# time_fig -time_fig = figure( - toolbar_location="above", - name="time figure", - x_range=(model.starttime, model.endtime), -) -legend = Legend(items=[]) -time_fig.add_layout(legend) -time_fig.toolbar.logo = None -time_fig.xaxis.formatter = get_formatter() -time_fig.xaxis.major_label_orientation = math.pi / 4 - -time_line = Span(location=model.starttime, dimension="height", line_color="red", line_width=3) - -time_fig.add_layout(time_line) - - -# Controls - -# controls: dateslider -date_time_slider = DatetimeSlider( - start=pytz.utc.localize(model.starttime), - end=pytz.utc.localize(model.endtime), - value=pytz.utc.localize(model.starttime), - step=model.solver.saveat * 1000, - # format="%Y-%m-%d %H:%M:%S %Z", -) -date_time_slider.widget.js_link("value", time_line, "location") -# date_time_slider.widget.on_change("value", move_time_line) -date_time_slider.widget.on_change("value_throttled", update_map_values) - -# controls: map fig variables -map_fig_variable = RadioGroup(labels=basin_variable_columns, active=actives[0]) -map_fig_variable.on_change("active", update_map_values) - -# controls: time fig variables -time_fig_variables = CheckboxGroup(labels=basin_variable_columns, active=actives) -time_fig_variables.on_change("active", add_lines) - -# controls: map values -map_fig_variable_range = RangeSlider(start=0, end=100, value=(cm.low, cm.high)) -map_fig_variable_range.on_change("value", update_cm) - -# layouts -variables_layout = column( - Div(text="Variabelen:"), - row( - column(Div(text="kaart:"), map_fig_variable), - column(Div(text="grafiek:"), time_fig_variables), - ), -) -control_layout = column(variables_layout, map_fig_variable_range, date_time_slider.widget) - -layout = row(map_fig, time_fig, control_layout) - -curdoc().add_root(layout) -curdoc().title = f"ribasim: {model.filepath.stem}" - -# init app -update_map_values(None, None, None) diff --git a/app/static/favicon.ico b/app/static/favicon.ico deleted file mode 100644 index 7c9f768f65e07a677db99ab563d65639e61e874a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4286 zcmc&&>04A~6rcWqo<8-tkCkb=r7c?dl)Jf+=5CgzWx2LITG`@Knx?t2$j$(=DL$ft z3yaF?-~fss%P<223W^9K2>d#~Gv50UFr$8{m*>5A?!E7O_TM?@#bS9({+@cu!mB0v z35(@Pi^cM^OnGG)DU%A!*#NLcYPqCXucbNnMu>!NY6elix;MV|Q}8Zk>IAuD7jTs4k-m$L@R>pzG8 zm}e0%e()nt|8dVDa?U#_h}eyyq%g#PF%^90la_oBpEVMtxmn%L>MA6zo{Io^7dYYh zN16fSJo4HTLtzY;?Hoe=h9PkL5bw0)Tj)n`pd=#|%70CN{-o_cc981PPcGRXATw5c z6|-mzDl$?G_PF6FN5t%r`n}&iLqCImW|~+2aZ4v7b>j*|e=4mAAmpgPW*ZXnro|po*vX+vLSxi6xb6& zQF=TcN7sFdlH@3KcXgRMQ-<8&?I?^5gd^t+s?VQ9HTWK|v z+EEa>NA)GY?yh^FOhrlI$lSF7MTucL3l9d0-lss`-S;|?9kdm(izgsZ{6?DplEVjo zW8L93bChpR&^BBzFHyP$k-Kqx!xw5_+T+-d_i-!q0~i+))9&H|bhO>pduwAoip5uP zOFvXR5MwIKgZPWgxgk4n%Uy$e?RV4;SJRK;z~ooNrh^n;_V{2p3$hS4<4qH5L6crY zb@plXO-){g?&HOopM~&`-$v4knTT69S#6^D8zBCuKlyNvF{o?u+IePkuP3)04Xd(|*GR4@I!YhiJSS`zy0f;9^`5I>jz+P1kU8^J;`hjwtcMpL$fCP1Wrk zt+xeho7HZ_9_<%2@kN#ALeyR`_b9z-ui>ENRmKZ*V9NT%=xlFO8W*hl)Fy^J6&c46 zHuEj7{K=y*HULdkWhg!ziF5llAwS}8oZY)o^1*1m-)(6&$Z5P?NlnC2;d%OxZ`B{P zA>VN@FVhAbjN!bnoudEn0r}ImQS(MA-xjx1{eP#qNpYRE`xhMl`Af9jxURH%Ur0AQ zaI1-V>JYzdDq0&GKv`{~4|7ENjvv%d^i@Cm3vb3rO+HgW--#_{pm9by^iEkO()V-;)RQGhWwrDP7 zd@(NR|8x5`_GN266aR{L+EDTz?M%KLwB?~yvj@hX)Ia9;w2doai}pA3Ic=K0eZ69v z{E2bqX7Bd@L-StA5i4#wE78?)7j0sH%9SMjg;^6tzTWyLtelNwKP=I_L0P#sJK%TC zbB$Lo!Mfmmt%bxdW4(91<$8{W%Y}-?)7!oiUk7UbVBRK;D2W96V z=3_q_@3B2=a9{qb=HQ^@>W0e~g?B3|(oGDE%qS;lAM#00ax*FA= zcHA#Kh!tWjdf{l)+4HnUrEOY?6p16s%iMQSYD9kc-zrb=)S+JE&)~22qkkCdltubc z#vWtp&do+O2Wt*%6LSggX`5E*_uCD1k_V+W->+BHnR$;G;N)79ejoo2#NSv;%*RMy zO&kq0db+!Cy{s4p=8QGPhah div { - background-color: rgba(255, 255, 255, 0.8); - text-align: center; - /* padding: 20px 0; */ - font-size: 30px; - /* height:800px; */ -} - -/*****************************/ - -#header { - overflow: auto; - grid-area: header; -} - -#kaart { - position: relative; - overflow: auto; - grid-area: kaart; - cursor: zoom-in; - z-index:0; -} - -#kaartdragbar { - grid-area: kaartdragbar; - cursor: ew-resize; -} - -#grafiek { - grid-area: grafiek; - cursor: ew-resize; -} -#controls { - grid-area: controls; - cursor: ew-resize; -} diff --git a/app/templates/index.html b/app/templates/index.html deleted file mode 100644 index d4a50c8..0000000 --- a/app/templates/index.html +++ /dev/null @@ -1,5 +0,0 @@ -{% extends base %} - -{% block preamble %} - -{% endblock %} diff --git a/app/templates/js/dragbar.js b/app/templates/js/dragbar.js deleted file mode 100644 index dd94c8f..0000000 --- a/app/templates/js/dragbar.js +++ /dev/null @@ -1,58 +0,0 @@ -// example from https://stackoverflow.com/questions/46931103/making-a-dragbar-to-resize-divs-inside-css-grids -let isLeftDragging = false; -let isRightDragging = false; - -function ResetColumnSizes() { - // when page resizes return to default col sizes - let page = document.getElementById("grid-container"); - page.style.gridTemplateColumns = "150px auto 8px 30%"; -} - -function SetCursor(cursor) { - let page = document.getElementById("grid-container"); - page.style.cursor = cursor; -} - -function StartKaartDrag() { - // console.log("mouse down"); - isLeftDragging = true; - - SetCursor("ew-resize"); -} - - -function EndDrag() { - // console.log("mouse up"); - isLeftDragging = false; - isRightDragging = false; - - SetCursor("auto"); -} - -function OnDrag(event) { - if (isLeftDragging || isRightDragging) { - // console.log("Dragging"); - //console.log(event); - - let page = document.getElementById("grid-container"); - let leftcol = document.getElementById("kaart"); - - let leftColWidth = isLeftDragging ? event.clientX : leftcol.clientWidth; - - let dragbarWidth = 8; - - let cols = [ - 150, - leftColWidth-150, - dragbarWidth, - page.clientWidth - dragbarWidth - leftColWidth, - ]; - - let newColDefn = cols.map(c => c.toString() + "px").join(" "); - - // console.log(newColDefn); - page.style.gridTemplateColumns = newColDefn; - - event.preventDefault() - } -} diff --git a/app/templates/js/mapopt.js b/app/templates/js/mapopt.js deleted file mode 100644 index 3588868..0000000 --- a/app/templates/js/mapopt.js +++ /dev/null @@ -1,13 +0,0 @@ -function popMapLegend() { - var map_opt = document.getElementById("map_opt"); - var kaart = document.getElementById("kaart"); - if(map_opt.style.opacity < "0.1"){ - map_opt.style.opacity = "1"; - } - else if(map_opt.style.display == "none" ) { - map_opt.style.display = "block"; - } - else { - map_opt.style.display = "none"; - } - } diff --git a/app/widgets/__init__.py b/app/widgets/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/app/widgets/date_time_slider.py b/app/widgets/date_time_slider.py deleted file mode 100644 index 5d735ff..0000000 --- a/app/widgets/date_time_slider.py +++ /dev/null @@ -1,86 +0,0 @@ -import math -import numbers -from dataclasses import dataclass, field -from datetime import datetime, timedelta -from typing import Literal - -from bokeh.models import DatetimeTickFormatter, Slider - - -def get_formatter(format="%Y-%m-%d %H:%M:%S"): - return DatetimeTickFormatter(years=format, days=format, hours=format, minutes=format) - - -def round_seconds(ts_seconds, td_seconds, method): - if method in ["floor", "ceil"]: - rounder = getattr(math, method) - else: - rounder = round - - return rounder(ts_seconds / td_seconds) * td_seconds - - -def round_datetime(dt, timedelta_round, method: Literal["floor", "ceil", "round"] = "round"): - ts_seconds = dt.timestamp() - td_seconds = timedelta_round.total_seconds() - rounded_seconds = round_seconds(ts_seconds, td_seconds, method) - - return datetime.fromtimestamp(rounded_seconds) - - -@dataclass -class DatetimeSlider: - start: datetime | int - end: datetime | int - value: datetime | int - step: timedelta | int - format: str = "%Y-%m-%d %H:%M:%S" - kwargs: dict = field(default_factory=dict) - widget: Slider | None = None - - def __post_init__(self): - # validate values - if self.start >= self.end: - raise ValueError(f"{self.start} >= {self.end}") - - if self.value < self.start: - raise ValueError(f"{self.value} < {self.start}") - - if self.value > self.end: - raise ValueError(f"{self.value} > {self.end}") - - if isinstance(self.start, datetime): - start = self.start.timestamp() * 1000 - else: - start = self.start - - if isinstance(self.end, datetime): - end = self.end.timestamp() * 1000 - else: - end = self.end - - if isinstance(self.value, datetime): - value = self.value.timestamp() * 1000 - else: - value = self.value - - if isinstance(self.step, timedelta): - step = self.step.total_seconds() * 1000 - else: - step = self.step - - self.widget = Slider( - start=round_seconds(start, step, "floor"), - end=round_seconds(end, step, "ceil"), - value=round_seconds(value, step, "round"), - step=step, - format=get_formatter(self.format), - **self.kwargs, - ) - - @property - def value_as_datetime(self): - if isinstance(self.widget.value, numbers.Number): - return datetime.fromtimestamp(self.widget.value / 1000) - else: - return self.widget.value diff --git a/app/widgets/map_figure.py b/app/widgets/map_figure.py deleted file mode 100644 index 46b57b8..0000000 --- a/app/widgets/map_figure.py +++ /dev/null @@ -1,133 +0,0 @@ -from typing import Literal - -import bokeh.models as bokeh_models -from bokeh.layouts import column -from bokeh.models import ( - HoverTool, - Range1d, - TapTool, -) -from bokeh.models.widgets import CheckboxGroup, Div, RadioGroup -from bokeh.plotting import figure - -BOKEH_BACKGROUNDS = { - "luchtfoto": { - "url": ( - "https://service.pdok.nl/hwh/luchtfotorgb/wms/v1_0?" - "service=WMS&version=1.3.0&request=GetMap&layers=Actueel_orthoHR" - "&width=265&height=265&styles=&crs=EPSG:28992&format=image/jpeg" - "&bbox={XMIN},{YMIN},{XMAX},{YMAX}" - ), - "class": "BBoxTileSource", - }, - "topografie": { - "url": ( - "https://services.arcgisonline.nl/arcgis/rest/services/Basiskaarten/Topo/" - "MapServer/export?" - "bbox={XMIN},{YMIN},{XMAX},{YMAX}" - "&layers=show" - "&size=385,385" - "&bboxSR=28892" - "&dpi=2500" - "&transparent=true" - "&format=png" - "&f=image" - ), - "class": "BBoxTileSource", - }, -} - -BOKEH_SETTINGS = { - "background": "topografie", - "save_tool": "save", - "active_scroll": "wheel_zoom", - "toolbar_location": "above", -} - - -def get_tilesource(layer, map_configs=BOKEH_BACKGROUNDS): - url = map_configs[layer]["url"] - if "args" in map_configs[layer]: - args = map_configs[layer]["args"] - else: - args = {} - return getattr(bokeh_models, map_configs[layer]["class"])(url=url, **args) - - -def make_map( - bounds: list[float, float, float, float], - background: Literal["luchtfoto", "topografie"] = "topografie", -) -> figure: - # figure ranges - x_range = Range1d(start=bounds[0], end=bounds[2], min_interval=100) - y_range = Range1d(start=bounds[1], end=bounds[3], min_interval=100) - - # set tools - map_hover = HoverTool( - tooltips=[ - ("name", "@name"), - ("node id", "@node_id"), - ("diepte 1", "@value"), - ("diepte 2", "@value"), - ] - ) - - map_hover.visible = False - - tools = [ - "tap", - "wheel_zoom", - "pan", - "reset", - "box_select", - map_hover, - "save", - ] - - # initialize figure - map_fig = figure( - tools=tools, - active_scroll="wheel_zoom", - x_range=x_range, - y_range=y_range, - toolbar_location="above", - ) - - # misc settings - map_fig.axis.visible = False - map_fig.toolbar.logo = None - map_fig.toolbar.autohide = True - map_fig.xgrid.grid_line_color = None - map_fig.ygrid.grid_line_color = None - map_fig.select(type=TapTool) - - # add background - tile_source = get_tilesource(background) - map_fig.add_tile(tile_source, name="background") - - return map_fig - - -def make_options( - map_overlays: dict, - overlays_change, - background_title: str, - background_change, -): - # set overlay and handlers - overlay_options = list(map_overlays.keys()) - active_overlays = [idx for idx, (_, v) in enumerate(map_overlays.items()) if v["visible"]] - overlay_control = CheckboxGroup(labels=overlay_options, active=active_overlays) - overlay_control.on_change("active", overlays_change) - - # set background and handlers - background_options = list(BOKEH_BACKGROUNDS.keys()) - background_active = list(BOKEH_BACKGROUNDS.keys()).index(BOKEH_SETTINGS["background"]) - background_control = RadioGroup(labels=background_options, active=background_active) - background_control.on_change("active", background_change) - map_controls = column( - overlay_control, - Div(text=f"
{background_title}
"), - background_control, - ) - return map_controls diff --git a/pixi.toml b/pixi.toml index ee0682c..7bc700a 100644 --- a/pixi.toml +++ b/pixi.toml @@ -33,8 +33,6 @@ test-hydamo-cov = "pytest --numprocesses=auto --cov=hydamo --cov-report=xml --co test-ribasim_nl-cov = "pytest --numprocesses=auto --cov=ribasim_nl --cov-report=xml --cov-report=term-missing src/ribasim_nl/tests" test-peilbeheerst_model-cov = "pytest --numprocesses=auto --cov=peilbeheerst_model --cov-report=xml --cov-report=term-missing src/peilbeheerst_model/tests" tests = { depends_on = ["lint", "test-hydamo", "test-ribasim_nl"] } -# Web app -app = "bokeh serve web_app --show --dev --port 5007" [feature.common.dependencies] bokeh = ">=3.0" diff --git a/web_app/main.py b/web_app/main.py deleted file mode 100644 index 7763669..0000000 --- a/web_app/main.py +++ /dev/null @@ -1,23 +0,0 @@ -# %% -import sys - -import ribasim -from bokeh.io import curdoc - -from bokeh_helpers.widgets.map_figure_widget import MapFigure - -toml_file = next((i for i in sys.argv if i.lower().endswith(".toml")), None) -if toml_file is None: - toml_file = r"d:\projecten\D2306.LHM_RIBASIM\02.brongegevens\Rijkswaterstaat\modellen\hws_2024_4_4\hws.toml" - -model = ribasim.Model.read(toml_file) - -nodes_df = model.node_table().df - -map_figure = MapFigure(bounds=nodes_df.total_bounds) - -curdoc().add_root(map_figure.map_figure_widget) -curdoc().add_root(map_figure.background_control_widget) -curdoc().title = "Ribasim" - -# %% diff --git a/web_app/old/__init__.py b/web_app/old/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/web_app/old/main.py b/web_app/old/main.py deleted file mode 100644 index 4d2c711..0000000 --- a/web_app/old/main.py +++ /dev/null @@ -1,240 +0,0 @@ -# %% -import math -import sys -from itertools import cycle -from pathlib import Path - -import pandas as pd -import pytz -import ribasim -from bokeh.io import curdoc -from bokeh.layouts import column, row -from bokeh.models import ( - CheckboxGroup, - ColorBar, - ColumnDataSource, - Div, - FixedTicker, - Legend, - LinearColorMapper, - RadioGroup, - RangeSlider, - Span, -) -from bokeh.palettes import Category20_20 -from bokeh.plotting import figure - -try: - from widgets.date_time_slider import DatetimeSlider, get_formatter - from widgets.map_figure import make_map -except ImportError: - sys.path.insert(0, Path(__file__).parent.as_posix()) - from widgets.date_time_slider import DatetimeSlider, get_formatter - from widgets.map_figure import make_map - -pd.options.mode.chained_assignment = None - -INIT_VARIABLE = "outflow_rate" - - -def get_colors(): - return cycle(Category20_20) - - -def get_columns(active_variables): - return basin_results_df.columns[active_variables].to_list() - - -def get_basin_variables(basin_results_df): - return [i for i in basin_results_df.columns if i not in ["node_id", "time"]] - - -def add_lines(attr, old, new): - # remove renderers and legend items - time_fig.renderers = [] - legend.items = [] - - # populate graph - colors = get_colors() - node_ids = locations_source.data["node_id"][locations_source.selected.indices] - min_y = [] - max_y = [] - for node_id in node_ids: - for active_variable in time_fig_variables.active: - variable_name = basin_variable_columns[active_variable] - df = basin_results_df[basin_results_df.node_id == node_id][["time", variable_name]] - max_y += [df[variable_name].max()] - min_y += [df[variable_name].min()] - source = ColumnDataSource(df) - time_fig.line( - x="time", - y=variable_name, - source=source, - color=next(colors), - legend_label=f"{node_id}_{variable_name}", - ) - - if min_y and max_y: - max_y = max(max_y) - min_y = min(min_y) - if (max_y - min_y) > 1: - time_fig.y_range.start = min_y * 0.95 - time_fig.y_range.end = max_y * 1.05 - - -def move_time_line(attr, old, new): - now = date_time_slider.value_as_datetime - time_line.location = now - - -def update_map_values(attr, old, new): - # get selected variable - variable = basin_variable_columns[map_fig_variable.active] - - # slice results - df_results_select = basin_results_df[ - basin_results_df["time"] == date_time_slider.value_as_datetime.date().isoformat() - ].set_index("node_id")[variable] - - # update locations source - locations_source.data["value"] = df_select.node_id.apply(lambda x: abs(df_results_select.at[x])).to_list() - - # update map_fig_variable_range - map_fig_variable_range.start = 0 - map_fig_variable_range.end = 100 - map_fig_variable_range.title = f"{variable} range" - - # update color bar title - color_bar.title = variable - - -def update_cm(attr, old, new): - low, high = map_fig_variable_range.value - cm.low = low - cm.high = high - nb_ticks = len(color_bar.ticker.ticks) - interval = (high - low) / (nb_ticks - 1) - color_bar.ticker.ticks = [low + (i * interval) for i in range(nb_ticks)] - - -# read model -toml_file = next((i for i in sys.argv if i.lower().endswith(".toml")), None) -if toml_file is None: - toml_file = r"d:\projecten\D2306.LHM_RIBASIM\02.brongegevens\Rijkswaterstaat\modellen\hws_2024_4_1\hws.toml" -model = ribasim.Model.read(toml_file) -results_dir = model.filepath.parent / model.results_dir - -# basin results -basin_results_df = pd.read_feather(results_dir / "basin.arrow") -basin_results_df.reset_index(inplace=True) -basin_variable_columns = [i for i in basin_results_df.columns if i not in ["time", "node_id"]] -if INIT_VARIABLE in basin_variable_columns: - actives = [basin_variable_columns.index(INIT_VARIABLE)] -else: - actives = [0] - -# prepare nodes -# nodes_df = model.network.node.df -# support older versions -if hasattr(model, "network"): - nodes_df = model.network.node.df -else: - nodes_df = model.node_table().df -nodes_df["x"] = nodes_df.geometry.x -nodes_df["y"] = nodes_df.geometry.y - -# map fig -map_fig = make_map(bounds=nodes_df.total_bounds) -df_select = nodes_df[nodes_df["node_type"] == "Basin"][["x", "y", "name", "node_id"]] - -locations_source = ColumnDataSource(df_select) - -palette = ["green", "yellow", "orange", "red"] -cm = LinearColorMapper(palette=palette, low=0, high=100, nan_color="grey") - -locations_source.selected.on_change("indices", add_lines) - -map_fig.scatter( - x="x", - y="y", - size=8, - source=locations_source, - line_color=None, - fill_color={"field": "value", "transform": cm}, - selection_fill_alpha=1, - selection_line_color="yellow", - selection_line_width=2, - nonselection_fill_alpha=0.6, - nonselection_line_alpha=0.5, -) - -color_bar = ColorBar( - color_mapper=cm, - location=(0, 0), - ticker=FixedTicker(ticks=[0, 25, 50, 75, 100]), - title=INIT_VARIABLE, -) - -map_fig.add_layout(color_bar, "left") - - -# time_fig -time_fig = figure( - toolbar_location="above", - name="time figure", - x_range=(model.starttime, model.endtime), -) -legend = Legend(items=[]) -time_fig.add_layout(legend) -time_fig.toolbar.logo = None -time_fig.xaxis.formatter = get_formatter() -time_fig.xaxis.major_label_orientation = math.pi / 4 - -time_line = Span(location=model.starttime, dimension="height", line_color="red", line_width=3) - -time_fig.add_layout(time_line) - - -# Controls - -# controls: dateslider -date_time_slider = DatetimeSlider( - start=pytz.utc.localize(model.starttime), - end=pytz.utc.localize(model.endtime), - value=pytz.utc.localize(model.starttime), - step=model.solver.saveat * 1000, - # format="%Y-%m-%d %H:%M:%S %Z", -) -date_time_slider.widget.js_link("value", time_line, "location") -# date_time_slider.widget.on_change("value", move_time_line) -date_time_slider.widget.on_change("value_throttled", update_map_values) - -# controls: map fig variables -map_fig_variable = RadioGroup(labels=basin_variable_columns, active=actives[0]) -map_fig_variable.on_change("active", update_map_values) - -# controls: time fig variables -time_fig_variables = CheckboxGroup(labels=basin_variable_columns, active=actives) -time_fig_variables.on_change("active", add_lines) - -# controls: map values -map_fig_variable_range = RangeSlider(start=0, end=100, value=(cm.low, cm.high)) -map_fig_variable_range.on_change("value", update_cm) - -# layouts -variables_layout = column( - Div(text="Variabelen:"), - row( - column(Div(text="kaart:"), map_fig_variable), - column(Div(text="grafiek:"), time_fig_variables), - ), -) -control_layout = column(variables_layout, map_fig_variable_range, date_time_slider.widget) - -layout = row(map_fig, time_fig, control_layout) - -curdoc().add_root(layout) -curdoc().title = f"ribasim: {model.filepath.stem}" - -# init app -update_map_values(None, None, None) diff --git a/web_app/old/static/favicon.ico b/web_app/old/static/favicon.ico deleted file mode 100644 index 7c9f768f65e07a677db99ab563d65639e61e874a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4286 zcmc&&>04A~6rcWqo<8-tkCkb=r7c?dl)Jf+=5CgzWx2LITG`@Knx?t2$j$(=DL$ft z3yaF?-~fss%P<223W^9K2>d#~Gv50UFr$8{m*>5A?!E7O_TM?@#bS9({+@cu!mB0v z35(@Pi^cM^OnGG)DU%A!*#NLcYPqCXucbNnMu>!NY6elix;MV|Q}8Zk>IAuD7jTs4k-m$L@R>pzG8 zm}e0%e()nt|8dVDa?U#_h}eyyq%g#PF%^90la_oBpEVMtxmn%L>MA6zo{Io^7dYYh zN16fSJo4HTLtzY;?Hoe=h9PkL5bw0)Tj)n`pd=#|%70CN{-o_cc981PPcGRXATw5c z6|-mzDl$?G_PF6FN5t%r`n}&iLqCImW|~+2aZ4v7b>j*|e=4mAAmpgPW*ZXnro|po*vX+vLSxi6xb6& zQF=TcN7sFdlH@3KcXgRMQ-<8&?I?^5gd^t+s?VQ9HTWK|v z+EEa>NA)GY?yh^FOhrlI$lSF7MTucL3l9d0-lss`-S;|?9kdm(izgsZ{6?DplEVjo zW8L93bChpR&^BBzFHyP$k-Kqx!xw5_+T+-d_i-!q0~i+))9&H|bhO>pduwAoip5uP zOFvXR5MwIKgZPWgxgk4n%Uy$e?RV4;SJRK;z~ooNrh^n;_V{2p3$hS4<4qH5L6crY zb@plXO-){g?&HOopM~&`-$v4knTT69S#6^D8zBCuKlyNvF{o?u+IePkuP3)04Xd(|*GR4@I!YhiJSS`zy0f;9^`5I>jz+P1kU8^J;`hjwtcMpL$fCP1Wrk zt+xeho7HZ_9_<%2@kN#ALeyR`_b9z-ui>ENRmKZ*V9NT%=xlFO8W*hl)Fy^J6&c46 zHuEj7{K=y*HULdkWhg!ziF5llAwS}8oZY)o^1*1m-)(6&$Z5P?NlnC2;d%OxZ`B{P zA>VN@FVhAbjN!bnoudEn0r}ImQS(MA-xjx1{eP#qNpYRE`xhMl`Af9jxURH%Ur0AQ zaI1-V>JYzdDq0&GKv`{~4|7ENjvv%d^i@Cm3vb3rO+HgW--#_{pm9by^iEkO()V-;)RQGhWwrDP7 zd@(NR|8x5`_GN266aR{L+EDTz?M%KLwB?~yvj@hX)Ia9;w2doai}pA3Ic=K0eZ69v z{E2bqX7Bd@L-StA5i4#wE78?)7j0sH%9SMjg;^6tzTWyLtelNwKP=I_L0P#sJK%TC zbB$Lo!Mfmmt%bxdW4(91<$8{W%Y}-?)7!oiUk7UbVBRK;D2W96V z=3_q_@3B2=a9{qb=HQ^@>W0e~g?B3|(oGDE%qS;lAM#00ax*FA= zcHA#Kh!tWjdf{l)+4HnUrEOY?6p16s%iMQSYD9kc-zrb=)S+JE&)~22qkkCdltubc z#vWtp&do+O2Wt*%6LSggX`5E*_uCD1k_V+W->+BHnR$;G;N)79ejoo2#NSv;%*RMy zO&kq0db+!Cy{s4p=8QGPhah div { - background-color: rgba(255, 255, 255, 0.8); - text-align: center; - /* padding: 20px 0; */ - font-size: 30px; - /* height:800px; */ -} - -/*****************************/ - -#header { - overflow: auto; - grid-area: header; -} - -#kaart { - position: relative; - overflow: auto; - grid-area: kaart; - cursor: zoom-in; - z-index:0; -} - -#kaartdragbar { - grid-area: kaartdragbar; - cursor: ew-resize; -} - -#grafiek { - grid-area: grafiek; - cursor: ew-resize; -} -#controls { - grid-area: controls; - cursor: ew-resize; -} diff --git a/web_app/old/templates/index.html b/web_app/old/templates/index.html deleted file mode 100644 index d4a50c8..0000000 --- a/web_app/old/templates/index.html +++ /dev/null @@ -1,5 +0,0 @@ -{% extends base %} - -{% block preamble %} - -{% endblock %} diff --git a/web_app/old/templates/js/dragbar.js b/web_app/old/templates/js/dragbar.js deleted file mode 100644 index dd94c8f..0000000 --- a/web_app/old/templates/js/dragbar.js +++ /dev/null @@ -1,58 +0,0 @@ -// example from https://stackoverflow.com/questions/46931103/making-a-dragbar-to-resize-divs-inside-css-grids -let isLeftDragging = false; -let isRightDragging = false; - -function ResetColumnSizes() { - // when page resizes return to default col sizes - let page = document.getElementById("grid-container"); - page.style.gridTemplateColumns = "150px auto 8px 30%"; -} - -function SetCursor(cursor) { - let page = document.getElementById("grid-container"); - page.style.cursor = cursor; -} - -function StartKaartDrag() { - // console.log("mouse down"); - isLeftDragging = true; - - SetCursor("ew-resize"); -} - - -function EndDrag() { - // console.log("mouse up"); - isLeftDragging = false; - isRightDragging = false; - - SetCursor("auto"); -} - -function OnDrag(event) { - if (isLeftDragging || isRightDragging) { - // console.log("Dragging"); - //console.log(event); - - let page = document.getElementById("grid-container"); - let leftcol = document.getElementById("kaart"); - - let leftColWidth = isLeftDragging ? event.clientX : leftcol.clientWidth; - - let dragbarWidth = 8; - - let cols = [ - 150, - leftColWidth-150, - dragbarWidth, - page.clientWidth - dragbarWidth - leftColWidth, - ]; - - let newColDefn = cols.map(c => c.toString() + "px").join(" "); - - // console.log(newColDefn); - page.style.gridTemplateColumns = newColDefn; - - event.preventDefault() - } -} diff --git a/web_app/old/templates/js/mapopt.js b/web_app/old/templates/js/mapopt.js deleted file mode 100644 index 3588868..0000000 --- a/web_app/old/templates/js/mapopt.js +++ /dev/null @@ -1,13 +0,0 @@ -function popMapLegend() { - var map_opt = document.getElementById("map_opt"); - var kaart = document.getElementById("kaart"); - if(map_opt.style.opacity < "0.1"){ - map_opt.style.opacity = "1"; - } - else if(map_opt.style.display == "none" ) { - map_opt.style.display = "block"; - } - else { - map_opt.style.display = "none"; - } - } diff --git a/web_app/old/widgets/__init__.py b/web_app/old/widgets/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/web_app/old/widgets/date_time_slider.py b/web_app/old/widgets/date_time_slider.py deleted file mode 100644 index 5d735ff..0000000 --- a/web_app/old/widgets/date_time_slider.py +++ /dev/null @@ -1,86 +0,0 @@ -import math -import numbers -from dataclasses import dataclass, field -from datetime import datetime, timedelta -from typing import Literal - -from bokeh.models import DatetimeTickFormatter, Slider - - -def get_formatter(format="%Y-%m-%d %H:%M:%S"): - return DatetimeTickFormatter(years=format, days=format, hours=format, minutes=format) - - -def round_seconds(ts_seconds, td_seconds, method): - if method in ["floor", "ceil"]: - rounder = getattr(math, method) - else: - rounder = round - - return rounder(ts_seconds / td_seconds) * td_seconds - - -def round_datetime(dt, timedelta_round, method: Literal["floor", "ceil", "round"] = "round"): - ts_seconds = dt.timestamp() - td_seconds = timedelta_round.total_seconds() - rounded_seconds = round_seconds(ts_seconds, td_seconds, method) - - return datetime.fromtimestamp(rounded_seconds) - - -@dataclass -class DatetimeSlider: - start: datetime | int - end: datetime | int - value: datetime | int - step: timedelta | int - format: str = "%Y-%m-%d %H:%M:%S" - kwargs: dict = field(default_factory=dict) - widget: Slider | None = None - - def __post_init__(self): - # validate values - if self.start >= self.end: - raise ValueError(f"{self.start} >= {self.end}") - - if self.value < self.start: - raise ValueError(f"{self.value} < {self.start}") - - if self.value > self.end: - raise ValueError(f"{self.value} > {self.end}") - - if isinstance(self.start, datetime): - start = self.start.timestamp() * 1000 - else: - start = self.start - - if isinstance(self.end, datetime): - end = self.end.timestamp() * 1000 - else: - end = self.end - - if isinstance(self.value, datetime): - value = self.value.timestamp() * 1000 - else: - value = self.value - - if isinstance(self.step, timedelta): - step = self.step.total_seconds() * 1000 - else: - step = self.step - - self.widget = Slider( - start=round_seconds(start, step, "floor"), - end=round_seconds(end, step, "ceil"), - value=round_seconds(value, step, "round"), - step=step, - format=get_formatter(self.format), - **self.kwargs, - ) - - @property - def value_as_datetime(self): - if isinstance(self.widget.value, numbers.Number): - return datetime.fromtimestamp(self.widget.value / 1000) - else: - return self.widget.value diff --git a/web_app/old/widgets/map_figure.py b/web_app/old/widgets/map_figure.py deleted file mode 100644 index 46b57b8..0000000 --- a/web_app/old/widgets/map_figure.py +++ /dev/null @@ -1,133 +0,0 @@ -from typing import Literal - -import bokeh.models as bokeh_models -from bokeh.layouts import column -from bokeh.models import ( - HoverTool, - Range1d, - TapTool, -) -from bokeh.models.widgets import CheckboxGroup, Div, RadioGroup -from bokeh.plotting import figure - -BOKEH_BACKGROUNDS = { - "luchtfoto": { - "url": ( - "https://service.pdok.nl/hwh/luchtfotorgb/wms/v1_0?" - "service=WMS&version=1.3.0&request=GetMap&layers=Actueel_orthoHR" - "&width=265&height=265&styles=&crs=EPSG:28992&format=image/jpeg" - "&bbox={XMIN},{YMIN},{XMAX},{YMAX}" - ), - "class": "BBoxTileSource", - }, - "topografie": { - "url": ( - "https://services.arcgisonline.nl/arcgis/rest/services/Basiskaarten/Topo/" - "MapServer/export?" - "bbox={XMIN},{YMIN},{XMAX},{YMAX}" - "&layers=show" - "&size=385,385" - "&bboxSR=28892" - "&dpi=2500" - "&transparent=true" - "&format=png" - "&f=image" - ), - "class": "BBoxTileSource", - }, -} - -BOKEH_SETTINGS = { - "background": "topografie", - "save_tool": "save", - "active_scroll": "wheel_zoom", - "toolbar_location": "above", -} - - -def get_tilesource(layer, map_configs=BOKEH_BACKGROUNDS): - url = map_configs[layer]["url"] - if "args" in map_configs[layer]: - args = map_configs[layer]["args"] - else: - args = {} - return getattr(bokeh_models, map_configs[layer]["class"])(url=url, **args) - - -def make_map( - bounds: list[float, float, float, float], - background: Literal["luchtfoto", "topografie"] = "topografie", -) -> figure: - # figure ranges - x_range = Range1d(start=bounds[0], end=bounds[2], min_interval=100) - y_range = Range1d(start=bounds[1], end=bounds[3], min_interval=100) - - # set tools - map_hover = HoverTool( - tooltips=[ - ("name", "@name"), - ("node id", "@node_id"), - ("diepte 1", "@value"), - ("diepte 2", "@value"), - ] - ) - - map_hover.visible = False - - tools = [ - "tap", - "wheel_zoom", - "pan", - "reset", - "box_select", - map_hover, - "save", - ] - - # initialize figure - map_fig = figure( - tools=tools, - active_scroll="wheel_zoom", - x_range=x_range, - y_range=y_range, - toolbar_location="above", - ) - - # misc settings - map_fig.axis.visible = False - map_fig.toolbar.logo = None - map_fig.toolbar.autohide = True - map_fig.xgrid.grid_line_color = None - map_fig.ygrid.grid_line_color = None - map_fig.select(type=TapTool) - - # add background - tile_source = get_tilesource(background) - map_fig.add_tile(tile_source, name="background") - - return map_fig - - -def make_options( - map_overlays: dict, - overlays_change, - background_title: str, - background_change, -): - # set overlay and handlers - overlay_options = list(map_overlays.keys()) - active_overlays = [idx for idx, (_, v) in enumerate(map_overlays.items()) if v["visible"]] - overlay_control = CheckboxGroup(labels=overlay_options, active=active_overlays) - overlay_control.on_change("active", overlays_change) - - # set background and handlers - background_options = list(BOKEH_BACKGROUNDS.keys()) - background_active = list(BOKEH_BACKGROUNDS.keys()).index(BOKEH_SETTINGS["background"]) - background_control = RadioGroup(labels=background_options, active=background_active) - background_control.on_change("active", background_change) - map_controls = column( - overlay_control, - Div(text=f"
{background_title}
"), - background_control, - ) - return map_controls