diff --git a/gis/geo_schelling/app.py b/gis/geo_schelling/app.py index 8ec379a4..82c69a86 100644 --- a/gis/geo_schelling/app.py +++ b/gis/geo_schelling/app.py @@ -34,8 +34,8 @@ def schelling_draw(agent): model, [ make_geospace_leaflet(schelling_draw, zoom=4), - make_plot_happiness, make_plot_measure(["happy"]), + make_plot_happiness, ], model_params=model_params, name="GeoSchelling", diff --git a/gis/geo_schelling_points/app.py b/gis/geo_schelling_points/app.py index 2fe61956..e025b996 100644 --- a/gis/geo_schelling_points/app.py +++ b/gis/geo_schelling_points/app.py @@ -36,8 +36,8 @@ def schelling_draw(agent): model, [ make_geospace_leaflet(schelling_draw, zoom=4), - make_plot_happiness, make_plot_measure(["happy", "unhappy"]), + make_plot_happiness, ], model_params=model_params, name="GeoSchellingPoints", diff --git a/gis/population/README.md b/gis/population/README.md index 4ac4af39..852ab90f 100644 --- a/gis/population/README.md +++ b/gis/population/README.md @@ -14,15 +14,15 @@ The GeoSpace consists of both a raster and a vector layer. The raster layer cont The GeoAgents are people, created based on the population data. As this is a simple example model, the agents only move randomly to neighboring cells at each time step. To make the simulation more realistic and visually appealing, the agents in the same cell have a randomized position within the cell, so that they don’t stand on top of each other at exactly the same coordinate. -## 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 `▶`. ## License diff --git a/gis/population/population/server.py b/gis/population/app.py similarity index 55% rename from gis/population/population/server.py rename to gis/population/app.py index c598ed3a..404b39ff 100644 --- a/gis/population/population/server.py +++ b/gis/population/app.py @@ -1,17 +1,14 @@ -import mesa import mesa_geo as mg +import solara +from mesa.visualization import SolaraViz +from mesa_geo.visualization import make_geospace_leaflet +from population.model import Population +from population.space import UgandaCell from shapely.geometry import Point, Polygon -from .model import Population -from .space import UgandaCell - -class NumAgentsElement(mesa.visualization.TextElement): - def __init__(self): - super().__init__() - - def render(self, model): - return f"Number of Agents: {len(model.space.agents)}" +def make_plot_num_agents(model): + return solara.Markdown(f"**Number of Agents: {len(model.space.agents)}**") def agent_portrayal(agent): @@ -32,9 +29,14 @@ def agent_portrayal(agent): return (agent.population, agent.population, agent.population, 1) -geospace_element = mg.visualization.MapModule(agent_portrayal) -num_agents_element = NumAgentsElement() - -server = mesa.visualization.ModularServer( - Population, [geospace_element, num_agents_element], "Population Model" +model = Population() +page = SolaraViz( + model, + [ + make_geospace_leaflet(agent_portrayal), + make_plot_num_agents, + ], + name="Population Model", ) + +page # noqa diff --git a/gis/population/population/__init__.py b/gis/population/population/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/gis/population/requirements.txt b/gis/population/requirements.txt index 96935875..30d02edc 100644 --- a/gis/population/requirements.txt +++ b/gis/population/requirements.txt @@ -1 +1 @@ -mesa-geo~=0.7 +mesa-geo~=0.9.0a1 diff --git a/gis/population/run.py b/gis/population/run.py deleted file mode 100644 index 00246200..00000000 --- a/gis/population/run.py +++ /dev/null @@ -1,3 +0,0 @@ -from population.server import server - -server.launch() diff --git a/gis/rainfall/README.md b/gis/rainfall/README.md index 61947b46..41fa2832 100644 --- a/gis/rainfall/README.md +++ b/gis/rainfall/README.md @@ -14,15 +14,15 @@ The GeoSpace contains a raster layer representing elevations. It is this elevati In this example, the raindrops are the GeoAgents. At each time step, raindrops are randomly created across the landscape to simulate rainfall. The raindrops flow from cells of higher elevation to lower elevation based on their eight surrounding cells (i.e., Moore neighbourhood). The raindrop also has its own height, which allows them to accumulate, gain height and flow if they are trapped at places such as potholes, pools, or depressions. When they reach the boundary of the GeoSpace, they are removed from the model as outflow. -## 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 `▶`. ## License diff --git a/gis/rainfall/app.py b/gis/rainfall/app.py new file mode 100644 index 00000000..32af70a0 --- /dev/null +++ b/gis/rainfall/app.py @@ -0,0 +1,44 @@ +from typing import Tuple + +from mesa.visualization import Slider, SolaraViz, make_plot_measure +from mesa_geo.visualization import make_geospace_leaflet +from rainfall.model import Rainfall +from rainfall.space import LakeCell + +model_params = { + "rain_rate": Slider("rain rate", 500, 0, 500, 5), + "water_height": Slider("water height", 5, 1, 5, 1), + "num_steps": Slider("total number of steps", 20, 1, 100, 1), + "export_data": False, +} + + +def cell_portrayal(cell: LakeCell) -> Tuple[float, float, float, float]: + if cell.water_level == 0: + return cell.elevation, cell.elevation, cell.elevation, 1 + else: + # return a blue color gradient based on the normalized water level + # from the lowest water level colored as RGBA: (74, 141, 255, 1) + # to the highest water level colored as RGBA: (0, 0, 255, 1) + return ( + (1 - cell.water_level_normalized) * 74, + (1 - cell.water_level_normalized) * 141, + 255, + 1, + ) + + +model = Rainfall() +page = SolaraViz( + model, + [ + make_geospace_leaflet(cell_portrayal, zoom=11), + make_plot_measure( + ["Total Amount of Water", "Total Contained", "Total Outflow"] + ), + ], + name="Rainfall Model", + model_params=model_params, +) + +page # noqa diff --git a/gis/rainfall/rainfall/__init__.py b/gis/rainfall/rainfall/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/gis/rainfall/rainfall/server.py b/gis/rainfall/rainfall/server.py deleted file mode 100644 index 1c92e6dd..00000000 --- a/gis/rainfall/rainfall/server.py +++ /dev/null @@ -1,47 +0,0 @@ -from typing import Tuple - -import mesa -import mesa_geo as mg - -from .model import Rainfall -from .space import LakeCell - -model_params = { - "rain_rate": mesa.visualization.Slider("rain rate", 500, 0, 500, 5), - "water_height": mesa.visualization.Slider("water height", 5, 1, 5, 1), - "num_steps": mesa.visualization.Slider("total number of steps", 20, 1, 100, 1), - "export_data": mesa.visualization.Checkbox("export data after simulation", False), -} - - -def cell_portrayal(cell: LakeCell) -> Tuple[float, float, float, float]: - if cell.water_level == 0: - return cell.elevation, cell.elevation, cell.elevation, 1 - else: - # return a blue color gradient based on the normalized water level - # from the lowest water level colored as RGBA: (74, 141, 255, 1) - # to the highest water level colored as RGBA: (0, 0, 255, 1) - return ( - (1 - cell.water_level_normalized) * 74, - (1 - cell.water_level_normalized) * 141, - 255, - 1, - ) - - -map_module = mg.visualization.MapModule( - portrayal_method=cell_portrayal, - map_height=341, - map_width=498, -) -water_chart = mesa.visualization.ChartModule( - [ - {"Label": "Total Amount of Water", "Color": "Black"}, - {"Label": "Total Contained", "Color": "Blue"}, - {"Label": "Total Outflow", "Color": "Orange"}, - ] -) - -server = mesa.visualization.ModularServer( - Rainfall, [map_module, water_chart], "Rainfall Model", model_params -) diff --git a/gis/rainfall/requirements.txt b/gis/rainfall/requirements.txt index 96935875..30d02edc 100644 --- a/gis/rainfall/requirements.txt +++ b/gis/rainfall/requirements.txt @@ -1 +1 @@ -mesa-geo~=0.7 +mesa-geo~=0.9.0a1 diff --git a/gis/rainfall/run.py b/gis/rainfall/run.py deleted file mode 100644 index 77a56d26..00000000 --- a/gis/rainfall/run.py +++ /dev/null @@ -1,3 +0,0 @@ -from rainfall.server import server - -server.launch() diff --git a/gis/urban_growth/README.md b/gis/urban_growth/README.md index 33b9497a..d99b1a9f 100644 --- a/gis/urban_growth/README.md +++ b/gis/urban_growth/README.md @@ -6,15 +6,15 @@ This is an implementation of the [UrbanGrowth Model](https://github.com/abmgis/abmgis/tree/master/Chapter06-IntegratingABMandGIS/Models/UrbanGrowth) in Python, using [Mesa](https://github.com/projectmesa/mesa) and [Mesa-Geo](https://github.com/projectmesa/mesa-geo). -## 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 `▶`. ## License diff --git a/gis/urban_growth/app.py b/gis/urban_growth/app.py new file mode 100644 index 00000000..9c3ffc59 --- /dev/null +++ b/gis/urban_growth/app.py @@ -0,0 +1,47 @@ +from typing import Tuple + +import solara +from mesa.visualization import Slider, SolaraViz, make_plot_measure +from mesa_geo.visualization import make_geospace_leaflet +from urban_growth.model import UrbanGrowth +from urban_growth.space import UrbanCell + + +def cell_portrayal(cell: UrbanCell) -> Tuple[float, float, float, float]: + if cell.urban: + if cell.old_urbanized: + return 0, 0, 255, 1 + else: + return 255, 0, 0, 1 + else: + return 0, 0, 0, 0 + + +def make_plot_urbanized(model): + return solara.Markdown(f"**Percentage Urbanized: {model.pct_urbanized:.2f}%**") + + +model_params = { + "max_coefficient": 100, + "dispersion_coefficient": Slider("dispersion_coefficient", 20, 0, 100, 1), + "spread_coefficient": Slider("spread_coefficient", 27, 0, 100, 1), + "breed_coefficient": Slider("breed_coefficient", 5, 0, 100, 1), + "rg_coefficient": Slider("rg_coefficient", 10, 0, 100, 1), + "slope_coefficient": Slider("slope_coefficient", 50, 0, 100, 1), + "critical_slope": Slider("critical_slope", 25, 0, 100, 1), + "road_influence": False, +} + +model = UrbanGrowth() +page = SolaraViz( + model, + [ + make_geospace_leaflet(cell_portrayal, zoom=12.1), + make_plot_measure(["Percentage Urbanized"]), + make_plot_urbanized, + ], + name="Urban Growth Model", + model_params=model_params, +) + +page # noqa diff --git a/gis/urban_growth/requirements.txt b/gis/urban_growth/requirements.txt index 96935875..301b776a 100644 --- a/gis/urban_growth/requirements.txt +++ b/gis/urban_growth/requirements.txt @@ -1 +1 @@ -mesa-geo~=0.7 +mesa-geo~=0.9.0a1 \ No newline at end of file diff --git a/gis/urban_growth/run.py b/gis/urban_growth/run.py deleted file mode 100644 index 7291302e..00000000 --- a/gis/urban_growth/run.py +++ /dev/null @@ -1,3 +0,0 @@ -from urban_growth.server import server - -server.launch() diff --git a/gis/urban_growth/urban_growth/__init__.py b/gis/urban_growth/urban_growth/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/gis/urban_growth/urban_growth/server.py b/gis/urban_growth/urban_growth/server.py deleted file mode 100644 index 5edfd6e0..00000000 --- a/gis/urban_growth/urban_growth/server.py +++ /dev/null @@ -1,63 +0,0 @@ -from typing import Tuple - -import mesa -import mesa_geo as mg - -from .model import UrbanGrowth -from .space import UrbanCell - - -def cell_portrayal(cell: UrbanCell) -> Tuple[float, float, float, float]: - if cell.urban: - if cell.old_urbanized: - return 0, 0, 255, 1 - else: - return 255, 0, 0, 1 - else: - return 0, 0, 0, 0 - - -class UrbanizedText(mesa.visualization.TextElement): - def render(self, model): - return f"Percentage Urbanized: {model.pct_urbanized:.2f}%" - - -model_params = { - "max_coefficient": mesa.visualization.NumberInput("max_coefficient", 100), - "dispersion_coefficient": mesa.visualization.Slider( - "dispersion_coefficient", 20, 0, 100, 1 - ), - "spread_coefficient": mesa.visualization.Slider( - "spread_coefficient", 27, 0, 100, 1 - ), - "breed_coefficient": mesa.visualization.Slider("breed_coefficient", 5, 0, 100, 1), - "rg_coefficient": mesa.visualization.Slider("rg_coefficient", 10, 0, 100, 1), - "slope_coefficient": mesa.visualization.Slider("slope_coefficient", 50, 0, 100, 1), - "critical_slope": mesa.visualization.Slider("critical_slope", 25, 0, 100, 1), - "road_influence": mesa.visualization.Choice( - "road_influence", False, choices=[True, False] - ), -} - - -map_module = mg.visualization.MapModule( - portrayal_method=cell_portrayal, - view=[12.904598815296707, -8.027435210420451], - zoom=12.1, - map_height=394, - map_width=531, - scale_options={"imperial": False}, -) -urbanized_text = UrbanizedText() -urbanized_chart = mesa.visualization.ChartModule( - [ - {"Label": "Percentage Urbanized", "Color": "Black"}, - ] -) - -server = mesa.visualization.ModularServer( - UrbanGrowth, - [map_module, urbanized_text, urbanized_chart], - "Urban Growth Model", - model_params, -)