Skip to content

Commit

Permalink
add uganda example
Browse files Browse the repository at this point in the history
  • Loading branch information
wang-boyu authored and rht committed Jul 26, 2022
1 parent a12f735 commit 93e8d60
Show file tree
Hide file tree
Showing 8 changed files with 215 additions and 0 deletions.
14 changes: 14 additions & 0 deletions examples/uganda/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Uganda Model
==============

An implementation of the [Uganda Example](https://github.com/abmgis/abmgis/tree/master/Chapter05-GIS/Models/UgandaExample) in Python, using [Mesa](https://github.com/projectmesa/mesa) and [Mesa-Geo](https://github.com/projectmesa/mesa-geo).

## How to run

To run the model interactively, run `mesa runserver` in this directory. e.g.

```bash
mesa runserver
```

Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/) and press `Start`.
Binary file added examples/uganda/data/clip.zip
Binary file not shown.
Binary file added examples/uganda/data/lake.zip
Binary file not shown.
Binary file added examples/uganda/data/popu.asc.gz
Binary file not shown.
3 changes: 3 additions & 0 deletions examples/uganda/run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from uganda.server import server

server.launch()
91 changes: 91 additions & 0 deletions examples/uganda/uganda/model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import math
import random
import uuid

import mesa
import numpy as np
from shapely.geometry import Point

from mesa_geo.geoagent import GeoAgent

from .space import UgandaArea


class Person(GeoAgent):
MOBILITY_RANGE_X = 0.0
MOBILITY_RANGE_Y = 0.0

def __init__(self, unique_id, model, geometry, crs, img_coord):
super().__init__(unique_id, model, geometry, crs)
self.img_coord = img_coord

def set_random_world_coord(self):
world_coord_point = Point(
self.model.space.population_layer.transform * self.img_coord
)
random_world_coord_x = world_coord_point.x + np.random.uniform(
-self.MOBILITY_RANGE_X, self.MOBILITY_RANGE_X
)
random_world_coord_y = world_coord_point.y + np.random.uniform(
-self.MOBILITY_RANGE_Y, self.MOBILITY_RANGE_Y
)
self.geometry = Point(random_world_coord_x, random_world_coord_y)

def step(self):
neighborhood = self.model.space.population_layer.get_neighborhood(
self.img_coord, moore=True
)
found = False
while neighborhood and not found:
next_img_coord = random.choice(neighborhood)
world_coord_point = Point(
self.model.space.population_layer.transform * next_img_coord
)
if world_coord_point.within(self.model.space.lake):
neighborhood.remove(next_img_coord)
continue
else:
found = True
self.img_coord = next_img_coord
self.set_random_world_coord()


class Uganda(mesa.Model):
def __init__(
self,
population_gzip_file="data/popu.asc.gz",
lake_zip_file="data/lake.zip",
world_zip_file="data/clip.zip",
):
super().__init__()
self.space = UgandaArea(crs="epsg:4326")
self.space.load_data(population_gzip_file, lake_zip_file, world_zip_file)
pixel_size_x, pixel_size_y = self.space.population_layer.resolution
Person.MOBILITY_RANGE_X = pixel_size_x / 2.0
Person.MOBILITY_RANGE_Y = pixel_size_y / 2.0

self.schedule = mesa.time.RandomActivation(self)
self._create_agents()

def _create_agents(self):
num_agents = 0
for cell in self.space.population_layer:
popu_round = math.ceil(cell.population)
if popu_round > 0:
for _ in range(popu_round):
num_agents += 1
point = Point(self.space.population_layer.transform * cell.indices)
if not point.within(self.space.lake):
person = Person(
unique_id=uuid.uuid4().int,
model=self,
crs=self.space.crs,
geometry=point,
img_coord=cell.indices,
)
person.set_random_world_coord()
self.space.add_agents(person)
self.schedule.add(person)

def step(self):
self.schedule.step()
47 changes: 47 additions & 0 deletions examples/uganda/uganda/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import mesa
from shapely.geometry import Point, Polygon

from mesa_geo.geoagent import GeoAgent
from mesa_geo.visualization.ModularVisualization import ModularServer
from mesa_geo.visualization.modules import MapModule

from .model import Uganda
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 agent_portrayal(agent):
if isinstance(agent, GeoAgent):
if isinstance(agent.geometry, Point):
return {
"stroke": False,
"color": "Green",
"radius": 2,
"fillOpacity": 0.3,
}
elif isinstance(agent.geometry, Polygon):
return {
"fillColor": "Blue",
"fillOpacity": 1.0,
}
elif isinstance(agent, UgandaCell):
return (agent.population, agent.population, agent.population, 1)


geospace_element = MapModule(
agent_portrayal,
[0.18357442221655898, 32.877823549000034],
12.9,
map_height=500,
map_width=500,
)
num_agents_element = NumAgentsElement()

server = ModularServer(Uganda, [geospace_element, num_agents_element], "Uganda Model")
60 changes: 60 additions & 0 deletions examples/uganda/uganda/space.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from __future__ import annotations

import gzip
import uuid

import geopandas as gpd
import mesa
import rasterio as rio

from mesa_geo.geoagent import GeoAgent
from mesa_geo.geospace import GeoSpace
from mesa_geo.raster_layers import Cell, RasterLayer


class UgandaCell(Cell):
population: float | None

def __init__(
self,
pos: mesa.space.Coordinate | None = None,
indices: mesa.space.Coordinate | None = None,
):
super().__init__(pos, indices)
self.population = None

def step(self):
pass


class Lake(GeoAgent):
pass


class UgandaArea(GeoSpace):
def __init__(self, crs):
super().__init__(crs=crs)

def load_data(self, population_gzip_file, lake_zip_file, world_zip_file):
world_size = gpd.GeoDataFrame.from_file(world_zip_file)
with gzip.open(population_gzip_file, "rb") as population_file:
with rio.open(population_file, "r") as population_data:
values = population_data.read()
_, height, width = values.shape
self.add_layer(
RasterLayer(
width,
height,
world_size.crs,
world_size.total_bounds,
cell_cls=UgandaCell,
)
)
self.population_layer.apply_raster(values, name="population")

self.lake = gpd.GeoDataFrame.from_file(lake_zip_file).geometry[0]
self.add_agents(GeoAgent(uuid.uuid4().int, None, self.lake, self.crs))

@property
def population_layer(self):
return self.layers[0]

0 comments on commit 93e8d60

Please sign in to comment.