From 8baf519eef8c6a8f3bee512fe05d1d694612dba3 Mon Sep 17 00:00:00 2001 From: Christian Chwala Date: Tue, 4 Mar 2025 21:53:50 +0100 Subject: [PATCH] first basic version of map and timeseries using panel from holoviz --- visualization/Dockerfile | 2 +- visualization/main.py | 124 +++++++++++---------------------- visualization/requirements.txt | 7 +- 3 files changed, 46 insertions(+), 87 deletions(-) diff --git a/visualization/Dockerfile b/visualization/Dockerfile index 54dac63..9cc7c4d 100644 --- a/visualization/Dockerfile +++ b/visualization/Dockerfile @@ -9,4 +9,4 @@ COPY . . EXPOSE 5000 -CMD ["python", "main.py"] \ No newline at end of file +CMD ["panel", "serve", "main.py", "--port=5000"] \ No newline at end of file diff --git a/visualization/main.py b/visualization/main.py index 0acf644..548eac0 100644 --- a/visualization/main.py +++ b/visualization/main.py @@ -1,95 +1,55 @@ import os import time -import folium import psycopg2 -from flask import Flask, render_template_string, request -import altair as alt -app = Flask(__name__) +import pandas as pd -# Function to read data from DB and generate a Leaflet map -def generate_map(): +import folium +import panel as pn +import bokeh.plotting + +time.sleep(10) + +pn.extension(sizing_mode="stretch_width") + +def get_metadata_from_db(): # Connect to the database conn = psycopg2.connect(os.getenv('DATABASE_URL')) - cur = conn.cursor() - - # Execute a query to retrieve data from the table - cur.execute("SELECT cml_id, site_0_lon, site_0_lat, site_1_lon, site_1_lat FROM cml_metadata") - data = cur.fetchall() - - # Create a map centered at the average latitude and longitude - latitudes = [row[2] for row in data] - longitudes = [row[1] for row in data] - avg_lat = sum(latitudes) / len(latitudes) - avg_lon = sum(longitudes) / len(longitudes) - m = folium.Map(location=[avg_lat, avg_lon], zoom_start=4) - - # Loop through the data and add a line for each row - for row in data: - cml_id = row[0] - site_0_lon = row[1] - site_0_lat = row[2] - site_1_lon = row[3] - site_1_lat = row[4] - folium.PolyLine([[site_0_lat, site_0_lon], [site_1_lat, site_1_lon]], color='blue', weight=2.5, opacity=1, popup=f'cml_id: {cml_id}').add_to(m) - - # Save the map as an HTML file - m.save("map.html") - - # Close the database connection - cur.close() - conn.close() - -# Function to query time series data from the database and add it to the altair plot -def generate_time_series_plot(cml_id=None): - # # Connect to the database - # conn = psycopg2.connect(os.getenv('DATABASE_URL')) - # cur = conn.cursor() - - # # Execute a query to retrieve time series data from the table - # if cml_id: - # cur.execute("SELECT date, value FROM time_series_data WHERE cml_id = %s", (cml_id,)) - # else: - # cur.execute("SELECT date, value FROM time_series_data") - # data = cur.fetchall() - import pandas as pd + query = "SELECT cml_id, site_0_lon, site_0_lat, site_1_lon, site_1_lat FROM cml_metadata" + return pd.read_sql_query(query, conn) +def get_data_from_db(): conn = psycopg2.connect(os.getenv('DATABASE_URL')) query = 'SELECT * FROM cml_data WHERE cml_id = %s AND sublink_id = %s' - params = ('10001', 'sublink_1') - df = pd.read_sql_query(query, conn, params=params) - conn.close() - - # Create an altair plot - plot = alt.Chart(df).mark_line().encode( - x='time:T', - y='rsl:Q' - ) - - # Return the plot as an HTML string - return plot.to_html() + params = ('10023', 'sublink_1') + return pd.read_sql_query(query, conn, params=params) -# Route to serve the map and time series plot -@app.route('/') -def serve_map_and_plot(): - with open('map.html', 'r') as f: - map_html = f.read() - - time_series_plot_html = generate_time_series_plot() - - # Combine the map and time series plot HTML - combined_html = f"{map_html}

Time Series Plot

{time_series_plot_html}" - return render_template_string(combined_html) -# Route to update the time series plot based on the selected cml_id -@app.route('/update_plot', methods=['POST']) -def update_time_series_plot(): - cml_id = request.form['cml_id'] - time_series_plot_html = generate_time_series_plot(cml_id) - return render_template_string(time_series_plot_html) +# Function to read data from DB and generate a Bokeh plot +def generate_map(): + df_metadata = get_metadata_from_db() -# Start the Flask server -if __name__ == "__main__": - time.sleep(10) + # Create a map centered at the average latitude and longitude - generate_map() - app.run(host='0.0.0.0', debug=True) \ No newline at end of file + avg_lat = df_metadata['site_0_lat'].mean() + avg_lon = df_metadata['site_0_lon'].mean() + m = folium.Map(location=[avg_lat, avg_lon], zoom_start=10) + + for i in range(0, len(df_metadata)): + folium.PolyLine( + [ + [df_metadata.iloc[i]['site_0_lat'], df_metadata.iloc[i]['site_0_lon']], + [df_metadata.iloc[i]['site_1_lat'], df_metadata.iloc[i]['site_1_lon']] + ] + ).add_to(m) + return pn.pane.plot.Folium(m, height=400, sizing_mode='stretch_width') + +def generate_time_series_plot(): + df = get_data_from_db() + p = bokeh.plotting.figure(width=800) + p.line(df['time'], df['rsl']) + return pn.pane.Bokeh(p, height=200, sizing_mode='stretch_width') + + +pn.template.FastListTemplate( + title="GMDI prototype", main=[generate_map(), generate_time_series_plot()] +).servable() \ No newline at end of file diff --git a/visualization/requirements.txt b/visualization/requirements.txt index 703a707..bc0c738 100644 --- a/visualization/requirements.txt +++ b/visualization/requirements.txt @@ -1,6 +1,5 @@ psycopg2-binary folium -requests -flask -altair -pandas \ No newline at end of file +pandas +panel +bokeh \ No newline at end of file