Skip to content

Commit

Permalink
Merge branch 'dashboard'
Browse files Browse the repository at this point in the history
  • Loading branch information
balazsdukai committed Feb 16, 2024
2 parents f255aea + 9e5ea99 commit d3e13d6
Show file tree
Hide file tree
Showing 10 changed files with 385 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@ venv*
/report/report.Rproj
/report/_environment
.Rproj.user

/.quarto/
173 changes: 173 additions & 0 deletions dashboard/dashboard.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
{
"cells": [
{
"cell_type": "raw",
"id": "aedaabf5-3428-45b5-838d-30ea09be31eb",
"metadata": {},
"source": [
"---\n",
"title: Energielabel spreidingen van de buurten van Nederland\n",
"author: 3DGI\n",
"format: dashboard\n",
"---"
]
},
{
"cell_type": "markdown",
"id": "3183f862-3e10-4b45-b6dc-c05ff465712a",
"metadata": {},
"source": [
"# Energy labels of Dutch neighborhoods\n",
"\n",
"The [Voorbeeldwoningen 2022](https://www.rvo.nl/onderwerpen/wetten-en-regels-gebouwen/voorbeeldwoningen-bestaande-bouw) study describes the distribution of energy labels per vormfactor range for each dwelling type of the [WoON 2018](https://www.woononderzoek.nl/) study.\n",
"The latest release of the [3DBAG](https://3dbag.nl) data set (`2023.10.08`) provides the surface areas for calculating the vormfactor for each Pand.\n",
"This work explores the possibility of calculating the vormfactor for each dwelling within a Pand and applying the energy label distributions of the Voorbeeldwoningen 2022 study to estimate the energy label distribution of each neighborhood of the Netherlands.\n",
"\n",
"This repository contains the code for estimating the energy labels and report on the results.\n",
"\n",
"The report can be viewed at: https://3dgi.github.io/wijklabels/report_nl.html\n",
"\n",
"The estimated labels can be downloaded from: https://data.3dgi.xyz/wijklabels. The files with `.fgb` are in the [FlatGeobuf](https://flatgeobuf.org/) format, which can be viewed without download in several GIS applications (e.g. QGIS). For example, to view the neighborhood labels in QGIS, open the file URL (`https://data.3dgi.xyz/wijklabels/labels_neighborhood_geom.fgb`) as a vector data source.\n",
"\n",
"**License** of report and data: https://creativecommons.org/licenses/by/4.0/deed.nl. You are free to share and modify the report and the data, as long as you give appropriate credit to 3DGI, provide a link to the license, and indicate if changes were made.\n",
"\n",
"This project was made by [3DGI](https://3dgi.nl) and commissioned by the [Rijksdienst van Ondernemend Nederland](https://rvo.nl)."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "b3b55ba0-48ba-44fa-810f-ccb34ed817a3",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
" <iframe\n",
" width=\"100%\"\n",
" height=\"650\"\n",
" src=\"http://127.0.0.1:8050/\"\n",
" frameborder=\"0\"\n",
" allowfullscreen\n",
" \n",
" ></iframe>\n",
" "
],
"text/plain": [
"<IPython.lib.display.IFrame at 0x7fbd5a230ce0>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"from dash import Dash, html, dcc, callback, Output, Input\n",
"import geopandas\n",
"import plotly.graph_objects as go\n",
"\n",
"COLORS = {\"#1a9641\": \"a++++\",\n",
" \"#52b151\": \"a+++\",\n",
" \"#8acc62\": \"a++\",\n",
" \"#b8e17b\": \"a+\",\n",
" \"#dcf09e\": \"a\",\n",
" \"#ffffc0\": \"b\",\n",
" \"#ffdf9a\": \"c\",\n",
" \"#febe74\": \"d\",\n",
" \"#f69053\": \"e\",\n",
" \"#e75437\": \"f\",\n",
" \"#d7191c\": \"g\"\n",
" }\n",
"\n",
"neighborhoods = geopandas.read_file(\"labels_neighborhood_geom.fgb\").set_index([\"gemeentenaam\", \"buurtnaam\"])\n",
"nbhd_long = neighborhoods.drop(columns=[\"geometry\"]).melt(var_name=\"Energielabel\", value_name=\"Percentage\", ignore_index=False).sort_index()\n",
"nbhd_long[\"Percentage\"] = nbhd_long[\"Percentage\"] * 100 \n",
"\n",
"app = Dash(__name__)\n",
"\n",
"def plot_buurt(selected_municipality, selected_neighborhood):\n",
" fig = go.Figure(data=[go.Bar(\n",
" x=nbhd_long.loc[(selected_municipality, selected_neighborhood)].Energielabel,\n",
" y=nbhd_long.loc[(selected_municipality, selected_neighborhood)].Percentage,\n",
" marker_color=list(COLORS.keys()),\n",
" hovertemplate = \"%{x}: %{y:.1f}%\"\n",
" )])\n",
" fig.update_yaxes(range=[0, 60])\n",
" fig.update_layout(title_text=f\"{selected_municipality}, {selected_neighborhood}\", xaxis_title=\"Energielabel\", yaxis_title=\"Percentage (%)\")\n",
" return fig\n",
"\n",
"@callback(\n",
" Output(component_id='controls-and-graph', component_property='figure'),\n",
" [Input('municipalities-dropdown', 'value'), Input('neighborhoods-dropdown', 'value')]\n",
")\n",
"def update_graph(selected_municipality, selected_neighborhood):\n",
" fig = plot_buurt(selected_municipality, selected_neighborhood)\n",
" return fig\n",
"\n",
"@app.callback(\n",
" Output('neighborhoods-dropdown', 'options'),\n",
" [Input('municipalities-dropdown', 'value')])\n",
"def set_neighborhoods_options(selected_municipality):\n",
" return neighborhoods.index.get_loc_level(selected_municipality, level=0)[1]\n",
"\n",
"@app.callback(\n",
" Output('neighborhoods-dropdown', 'value'),\n",
" [Input('neighborhoods-dropdown', 'options')])\n",
"def set_neighborhoods_value(available_options):\n",
" return available_options[0]\n",
"\n",
"app.layout = html.Div([\n",
" html.Div([\n",
" html.Div(\n",
" children=[\n",
" html.H5(children=\"Municipality:\"),\n",
" dcc.Dropdown(\n",
" id=\"municipalities-dropdown\",\n",
" options=neighborhoods.index.get_level_values(0).unique(),\n",
" value=\"'s-Gravenhage\",\n",
" clearable=False\n",
" ),\n",
" ], style={'padding': 10, 'flex': 1}\n",
" ),\n",
" html.Div(\n",
" children=[\n",
" html.H5(children=\"Neighborhood:\"),\n",
" dcc.Dropdown(\n",
" id=\"neighborhoods-dropdown\",\n",
" value=\"Bezuidenhout-Midden\",\n",
" clearable=False\n",
" ),\n",
" ], style={'padding': 10, 'flex': 1}\n",
" ),\n",
" ], style={'display': 'flex', 'flexDirection': 'row'}),\n",
" dcc.Graph(figure={}, id=\"controls-and-graph\"),\n",
"])\n",
"\n",
"if __name__ == '__main__':\n",
" app.run(debug=True)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.1"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
98 changes: 98 additions & 0 deletions dashboard/dashboard_map.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "f96abda3-d1c0-4280-a635-83c7005662e0",
"metadata": {},
"outputs": [],
"source": [
"from dash import Dash, html, dash_table, dcc, callback, Output, Input\n",
"import dash_leaflet as dl\n",
"import geopandas\n",
"import plotly.graph_objects as go\n",
"from wijklabels.report import COLORS\n",
"\n",
"neighborhoods = geopandas.read_file(\"labels_neighborhood_geom_4326.fgb\").set_index(\"buurtnaam\")\n",
"nbhd_long = neighborhoods.drop(columns=[\"geometry\", \"buurtcode\"]).melt(var_name=\"Energielabel\", value_name=\"Percentage\", ignore_index=False)\n",
"nbhd_long[\"Percentage\"] = nbhd_long[\"Percentage\"] * 100 \n",
"\n",
"def plot_buurt(buurtnaam):\n",
" fig = go.Figure(data=[go.Bar(\n",
" x=nbhd_long.loc[buurtnaam].Energielabel,\n",
" y=nbhd_long.loc[buurtnaam].Percentage,\n",
" marker_color=list(COLORS.keys()),\n",
" hovertemplate = \"%{x}: %{y:.1f}%\"\n",
" )])\n",
" fig.update_yaxes(range=[0, 50])\n",
" fig.update_layout(title_text=buurtnaam, xaxis_title=\"Energielabel\", yaxis_title=\"Percentage (%)\")\n",
" return fig\n",
"\n",
"@callback(\n",
" Output(component_id='controls-and-graph', component_property='figure'),\n",
" Input(component_id='controls-and-dropdown', component_property='value')\n",
")\n",
"def update_graph(buurtnaam):\n",
" fig = plot_buurt(buurtnaam)\n",
" return fig\n",
"\n",
"def get_rect(center: list[float], delta: float = 2):\n",
" return dict(minX=center[1] - delta, maxX=center[1] + delta, minY=center[0] - delta, maxY=center[0] + delta)\n",
"\n",
"#initial_center=[82342.0, 455161.0]\n",
"initial_center=[52.08, 4.33]\n",
"#initial_center=[480905, 6813218]\n",
"\n",
"basemap_url=\"https://service.pdok.nl/brt/achtergrondkaart/wmts/v2_0?&service=wmts&request=GetTile&version=1.0.0&dpiMode=7&format=image/png&layer=grijs&styles=default&tileMatrixSet=EPSG:28992&tilematrix={z}&tilecol={x}&tilerow={y}\"\n",
"\n",
"app = Dash(__name__)\n",
"\n",
"gj = neighborhoods.to_json()\n",
"\n",
"app.layout = html.Div([\n",
" html.Div(children='Energielabel buurten'),\n",
" dl.Map(children=[\n",
" dl.TileLayer(),\n",
" dl.GeoJSON(\n",
" data=gj\n",
" )\n",
" ],\n",
" center=initial_center,\n",
" zoom=14,\n",
" style={'height': '50vh'}, id=\"map\"),\n",
" dcc.Dropdown(\n",
" id=\"controls-and-dropdown\",\n",
" options=neighborhoods.index,\n",
" value=\"Bezuidenhout-Midden\",\n",
" clearable=False\n",
" ),\n",
" dcc.Graph(figure={}, id=\"controls-and-graph\"),\n",
"])\n",
"\n",
"if __name__ == '__main__':\n",
" app.run(debug=False)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.1"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
93 changes: 93 additions & 0 deletions dashboard/dashboard_server/dashboard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
from dash import Dash, html, dcc, callback, Output, Input
import geopandas
import plotly.graph_objects as go
import flask

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

server = flask.Flask(__name__) # define flask app.server

app = Dash(__name__, external_stylesheets=external_stylesheets,
server=server,
url_base_pathname='/wijklabels/') # call flask server

# run following in command
# gunicorn graph:server -b :8050

COLORS = {"#1a9641": "a++++",
"#52b151": "a+++",
"#8acc62": "a++",
"#b8e17b": "a+",
"#dcf09e": "a",
"#ffffc0": "b",
"#ffdf9a": "c",
"#febe74": "d",
"#f69053": "e",
"#e75437": "f",
"#d7191c": "g"
}

neighborhoods = geopandas.read_file("labels_neighborhood_geom.fgb").set_index(["gemeentenaam", "buurtnaam"])
nbhd_long = neighborhoods.drop(columns=["geometry"]).melt(var_name="Energielabel", value_name="Percentage", ignore_index=False).sort_index()
nbhd_long["Percentage"] = nbhd_long["Percentage"] * 100

def plot_buurt(selected_municipality, selected_neighborhood):
fig = go.Figure(data=[go.Bar(
x=nbhd_long.loc[(selected_municipality, selected_neighborhood)].Energielabel,
y=nbhd_long.loc[(selected_municipality, selected_neighborhood)].Percentage,
marker_color=list(COLORS.keys()),
hovertemplate = "%{x}: %{y:.1f}%"
)])
fig.update_yaxes(range=[0, 60])
fig.update_layout(title_text=f"{selected_municipality}, {selected_neighborhood}", xaxis_title="Energielabel", yaxis_title="Percentage (%)")
return fig

@callback(
Output(component_id='controls-and-graph', component_property='figure'),
[Input('municipalities-dropdown', 'value'), Input('neighborhoods-dropdown', 'value')]
)
def update_graph(selected_municipality, selected_neighborhood):
fig = plot_buurt(selected_municipality, selected_neighborhood)
return fig

@app.callback(
Output('neighborhoods-dropdown', 'options'),
[Input('municipalities-dropdown', 'value')])
def set_neighborhoods_options(selected_municipality):
return neighborhoods.index.get_loc_level(selected_municipality, level=0)[1]

@app.callback(
Output('neighborhoods-dropdown', 'value'),
[Input('neighborhoods-dropdown', 'options')])
def set_neighborhoods_value(available_options):
return available_options[0]

app.layout = html.Div([
html.Div([
html.Div(
children=[
html.H5(children="Municipality:"),
dcc.Dropdown(
id="municipalities-dropdown",
options=neighborhoods.index.get_level_values(0).unique(),
value="'s-Gravenhage",
clearable=False
),
], style={'padding': 10, 'flex': 1}
),
html.Div(
children=[
html.H5(children="Neighborhood:"),
dcc.Dropdown(
id="neighborhoods-dropdown",
value="Bezuidenhout-Midden",
clearable=False
),
], style={'padding': 10, 'flex': 1}
),
], style={'display': 'flex', 'flexDirection': 'row'}),
dcc.Graph(figure={}, id="controls-and-graph"),
])

# if __name__ == '__main__':
# app.run_server(debug=True)
5 changes: 5 additions & 0 deletions dashboard/dashboard_server/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
geopandas==0.14.3
folium==0.15.1
plotly==5.18.0
dash==2.15.0
gunicorn==21.2.0
1 change: 1 addition & 0 deletions dashboard/dashboard_server/run_gunicorn.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
gunicorn --bind 0.0.0.0:8050 wsgi:server
4 changes: 4 additions & 0 deletions dashboard/dashboard_server/wsgi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from dashboard import app, server

if __name__ == "__main__":
app.run_server(port=8050, debug=True)
1 change: 1 addition & 0 deletions dashboard/render.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
quarto render dashboard_dash.ipynb --output-dir dashboard_rendered
Loading

0 comments on commit d3e13d6

Please sign in to comment.