-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding folium script to create animations from simulation_output.csv.
- Loading branch information
Jonathan Bloedow
committed
Jun 18, 2024
1 parent
b066650
commit 3a734a6
Showing
1 changed file
with
119 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import csv | ||
import sqlite3 | ||
import random | ||
import numpy as np | ||
import sys | ||
import argparse | ||
|
||
import folium | ||
from folium.plugins import HeatMapWithTime | ||
|
||
# Connect to the SQLite database (or create it) | ||
conn = sqlite3.connect(':memory:') | ||
cursor = conn.cursor() | ||
|
||
def preproc( sim_report_file ): | ||
# Create tables and import data from CSV files | ||
cursor.execute("DROP TABLE IF EXISTS engwal") | ||
cursor.execute("DROP TABLE IF EXISTS cities") | ||
|
||
# Import simulation_output.csv into engwal table | ||
with open(sim_report_file, 'r') as file: | ||
reader = csv.reader(file) | ||
headers = next(reader) | ||
cursor.execute(f"CREATE TABLE engwal ({', '.join(headers)})") | ||
cursor.executemany(f"INSERT INTO engwal VALUES ({', '.join(['?' for _ in headers])})", reader) | ||
|
||
# Import cities.csv into cities table | ||
with open('cities.csv', 'r') as file: | ||
reader = csv.reader(file) | ||
headers = next(reader) | ||
cursor.execute(f"CREATE TABLE cities ({', '.join(headers)})") | ||
cursor.executemany(f"INSERT INTO cities VALUES ({', '.join(['?' for _ in headers])})", reader) | ||
|
||
# Create the view | ||
cursor.execute(""" | ||
CREATE VIEW cases AS | ||
SELECT Timestep, Node, Name, Latitude, Longitude, "New Infections" | ||
FROM engwal, cities | ||
WHERE engwal.Node = cities.ID | ||
""") | ||
|
||
conn.commit() | ||
|
||
|
||
def process( output_file ): | ||
# Create a map centered at a specific location | ||
birmingham_location = (52.485,-1.86) | ||
m = folium.Map(location=(birmingham_location[0],birmingham_location[1]), zoom_start=8) # Create a list to store the data for HeatMapWithTime | ||
|
||
start_time=800 | ||
cursor.execute(f'SELECT CAST(Timestep AS INT), Latitude, Longitude, CAST("New Infections" AS INT) FROM cases WHERE (CAST(Timestep AS INT)>{start_time})') | ||
raw_data = cursor.fetchall() | ||
|
||
# Group the data by timestep | ||
grouped_data = {} | ||
for row in raw_data: | ||
timestep, lat, lon, new_infections = row | ||
if timestep not in grouped_data: | ||
grouped_data[timestep] = [] | ||
grouped_data[timestep].append([lat, lon, new_infections]) | ||
|
||
data = [] | ||
grouped_data = list(grouped_data.values()) | ||
locations=[[ float(elem[0]), float(elem[1]) ] for elem in grouped_data[0]] | ||
|
||
max_cases = 500 # updating this manually | ||
for t in range(len(grouped_data)-1): | ||
this_row = [] | ||
for i, location in enumerate( locations ): | ||
try: | ||
def renorm( value ): | ||
if value > max_cases: | ||
value = max_cases | ||
offset = 1 | ||
log_value = np.log(value + offset) | ||
log_min = np.log(0 + offset) | ||
log_max = np.log(max_cases + offset) | ||
return (log_value - log_min) / (log_max - log_min) | ||
case_value = grouped_data[t][i][2] | ||
case_value = renorm( case_value ) | ||
# random jitter seems to be essential!!! Yes, wack. | ||
this_row.append( [ location[0], location[1], case_value+random.random()/2000 ] ) | ||
except Exception as ex: | ||
print( f"Exception with t={t}, i={i}." ) | ||
raise ValueError( str( ex ) ) | ||
data.append( this_row ) | ||
|
||
heat_map = HeatMapWithTime( | ||
data, | ||
radius=15, | ||
gradient={0.0: 'blue', 0.2: 'green', 0.4: 'yellow', 0.6: 'orange', 0.8: 'red'}, | ||
min_opacity = 0.0, | ||
max_opacity = 0.8, | ||
#use_local_extrema=True, | ||
auto_play=True | ||
) | ||
heat_map.add_to(m) | ||
|
||
|
||
# Save the map as an HTML file | ||
m.save( output_file ) | ||
|
||
if __name__ == "__main__": | ||
# Set up argument parser | ||
parser = argparse.ArgumentParser(description='Process simulation data.') | ||
parser.add_argument('-i', '--input', type=str, default='simulation_output.csv', | ||
help='Input file name (default: simulation_output.csv)') | ||
parser.add_argument('-o', '--output', type=str, default='sim_animation.html', | ||
help='Output file name (default: sim_animation.html)') | ||
|
||
# Parse command-line arguments | ||
args = parser.parse_args() | ||
|
||
# Call the preproc function with the input file | ||
preproc(args.input) | ||
|
||
# Call the process function with the output file | ||
process(args.output) | ||
|