-
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(project): ♻️ Completely compatible to Mesa 3.0 framework
- Loading branch information
Showing
5 changed files
with
244 additions
and
46 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
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
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
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,175 @@ | ||
#!/usr/bin/env python 3.11.0 | ||
# -*-coding:utf-8 -*- | ||
# @Author : Shuang (Twist) Song | ||
# @Contact : [email protected] | ||
# GitHub : https://github.com/SongshGeo | ||
# Website: https://cv.songshgeo.com/ | ||
|
||
from typing import Any, Callable | ||
|
||
import matplotlib.pyplot as plt | ||
import numpy as np | ||
from matplotlib.axes import Axes | ||
from matplotlib.cm import ScalarMappable | ||
from matplotlib.colors import LinearSegmentedColormap, Normalize, to_rgba | ||
from matplotlib.figure import Figure | ||
from mesa.visualization.mpl_space_drawing import draw_orthogonal_grid | ||
from mesa.visualization.utils import update_counter | ||
from xarray import DataArray | ||
|
||
from abses.main import MainModel | ||
from abses.patch import PatchModule | ||
|
||
try: | ||
import solara | ||
except ImportError as e: | ||
raise ImportError( | ||
"`solara` is not installed, please install it via `pip install solara`" | ||
) from e | ||
|
||
|
||
def draw_property_layers( | ||
space: PatchModule, | ||
propertylayer_portrayal: dict[str, dict[str, Any]], | ||
ax: Axes, | ||
): | ||
"""Draw PropertyLayers on the given axes. | ||
Args: | ||
space (mesa.space._Grid): The space containing the PropertyLayers. | ||
propertylayer_portrayal (dict): the key is the name of the layer, the value is a dict with | ||
fields specifying how the layer is to be portrayed | ||
ax (matplotlib.axes.Axes): The axes to draw on. | ||
Notes: | ||
valid fields in in the inner dict of propertylayer_portrayal are "alpha", "vmin", "vmax", "color" or "colormap", and "colorbar" | ||
so you can do `{"some_layer":{"colormap":'viridis', 'alpha':.25, "colorbar":False}}` | ||
""" | ||
for layer_name, portrayal in propertylayer_portrayal.items(): | ||
layer: DataArray = space.get_xarray(layer_name) | ||
|
||
data = ( | ||
layer.data.astype(float) | ||
if layer.data.dtype == bool | ||
else layer.data | ||
) | ||
|
||
# Get portrayal properties, or use defaults | ||
alpha = portrayal.get("alpha", 1) | ||
vmin = portrayal.get("vmin", np.min(data)) | ||
vmax = portrayal.get("vmax", np.max(data)) | ||
colorbar = portrayal.get("colorbar", True) | ||
|
||
# Draw the layer | ||
if "color" in portrayal: | ||
rgba_color = to_rgba(portrayal["color"]) | ||
normalized_data = (data - vmin) / (vmax - vmin) | ||
rgba_data = np.full((*data.shape, 4), rgba_color) | ||
rgba_data[..., 3] *= normalized_data * alpha | ||
rgba_data = np.clip(rgba_data, 0, 1) | ||
cmap = LinearSegmentedColormap.from_list( | ||
layer_name, [(0, 0, 0, 0), (*rgba_color[:3], alpha)] | ||
) | ||
im = ax.imshow( | ||
rgba_data.transpose(1, 0, 2), | ||
origin="lower", | ||
) | ||
if colorbar: | ||
norm = Normalize(vmin=vmin, vmax=vmax) | ||
sm = ScalarMappable(norm=norm, cmap=cmap) | ||
sm.set_array([]) | ||
ax.figure.colorbar(sm, ax=ax, orientation="vertical") | ||
|
||
elif "colormap" in portrayal: | ||
cmap = portrayal.get("colormap", "viridis") | ||
if isinstance(cmap, list): | ||
cmap = LinearSegmentedColormap.from_list(layer_name, cmap) | ||
im = ax.imshow( | ||
data.T, | ||
cmap=cmap, | ||
alpha=alpha, | ||
vmin=vmin, | ||
vmax=vmax, | ||
origin="lower", | ||
) | ||
if colorbar: | ||
plt.colorbar(im, ax=ax, label=layer_name) | ||
else: | ||
raise ValueError( | ||
f"PropertyLayer {layer_name} portrayal must include 'color' or 'colormap'." | ||
) | ||
|
||
|
||
@solara.component | ||
def SpaceMatplotlib( | ||
model: MainModel, | ||
agent_portrayal, | ||
propertylayer_portrayal, | ||
dependencies: list[Any] | None = None, | ||
post_process: Callable | None = None, | ||
**space_drawing_kwargs, | ||
): | ||
"""Create a Matplotlib-based space visualization component.""" | ||
update_counter.get() | ||
if model.nature.major_layer is None: | ||
raise ValueError("Major layer is not set for the nature.") | ||
space = model.nature.major_layer | ||
|
||
fig = Figure() | ||
ax = fig.add_subplot() | ||
|
||
draw_orthogonal_grid( | ||
space, | ||
agent_portrayal, | ||
ax=ax, | ||
**space_drawing_kwargs, | ||
) | ||
|
||
if post_process is not None: | ||
post_process(ax) | ||
|
||
solara.FigureMatplotlib( | ||
fig, format="png", bbox_inches="tight", dependencies=dependencies | ||
) | ||
|
||
if propertylayer_portrayal: | ||
draw_property_layers(space, propertylayer_portrayal, ax=ax) | ||
|
||
|
||
def make_mpl_space_component( | ||
agent_portrayal: Callable | None = None, | ||
propertylayer_portrayal: dict | None = None, | ||
post_process: Callable | None = None, | ||
**space_drawing_kwargs, | ||
) -> Callable: | ||
"""Create a Matplotlib-based space visualization component. | ||
Args: | ||
agent_portrayal: Function to portray agents. | ||
propertylayer_portrayal: Dictionary of PropertyLayer portrayal specifications | ||
post_process : a callable that will be called with the Axes instance. Allows for fine tuning plots (e.g., control ticks) | ||
space_drawing_kwargs : additional keyword arguments to be passed on to the underlying space drawer function. See | ||
the functions for drawing the various spaces for further details. | ||
``agent_portrayal`` is called with an agent and should return a dict. Valid fields in this dict are "color", | ||
"size", "marker", "zorder", alpha, linewidths, and edgecolors. Other field are ignored and will result in a user warning. | ||
Returns: | ||
function: A function that creates a SpaceMatplotlib component | ||
""" | ||
if agent_portrayal is None: | ||
|
||
def agent_portrayal(a): | ||
return {} | ||
|
||
def MakeSpaceMatplotlib(model): | ||
return SpaceMatplotlib( | ||
model, | ||
agent_portrayal, | ||
propertylayer_portrayal, | ||
post_process=post_process, | ||
**space_drawing_kwargs, | ||
) | ||
|
||
return MakeSpaceMatplotlib |
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 |
---|---|---|
|
@@ -31,7 +31,7 @@ line_length = 79 | |
|
||
[tool.poetry] | ||
name = "abses" | ||
version = "0.7.0.alpha" | ||
version = "0.7.0" | ||
description = "ABSESpy makes it easier to build artificial Social-ecological systems with real GeoSpatial datasets and fully incorporate human behaviour." | ||
authors = ["Shuang Song <[email protected]>"] | ||
license = "Apache 2.0 License" | ||
|
@@ -51,8 +51,8 @@ abses = "hydra_plugins.abses_searchpath_plugin:ABSESpySearchPathPlugin" | |
python = ">=3.10,<3.12" | ||
netcdf4 = ">=1.6" | ||
hydra-core = "~1.3" | ||
mesa = {version = ">=3.0.0a4", allow-prereleases = true} | ||
mesa_geo = {git = "https://github.com/SongshGeo/mesa-geo.git", branch = "dev", develop = true} | ||
mesa = {version = ">=3.0.0", allow-prereleases = true} | ||
mesa_geo = {git = "https://github.com/SongshGeo/mesa-geo.git", branch = "dev"} | ||
xarray = ">=2023" | ||
fiona = ">1.8" | ||
loguru = ">=0.7" | ||
|
@@ -62,53 +62,54 @@ geopandas = "~0" | |
typing-extensions = "~4" | ||
fontawesome = ">=5" | ||
seaborn = ">=0.13" | ||
geocube = "^0.5.2" | ||
geocube = ">=0.5.0" | ||
solara = "*" | ||
|
||
[tool.poetry.group.dev.dependencies] | ||
pytest-clarity = "^1.0.1" | ||
pre-commit = "^3.0.1" | ||
scriv = "^1.2.0" | ||
pytest = "^7.2.1" | ||
sourcery = "^1.0.6" | ||
allure-pytest = "^2.13.2" | ||
pytest-sugar = "^0.9.7" | ||
ipykernel = "^6.25.1" | ||
jupyterlab = "^4.0.5" | ||
jupyterlab-execute-time = "^3.0.1" | ||
matplotlib = "^3.7.2" | ||
pytest-cov = "^4.1.0" | ||
flake8 = "^6.1.0" | ||
isort = "^5.12.0" | ||
nbstripout = "^0.6.2" | ||
pydocstyle = "^6.3.0" | ||
pre-commit-hooks = "^4.4.0" | ||
interrogate = "^1.5.0" | ||
mypy = "^1.6.1" | ||
bandit = "^1.7.5" | ||
black = "^23.9.1" | ||
pylint = "^3.0.1" | ||
tox = "^4.11.3" | ||
lxml = "^5.2.1" | ||
pytest-clarity = "*" | ||
pre-commit = ">=3.0.1" | ||
scriv = ">=1.2.0" | ||
pytest = ">=7.2.1" | ||
sourcery = ">=1.0.6" | ||
allure-pytest = ">=2.13.2" | ||
pytest-sugar = ">=0.9.7" | ||
ipykernel = ">=6.25.1" | ||
jupyterlab = ">=4.0.5" | ||
jupyterlab-execute-time = ">=3.0.1" | ||
matplotlib = ">=3.7.2" | ||
pytest-cov = ">=4.1.0" | ||
flake8 = ">=6.1.0" | ||
isort = ">=5.12.0" | ||
nbstripout = ">=0.6.2" | ||
pydocstyle = ">=6.3.0" | ||
pre-commit-hooks = ">=4.4.0" | ||
interrogate = ">=1.5.0" | ||
mypy = ">=1.6.1" | ||
bandit = ">=1.7.5" | ||
black = ">=23.9.1" | ||
pylint = ">=3.0.1" | ||
tox = ">=4.11.3" | ||
lxml = ">=5.2.1" | ||
|
||
|
||
[tool.poetry.group.docs.dependencies] | ||
mkdocs = "^1.5.3" | ||
mkdocs-material = "^9.4.2" | ||
mkdocs-git-revision-date-localized-plugin = "^1.2.0" | ||
mkdocs-minify-plugin = "^0.7.1" | ||
mkdocs-redirects = "^1.2.1" | ||
mkdocs-awesome-pages-plugin = "^2.9.2" | ||
mkdocs-git-authors-plugin = "^0.7.2" | ||
mkdocstrings = {extras = ["python"], version = "^0.24.0"} | ||
mkdocs-bibtex = "^2.11.0" | ||
mkdocs-macros-plugin = "^1.0.4" | ||
mkdocs-jupyter = "^0.24.5" | ||
mkdocs-callouts = "^1.9.1" | ||
mkdocs-glightbox = "^0.3.4" | ||
mike = "^2.0.0" | ||
mkdocs-exclude = "^1.0.2" | ||
mkdocs-simple-hooks = "^0.1.5" | ||
pymdown-extensions = "^10.7" | ||
mkdocs = ">=1.5.3" | ||
mkdocs-material = ">=9.4.2" | ||
mkdocs-git-revision-date-localized-plugin = ">=1.2.0" | ||
mkdocs-minify-plugin = ">=0.7.1" | ||
mkdocs-redirects = ">=1.2.1" | ||
mkdocs-awesome-pages-plugin = ">=2.9.2" | ||
mkdocs-git-authors-plugin = ">=0.7.2" | ||
mkdocstrings = {extras = ["python"], version = ">=0.24.0"} | ||
mkdocs-bibtex = ">=2.11.0" | ||
mkdocs-macros-plugin = ">=1.0.4" | ||
mkdocs-jupyter = ">=0.24.5" | ||
mkdocs-callouts = ">=1.9.1" | ||
mkdocs-glightbox = ">=0.3.4" | ||
mike = ">=2.0.0" | ||
mkdocs-exclude = ">=1.0.2" | ||
mkdocs-simple-hooks = ">=0.1.5" | ||
pymdown-extensions = ">=10.7" | ||
|
||
[build-system] | ||
requires = ["poetry-core"] | ||
|