From ad1e606882848105e46941ef3a34eb4c08e315d7 Mon Sep 17 00:00:00 2001 From: Wang Boyu Date: Thu, 26 Sep 2024 17:19:38 -0400 Subject: [PATCH] gis: update four examples to use solara viz --- gis/agents_and_networks/README.md | 4 +- gis/agents_and_networks/app.py | 65 +++++++++++++++++++ gis/agents_and_networks/requirements.txt | 2 +- gis/agents_and_networks/scripts/run.py | 64 ------------------ .../src/visualization/server.py | 64 ------------------ .../src/visualization/utils.py | 42 ++++++++++++ gis/geo_schelling/README.md | 6 +- gis/geo_schelling/app.py | 44 +++++++++++++ gis/geo_schelling/requirements.txt | 2 +- gis/geo_schelling/run.py | 3 - gis/geo_schelling/server.py | 45 ------------- gis/geo_schelling_points/README.md | 8 +-- gis/geo_schelling_points/app.py | 46 +++++++++++++ .../geo_schelling_points/__init__.py | 0 .../geo_schelling_points/model.py | 21 +++++- .../geo_schelling_points/server.py | 59 ----------------- gis/geo_schelling_points/requirements.txt | 2 +- gis/geo_schelling_points/run.py | 3 - gis/geo_sir/README.md | 8 +-- gis/geo_sir/app.py | 42 ++++++++++++ gis/geo_sir/geo_sir/__init__.py | 0 gis/geo_sir/{ => geo_sir}/agents.py | 0 gis/geo_sir/{ => geo_sir}/model.py | 2 +- gis/geo_sir/requirements.txt | 2 +- gis/geo_sir/run.py | 3 - gis/geo_sir/server.py | 63 ------------------ 26 files changed, 277 insertions(+), 323 deletions(-) create mode 100644 gis/agents_and_networks/app.py delete mode 100644 gis/agents_and_networks/scripts/run.py delete mode 100644 gis/agents_and_networks/src/visualization/server.py create mode 100644 gis/geo_schelling/app.py delete mode 100644 gis/geo_schelling/run.py delete mode 100644 gis/geo_schelling/server.py create mode 100644 gis/geo_schelling_points/app.py create mode 100644 gis/geo_schelling_points/geo_schelling_points/__init__.py delete mode 100644 gis/geo_schelling_points/geo_schelling_points/server.py delete mode 100644 gis/geo_schelling_points/run.py create mode 100644 gis/geo_sir/app.py create mode 100644 gis/geo_sir/geo_sir/__init__.py rename gis/geo_sir/{ => geo_sir}/agents.py (100%) rename gis/geo_sir/{ => geo_sir}/model.py (98%) delete mode 100644 gis/geo_sir/run.py delete mode 100644 gis/geo_sir/server.py diff --git a/gis/agents_and_networks/README.md b/gis/agents_and_networks/README.md index 55e08490..344d4db2 100644 --- a/gis/agents_and_networks/README.md +++ b/gis/agents_and_networks/README.md @@ -28,12 +28,12 @@ python3 -m pip install -r requirements.txt Then run the model: ```bash -python3 scripts/run.py --campus ub +solara run app.py -- --campus ub ``` Change `ub` to `gmu` for a different campus map. -Open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/) and press `Start`. +Then open your browser to [http://127.0.0.1:8765/](http://127.0.0.1:8765/) and press the play button `▶`. ## License diff --git a/gis/agents_and_networks/app.py b/gis/agents_and_networks/app.py new file mode 100644 index 00000000..1d4328a1 --- /dev/null +++ b/gis/agents_and_networks/app.py @@ -0,0 +1,65 @@ +import sys + +from mesa.visualization import Slider, SolaraViz, make_plot_measure +from mesa_geo.visualization import make_geospace_leaflet +from src.model.model import AgentsAndNetworks +from src.visualization.utils import agent_draw, make_plot_clock + + +def parse_args(): + campus = "ub" + if "--campus" in sys.argv: + campus = sys.argv[sys.argv.index("--campus") + 1] + return campus + + +if __name__ == "__main__": + campus = parse_args() + + if campus == "ub": + data_file_prefix = "UB" + elif campus == "gmu": + data_file_prefix = "Mason" + else: + raise ValueError("Invalid campus name. Choose from ub or gmu.") + + campus_params = { + "ub": {"data_crs": "epsg:4326", "commuter_speed": 0.5, "zoom": 14}, + "gmu": {"data_crs": "epsg:2283", "commuter_speed": 0.4, "zoom": 16}, + } + model_params = { + "campus": campus, + "data_crs": campus_params[campus]["data_crs"], + "buildings_file": f"data/{campus}/{data_file_prefix}_bld.zip", + "walkway_file": f"data/{campus}/{data_file_prefix}_walkway_line.zip", + "lakes_file": f"data/{campus}/hydrop.zip", + "rivers_file": f"data/{campus}/hydrol.zip", + "driveway_file": f"data/{campus}/{data_file_prefix}_Rds.zip", + "output_dir": "outputs", + "show_walkway": True, + "show_lakes_and_rivers": True, + "show_driveway": True, + "num_commuters": Slider( + "Number of Commuters", value=50, min=10, max=150, step=10 + ), + "commuter_speed": Slider( + "Commuter Walking Speed (m/s)", + value=campus_params[campus]["commuter_speed"], + min=0.1, + max=1.5, + step=0.1, + ), + } + model = AgentsAndNetworks() + page = SolaraViz( + model, + [make_geospace_leaflet(agent_draw, zoom=campus_params[campus]["zoom"]), + make_plot_clock, + make_plot_measure(["status_home", "status_work", "status_traveling"]), + make_plot_measure(["friendship_home", "friendship_work"]), + ], + name="Agents and Networks", + model_params=model_params, + ) + + page # noqa diff --git a/gis/agents_and_networks/requirements.txt b/gis/agents_and_networks/requirements.txt index e86259fe..a6b20378 100644 --- a/gis/agents_and_networks/requirements.txt +++ b/gis/agents_and_networks/requirements.txt @@ -2,7 +2,7 @@ -e . # external requirements -mesa-geo~=0.7 +mesa-geo~=0.9.0a0 geopandas numpy pandas diff --git a/gis/agents_and_networks/scripts/run.py b/gis/agents_and_networks/scripts/run.py deleted file mode 100644 index 8aa0712d..00000000 --- a/gis/agents_and_networks/scripts/run.py +++ /dev/null @@ -1,64 +0,0 @@ -import argparse - -import mesa -import mesa_geo as mg -from src.model.model import AgentsAndNetworks -from src.visualization.server import ( - agent_draw, - clock_element, - friendship_chart, - status_chart, -) - - -def make_parser(): - parser = argparse.ArgumentParser("Agents and Networks in Python") - parser.add_argument("--campus", type=str, required=True) - return parser - - -if __name__ == "__main__": - args = make_parser().parse_args() - - if args.campus == "ub": - data_file_prefix = "UB" - elif args.campus == "gmu": - data_file_prefix = "Mason" - else: - raise ValueError("Invalid campus name. Choose from ub or gmu.") - - campus_params = { - "ub": {"data_crs": "epsg:4326", "commuter_speed": 0.5}, - "gmu": {"data_crs": "epsg:2283", "commuter_speed": 0.4}, - } - model_params = { - "campus": args.campus, - "data_crs": campus_params[args.campus]["data_crs"], - "buildings_file": f"data/{args.campus}/{data_file_prefix}_bld.zip", - "walkway_file": f"data/{args.campus}/{data_file_prefix}_walkway_line.zip", - "lakes_file": f"data/{args.campus}/hydrop.zip", - "rivers_file": f"data/{args.campus}/hydrol.zip", - "driveway_file": f"data/{args.campus}/{data_file_prefix}_Rds.zip", - "output_dir": "outputs", - "show_walkway": True, - "show_lakes_and_rivers": True, - "show_driveway": True, - "num_commuters": mesa.visualization.Slider( - "Number of Commuters", value=50, min_value=10, max_value=150, step=10 - ), - "commuter_speed": mesa.visualization.Slider( - "Commuter Walking Speed (m/s)", - value=campus_params[args.campus]["commuter_speed"], - min_value=0.1, - max_value=1.5, - step=0.1, - ), - } - map_element = mg.visualization.MapModule(agent_draw, map_height=600, map_width=600) - server = mesa.visualization.ModularServer( - AgentsAndNetworks, - [map_element, clock_element, status_chart, friendship_chart], - "Agents and Networks", - model_params, - ) - server.launch() diff --git a/gis/agents_and_networks/src/visualization/server.py b/gis/agents_and_networks/src/visualization/server.py deleted file mode 100644 index c3c04e17..00000000 --- a/gis/agents_and_networks/src/visualization/server.py +++ /dev/null @@ -1,64 +0,0 @@ -import mesa - -from ..agent.building import Building -from ..agent.commuter import Commuter -from ..agent.geo_agents import Driveway, LakeAndRiver, Walkway - - -class ClockElement(mesa.visualization.TextElement): - def __init__(self): - super().__init__() - - def render(self, model): - return f"Day {model.day}, {model.hour:02d}:{model.minute:02d}" - - -def agent_draw(agent): - portrayal = {} - portrayal["color"] = "White" - if isinstance(agent, Driveway): - portrayal["color"] = "#D08004" - elif isinstance(agent, Walkway): - portrayal["color"] = "Brown" - elif isinstance(agent, LakeAndRiver): - portrayal["color"] = "#04D0CD" - elif isinstance(agent, Building): - portrayal["color"] = "Grey" - # if agent.function is None: - # portrayal["color"] = "Grey" - # elif agent.function == 1.0: - # portrayal["color"] = "Blue" - # elif agent.function == 2.0: - # portrayal["color"] = "Green" - # else: - # portrayal["color"] = "Grey" - elif isinstance(agent, Commuter): - if agent.status == "home": - portrayal["color"] = "Green" - elif agent.status == "work": - portrayal["color"] = "Blue" - elif agent.status == "transport": - portrayal["color"] = "Red" - else: - portrayal["color"] = "Grey" - portrayal["radius"] = "5" - portrayal["fillOpacity"] = 1 - return portrayal - - -clock_element = ClockElement() -status_chart = mesa.visualization.ChartModule( - [ - {"Label": "status_home", "Color": "Green"}, - {"Label": "status_work", "Color": "Blue"}, - {"Label": "status_traveling", "Color": "Red"}, - ], - data_collector_name="datacollector", -) -friendship_chart = mesa.visualization.ChartModule( - [ - {"Label": "friendship_home", "Color": "Green"}, - {"Label": "friendship_work", "Color": "Blue"}, - ], - data_collector_name="datacollector", -) diff --git a/gis/agents_and_networks/src/visualization/utils.py b/gis/agents_and_networks/src/visualization/utils.py index c31d1536..d34a9217 100644 --- a/gis/agents_and_networks/src/visualization/utils.py +++ b/gis/agents_and_networks/src/visualization/utils.py @@ -3,6 +3,48 @@ import matplotlib.pyplot as plt import pandas as pd import seaborn as sns +import solara + +from ..agent.building import Building +from ..agent.commuter import Commuter +from ..agent.geo_agents import Driveway, LakeAndRiver, Walkway + + +def make_plot_clock(model): + return solara.Markdown(f"**Day {model.day}, {model.hour:02d}:{model.minute:02d}**") + + +def agent_draw(agent): + portrayal = {} + portrayal["color"] = "White" + if isinstance(agent, Driveway): + portrayal["color"] = "#D08004" + elif isinstance(agent, Walkway): + portrayal["color"] = "Brown" + elif isinstance(agent, LakeAndRiver): + portrayal["color"] = "#04D0CD" + elif isinstance(agent, Building): + portrayal["color"] = "Grey" + # if agent.function is None: + # portrayal["color"] = "Grey" + # elif agent.function == 1.0: + # portrayal["color"] = "Blue" + # elif agent.function == 2.0: + # portrayal["color"] = "Green" + # else: + # portrayal["color"] = "Grey" + elif isinstance(agent, Commuter): + if agent.status == "home": + portrayal["color"] = "Green" + elif agent.status == "work": + portrayal["color"] = "Blue" + elif agent.status == "transport": + portrayal["color"] = "Red" + else: + portrayal["color"] = "Grey" + portrayal["radius"] = "5" + portrayal["fillOpacity"] = 1 + return portrayal def plot_commuter_status_count(model_vars_df: pd.DataFrame) -> None: diff --git a/gis/geo_schelling/README.md b/gis/geo_schelling/README.md index 6dd902cc..258dacae 100644 --- a/gis/geo_schelling/README.md +++ b/gis/geo_schelling/README.md @@ -16,10 +16,10 @@ NUTS-2 regions are the GeoAgents. The neighbors of a polygon are considered thos ## How to Run -To run the model interactively, run `mesa runserver` in this directory. e.g. +To run the model interactively, run `solara run app.py` in this directory. e.g. ```bash -mesa runserver +solara run app.py ``` -Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/) and press `Start`. +Then open your browser to [http://127.0.0.1:8765/](http://127.0.0.1:8765/) and press the play button `▶`. diff --git a/gis/geo_schelling/app.py b/gis/geo_schelling/app.py new file mode 100644 index 00000000..8ec379a4 --- /dev/null +++ b/gis/geo_schelling/app.py @@ -0,0 +1,44 @@ +import solara +from mesa.visualization import Slider, SolaraViz, make_plot_measure +from mesa_geo.visualization import make_geospace_leaflet +from model import GeoSchelling + + +def make_plot_happiness(model): + return solara.Markdown(f"**Happy agents: {model.happy}**") + + +model_params = { + "density": Slider("Agent density", 0.6, 0.1, 1.0, 0.1), + "minority_pc": Slider("Fraction minority", 0.2, 0.00, 1.0, 0.05), + "export_data": False, +} + + +def schelling_draw(agent): + """ + Portrayal Method for canvas + """ + portrayal = {} + if agent.atype is None: + portrayal["color"] = "Grey" + elif agent.atype == 0: + portrayal["color"] = "Red" + else: + portrayal["color"] = "Blue" + return portrayal + + +model = GeoSchelling() +page = SolaraViz( + model, + [ + make_geospace_leaflet(schelling_draw, zoom=4), + make_plot_happiness, + make_plot_measure(["happy"]), + ], + model_params=model_params, + name="GeoSchelling", +) + +page # noqa diff --git a/gis/geo_schelling/requirements.txt b/gis/geo_schelling/requirements.txt index 96935875..6d998684 100644 --- a/gis/geo_schelling/requirements.txt +++ b/gis/geo_schelling/requirements.txt @@ -1 +1 @@ -mesa-geo~=0.7 +mesa-geo~=0.9.0a0 diff --git a/gis/geo_schelling/run.py b/gis/geo_schelling/run.py deleted file mode 100644 index a25f3b12..00000000 --- a/gis/geo_schelling/run.py +++ /dev/null @@ -1,3 +0,0 @@ -from server import server - -server.launch() diff --git a/gis/geo_schelling/server.py b/gis/geo_schelling/server.py deleted file mode 100644 index c8e85d09..00000000 --- a/gis/geo_schelling/server.py +++ /dev/null @@ -1,45 +0,0 @@ -import mesa -import mesa_geo as mg -import xyzservices.providers as xyz -from model import GeoSchelling - - -class HappyElement(mesa.visualization.TextElement): - """ - Display a text count of how many happy agents there are. - """ - - def __init__(self): - pass - - def render(self, model): - return "Happy agents: " + str(model.happy) - - -model_params = { - "density": mesa.visualization.Slider("Agent density", 0.6, 0.1, 1.0, 0.1), - "minority_pc": mesa.visualization.Slider("Fraction minority", 0.2, 0.00, 1.0, 0.05), - "export_data": mesa.visualization.Checkbox("Export data after simulation", False), -} - - -def schelling_draw(agent): - """ - Portrayal Method for canvas - """ - portrayal = {} - if agent.atype is None: - portrayal["color"] = "Grey" - elif agent.atype == 0: - portrayal["color"] = "Red" - else: - portrayal["color"] = "Blue" - return portrayal - - -happy_element = HappyElement() -map_element = mg.visualization.MapModule(schelling_draw, tiles=xyz.CartoDB.Positron) -happy_chart = mesa.visualization.ChartModule([{"Label": "happy", "Color": "Black"}]) -server = mesa.visualization.ModularServer( - GeoSchelling, [map_element, happy_element, happy_chart], "Schelling", model_params -) diff --git a/gis/geo_schelling_points/README.md b/gis/geo_schelling_points/README.md index d80549d2..d4b67fe4 100644 --- a/gis/geo_schelling_points/README.md +++ b/gis/geo_schelling_points/README.md @@ -14,12 +14,12 @@ The NUTS-2 regions are considered as a shared definition of neighborhood among a There are two types of GeoAgents: people and regions. Each person resides in a randomly assigned region, and checks the color ratio of its region against a pre-defined "happiness" threshold at every time step. If the ratio falls below a certain threshold (e.g., 40%), the agent is found to be "unhappy", and randomly moves to another region. People are represented as points, with locations randomly chosen within their regions. The color of a region depends on the color of the majority population it contains (i.e., point in polygon calculations). -## How to run +## How to Run -To run the model interactively, run `mesa runserver` in this directory. e.g. +To run the model interactively, run `solara run app.py` in this directory. e.g. ```bash -mesa runserver +solara run app.py ``` -Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/) and press `Start`. +Then open your browser to [http://127.0.0.1:8765/](http://127.0.0.1:8765/) and press the play button `▶`. diff --git a/gis/geo_schelling_points/app.py b/gis/geo_schelling_points/app.py new file mode 100644 index 00000000..2fe61956 --- /dev/null +++ b/gis/geo_schelling_points/app.py @@ -0,0 +1,46 @@ +import solara +from geo_schelling_points.agents import PersonAgent, RegionAgent +from geo_schelling_points.model import GeoSchellingPoints +from mesa.visualization import Slider, SolaraViz, make_plot_measure +from mesa_geo.visualization import make_geospace_leaflet + + +def make_plot_happiness(model): + return solara.Markdown(f"**Happy agents: {model.happy}**") + + +model_params = { + "red_percentage": Slider("% red", 0.5, 0.00, 1.0, 0.05), + "similarity_threshold": Slider("% similar wanted", 0.5, 0.00, 1.0, 0.05), +} + + +def schelling_draw(agent): + portrayal = {} + if isinstance(agent, RegionAgent): + if agent.red_cnt > agent.blue_cnt: + portrayal["color"] = "Red" + elif agent.red_cnt < agent.blue_cnt: + portrayal["color"] = "Blue" + else: + portrayal["color"] = "Grey" + elif isinstance(agent, PersonAgent): + portrayal["radius"] = 1 + portrayal["shape"] = "circle" + portrayal["color"] = "Red" if agent.is_red else "Blue" + return portrayal + + +model = GeoSchellingPoints() +page = SolaraViz( + model, + [ + make_geospace_leaflet(schelling_draw, zoom=4), + make_plot_happiness, + make_plot_measure(["happy", "unhappy"]), + ], + model_params=model_params, + name="GeoSchellingPoints", +) + +page # noqa diff --git a/gis/geo_schelling_points/geo_schelling_points/__init__.py b/gis/geo_schelling_points/geo_schelling_points/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/gis/geo_schelling_points/geo_schelling_points/model.py b/gis/geo_schelling_points/geo_schelling_points/model.py index 91da59f1..0f2b3f34 100644 --- a/gis/geo_schelling_points/geo_schelling_points/model.py +++ b/gis/geo_schelling_points/geo_schelling_points/model.py @@ -1,6 +1,8 @@ import random from pathlib import Path +import geopandas as gpd +import libpysal import mesa import mesa_geo as mg @@ -10,6 +12,21 @@ script_directory = Path(__file__).resolve().parent +def get_largest_connected_components(gdf): + """Get the largest connected component of a GeoDataFrame.""" + # create spatial weights matrix + W = libpysal.weights.Queen.from_dataframe( + gdf, use_index=True, silence_warnings=True + ) + # get component labels + gdf["component"] = W.component_labels + # get the largest component + largest_component = gdf["component"].value_counts().idxmax() + # subset the GeoDataFrame + gdf = gdf[gdf["component"] == largest_component] + return gdf + + class GeoSchellingPoints(mesa.Model): def __init__(self, red_percentage=0.5, similarity_threshold=0.5): super().__init__() @@ -26,7 +43,9 @@ def __init__(self, red_percentage=0.5, similarity_threshold=0.5): # Set up the grid with patches for every NUTS region ac = mg.AgentCreator(RegionAgent, model=self) data_path = script_directory / "../data/nuts_rg_60M_2013_lvl_2.geojson" - regions = ac.from_file(data_path) + regions_gdf = gpd.read_file(data_path) + regions_gdf = get_largest_connected_components(regions_gdf) + regions = ac.from_GeoDataFrame(regions_gdf) self.space.add_regions(regions) for region in regions: diff --git a/gis/geo_schelling_points/geo_schelling_points/server.py b/gis/geo_schelling_points/geo_schelling_points/server.py deleted file mode 100644 index b86ff744..00000000 --- a/gis/geo_schelling_points/geo_schelling_points/server.py +++ /dev/null @@ -1,59 +0,0 @@ -import mesa -import mesa_geo as mg - -from .agents import PersonAgent, RegionAgent -from .model import GeoSchellingPoints - - -class HappyElement(mesa.visualization.TextElement): - def render(self, model): - return f"Happy agents: {model.happy}" - - -class UnhappyElement(mesa.visualization.TextElement): - def render(self, model): - return f"Unhappy agents: {model.unhappy}" - - -model_params = { - "red_percentage": mesa.visualization.Slider("% red", 0.5, 0.00, 1.0, 0.05), - "similarity_threshold": mesa.visualization.Slider( - "% similar wanted", 0.5, 0.00, 1.0, 0.05 - ), -} - - -def schelling_draw(agent): - portrayal = {} - if isinstance(agent, RegionAgent): - if agent.red_cnt > agent.blue_cnt: - portrayal["color"] = "Red" - elif agent.red_cnt < agent.blue_cnt: - portrayal["color"] = "Blue" - else: - portrayal["color"] = "Grey" - elif isinstance(agent, PersonAgent): - portrayal["radius"] = 1 - portrayal["shape"] = "circle" - portrayal["color"] = "Red" if agent.is_red else "Blue" - return portrayal - - -happy_element = HappyElement() -unhappy_element = UnhappyElement() -map_element = mg.visualization.MapModule(schelling_draw, [52, 12], 4) -happy_chart = mesa.visualization.ChartModule( - [ - {"Label": "unhappy", "Color": "Orange"}, - { - "Label": "happy", - "Color": "Green", - }, - ] -) -server = mesa.visualization.ModularServer( - GeoSchellingPoints, - [map_element, happy_element, unhappy_element, happy_chart], - "Schelling", - model_params, -) diff --git a/gis/geo_schelling_points/requirements.txt b/gis/geo_schelling_points/requirements.txt index 96935875..6d998684 100644 --- a/gis/geo_schelling_points/requirements.txt +++ b/gis/geo_schelling_points/requirements.txt @@ -1 +1 @@ -mesa-geo~=0.7 +mesa-geo~=0.9.0a0 diff --git a/gis/geo_schelling_points/run.py b/gis/geo_schelling_points/run.py deleted file mode 100644 index 637abd36..00000000 --- a/gis/geo_schelling_points/run.py +++ /dev/null @@ -1,3 +0,0 @@ -from geo_schelling_points.server import server - -server.launch() diff --git a/gis/geo_sir/README.md b/gis/geo_sir/README.md index 0cd2ca73..8ff6f65e 100644 --- a/gis/geo_sir/README.md +++ b/gis/geo_sir/README.md @@ -15,12 +15,12 @@ Susceptible agents (those who have never been infected) who come in proximity wi Neighbourhood agents represent neighbourhoods in the Toronto, and become hot-spots (colored red) if there are infected agents inside them. Data obtained from [this link](http://adamw523.com/toronto-geojson/). -## How to run +## How to Run -To run the model interactively, run `mesa runserver` in this directory. e.g. +To run the model interactively, run `solara run app.py` in this directory. e.g. ```bash -mesa runserver +solara run app.py ``` -Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/) and press `Start`. +Then open your browser to [http://127.0.0.1:8765/](http://127.0.0.1:8765/) and press the play button `▶`. diff --git a/gis/geo_sir/app.py b/gis/geo_sir/app.py new file mode 100644 index 00000000..2c8177cb --- /dev/null +++ b/gis/geo_sir/app.py @@ -0,0 +1,42 @@ +from geo_sir.agents import PersonAgent +from geo_sir.model import GeoSir +from mesa.visualization import Slider, SolaraViz, make_plot_measure +from mesa_geo.visualization import make_geospace_leaflet + +model_params = { + "pop_size": Slider("Population size", 30, 10, 100, 10), + "init_infected": Slider("Fraction initial infection", 0.2, 0.00, 1.0, 0.05), + "exposure_distance": Slider("Exposure distance", 500, 100, 1000, 100), +} + + +def infected_draw(agent): + """ + Portrayal Method for canvas + """ + portrayal = {} + if isinstance(agent, PersonAgent): + portrayal["radius"] = "2" + if agent.atype in ["hotspot", "infected"]: + portrayal["color"] = "Red" + elif agent.atype in ["safe", "susceptible"]: + portrayal["color"] = "Green" + elif agent.atype in ["recovered"]: + portrayal["color"] = "Blue" + elif agent.atype in ["dead"]: + portrayal["color"] = "Black" + return portrayal + + +model = GeoSir() +page = SolaraViz( + model, + [ + make_geospace_leaflet(infected_draw, zoom=12), + make_plot_measure(["infected", "susceptible", "recovered", "dead"]), + ], + name="Basic agent-based SIR model", + model_params=model_params, +) + +page # noqa diff --git a/gis/geo_sir/geo_sir/__init__.py b/gis/geo_sir/geo_sir/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/gis/geo_sir/agents.py b/gis/geo_sir/geo_sir/agents.py similarity index 100% rename from gis/geo_sir/agents.py rename to gis/geo_sir/geo_sir/agents.py diff --git a/gis/geo_sir/model.py b/gis/geo_sir/geo_sir/model.py similarity index 98% rename from gis/geo_sir/model.py rename to gis/geo_sir/geo_sir/model.py index b7baa2fa..a9c77ea0 100644 --- a/gis/geo_sir/model.py +++ b/gis/geo_sir/geo_sir/model.py @@ -13,7 +13,7 @@ class GeoSir(mesa.Model): """Model class for a simplistic infection model.""" # Geographical parameters for desired map - geojson_regions = script_directory / "data/TorontoNeighbourhoods.geojson" + geojson_regions = script_directory / "../data/TorontoNeighbourhoods.geojson" unique_id = "HOODNUM" def __init__( diff --git a/gis/geo_sir/requirements.txt b/gis/geo_sir/requirements.txt index 96935875..6d998684 100644 --- a/gis/geo_sir/requirements.txt +++ b/gis/geo_sir/requirements.txt @@ -1 +1 @@ -mesa-geo~=0.7 +mesa-geo~=0.9.0a0 diff --git a/gis/geo_sir/run.py b/gis/geo_sir/run.py deleted file mode 100644 index a25f3b12..00000000 --- a/gis/geo_sir/run.py +++ /dev/null @@ -1,3 +0,0 @@ -from server import server - -server.launch() diff --git a/gis/geo_sir/server.py b/gis/geo_sir/server.py deleted file mode 100644 index 6f336ab0..00000000 --- a/gis/geo_sir/server.py +++ /dev/null @@ -1,63 +0,0 @@ -import mesa -import mesa_geo as mg -from agents import PersonAgent -from model import GeoSir - - -class InfectedText(mesa.visualization.TextElement): - """ - Display a text count of how many steps have been taken - """ - - def __init__(self): - pass - - def render(self, model): - return "Steps: " + str(model.steps) - - -model_params = { - "pop_size": mesa.visualization.Slider("Population size", 30, 10, 100, 10), - "init_infected": mesa.visualization.Slider( - "Fraction initial infection", 0.2, 0.00, 1.0, 0.05 - ), - "exposure_distance": mesa.visualization.Slider( - "Exposure distance", 500, 100, 1000, 100 - ), -} - - -def infected_draw(agent): - """ - Portrayal Method for canvas - """ - portrayal = {} - if isinstance(agent, PersonAgent): - portrayal["radius"] = "2" - if agent.atype in ["hotspot", "infected"]: - portrayal["color"] = "Red" - elif agent.atype in ["safe", "susceptible"]: - portrayal["color"] = "Green" - elif agent.atype in ["recovered"]: - portrayal["color"] = "Blue" - elif agent.atype in ["dead"]: - portrayal["color"] = "Black" - return portrayal - - -infected_text = InfectedText() -map_element = mg.visualization.MapModule(infected_draw) -infected_chart = mesa.visualization.ChartModule( - [ - {"Label": "infected", "Color": "Red"}, - {"Label": "susceptible", "Color": "Green"}, - {"Label": "recovered", "Color": "Blue"}, - {"Label": "dead", "Color": "Black"}, - ] -) -server = mesa.visualization.ModularServer( - GeoSir, - [map_element, infected_text, infected_chart], - "Basic agent-based SIR model", - model_params, -)