Skip to content

Commit

Permalink
add agent point markers
Browse files Browse the repository at this point in the history
- add point markers options
- updates test
  • Loading branch information
tpike3 authored and rht committed Jul 15, 2024
1 parent 48f9652 commit 7f9b8df
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 26 deletions.
2 changes: 1 addition & 1 deletion mesa_geo/tile_layers.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class RasterWebTile:
kind: str = "raster_web_tile"

@classmethod
def from_xyzservices(cls, provider=xyzservices.TileProvider) -> RasterWebTile:
def from_xyzservices(cls, provider: xyzservices.TileProvider) -> RasterWebTile:
"""
Create a RasterWebTile from an xyzservices TileProvider.
Expand Down
59 changes: 53 additions & 6 deletions mesa_geo/visualization/leaflet_viz.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ def map(model, map_drawer, zoom, center_default):
scroll_wheel_zoom=True,
layers=[
ipyleaflet.TileLayer.element(url=base_map["url"]),
ipyleaflet.GeoJSON.element(data=layers["agents"]),
ipyleaflet.GeoJSON.element(data=layers["agents"][0]),
*layers["agents"][1],
],
)

Expand All @@ -53,7 +54,8 @@ def map_jupyter(model, map_drawer, zoom, center_default):
scroll_wheel_zoom=True,
layers=[
ipyleaflet.TileLayer.element(url=base_map["url"]),
ipyleaflet.GeoJSON.element(data=layers["agents"]),
ipyleaflet.GeoJSON.element(data=layers["agents"][0]),
*layers["agents"][1],
],
)

Expand All @@ -67,7 +69,6 @@ class LeafletViz:
"""

style: dict[str, LeafletOption] | None = None
pointToLayer: dict[str, LeafletOption] | None = None # noqa: N815
popupProperties: dict[str, LeafletOption] | None = None # noqa: N815


Expand Down Expand Up @@ -183,31 +184,77 @@ def _render_layers(self, model):
]
return layers

def _get_marker(self, location, properties):
"""
takes point objects and transforms them to ipyleaflet marker objects
allowed marker types are point marker types from ipyleaflet
https://ipyleaflet.readthedocs.io/en/latest/layers/index.html
default is circle with radius 5
Parameters
----------
location: iterable
iterable of location in models geometry
properties : dict
properties passed in through agent portrayal
Returns
-------
ipyleaflet marker element
"""

if "marker_type" not in properties: # make circle default marker type
properties["marker_type"] = "Circle"
properties["radius"] = 5

marker = properties["marker_type"]
if marker == "Circle":
return ipyleaflet.Circle(location=location, **properties)
elif marker == "CircleMarker":
return ipyleaflet.CircleMarker(location=location, **properties)
elif marker == "Marker" or marker == "Icon" or marker == "AwesomeIcon":
return ipyleaflet.Marker(location=location, **properties)
else:
raise ValueError(
f"Unsupported marker type:{marker}",
)

def _render_agents(self, model):
feature_collection = {"type": "FeatureCollection", "features": []}
point_markers = []
agent_portrayal = {}
for agent in model.space.agents:
transformed_geometry = agent.get_transformed_geometry(
model.space.transformer
)
agent_portrayal = {}

if self.portrayal_method:
properties = self.portrayal_method(agent)
agent_portrayal = LeafletViz(
popupProperties=properties.pop("description", None)
)
if isinstance(agent.geometry, Point):
agent_portrayal.pointToLayer = properties
location = mapping(transformed_geometry)
# for some reason points are reversed
location = (location["coordinates"][1], location["coordinates"][0])
point_markers.append(self._get_marker(location, properties))
else:
agent_portrayal.style = properties
agent_portrayal = dataclasses.asdict(
agent_portrayal,
dict_factory=lambda x: {k: v for (k, v) in x if v is not None},
)

feature_collection["features"].append(
{
"type": "Feature",
"geometry": mapping(transformed_geometry),
"properties": agent_portrayal,
}
)
return feature_collection
return [feature_collection, point_markers]
63 changes: 44 additions & 19 deletions tests/test_MapModule.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import mesa
import numpy as np
import xyzservices.providers as xyz
from ipyleaflet import Circle, CircleMarker, Marker
from shapely.geometry import LineString, Point, Polygon

import mesa_geo as mg
Expand Down Expand Up @@ -41,28 +42,42 @@ def tearDown(self) -> None:
pass

def test_render_point_agents(self):
# test length point agents and Circle marker as default
map_module = mgv.leaflet_viz.MapModule(
portrayal_method=lambda x: {"color": "Red", "radius": 7},
portrayal_method=lambda x: {"color": "Green"},
view=None,
zoom=3,
tiles=xyz.OpenStreetMap.Mapnik,
)
self.model.space.add_agents(self.point_agents)
self.assertDictEqual(
map_module.render(self.model).get("agents"),
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {"type": "Point", "coordinates": (1.0, 1.0)},
"properties": {"pointToLayer": {"color": "Red", "radius": 7}},
}
]
* len(self.point_agents),
self.assertEqual(len(map_module.render(self.model).get("agents")[1]), 7)
self.assertIsInstance(map_module.render(self.model).get("agents")[1][3], Circle)
# test CircleMarker option
map_module = mgv.leaflet_viz.MapModule(
portrayal_method=lambda x: {
"marker_type": "CircleMarker",
"color": "Green",
},
view=None,
zoom=3,
tiles=xyz.OpenStreetMap.Mapnik,
)
self.model.space.add_agents(self.point_agents)
self.assertIsInstance(
map_module.render(self.model).get("agents")[1][3], CircleMarker
)

# test Marker option
map_module = mgv.leaflet_viz.MapModule(
portrayal_method=lambda x: {"marker_type": "Icon", "color": "Green"},
view=None,
zoom=3,
tiles=xyz.OpenStreetMap.Mapnik,
)
self.model.space.add_agents(self.point_agents)
self.assertEqual(len(map_module.render(self.model).get("agents")[1]), 7)
self.assertIsInstance(map_module.render(self.model).get("agents")[1][3], Marker)
# test popupProperties for Point
map_module = mgv.leaflet_viz.MapModule(
portrayal_method=lambda x: {
"color": "Red",
Expand All @@ -75,15 +90,14 @@ def test_render_point_agents(self):
)
self.model.space.add_agents(self.point_agents)
self.assertDictEqual(
map_module.render(self.model).get("agents"),
map_module.render(self.model).get("agents")[0],
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {"type": "Point", "coordinates": (1.0, 1.0)},
"properties": {
"pointToLayer": {"color": "Red", "radius": 7},
"popupProperties": "popupMsg",
},
}
Expand All @@ -92,6 +106,17 @@ def test_render_point_agents(self):
},
)

# test ValueError if not known markertype
map_module = mgv.leaflet_viz.MapModule(
portrayal_method=lambda x: {"marker_type": "Hexagon", "color": "Green"},
view=None,
zoom=3,
tiles=xyz.OpenStreetMap.Mapnik,
)
self.model.space.add_agents(self.point_agents)
with self.assertRaises(ValueError):
map_module.render(self.model)

def test_render_line_agents(self):
map_module = mgv.leaflet_viz.MapModule(
portrayal_method=lambda x: {"color": "#3388ff", "weight": 7},
Expand All @@ -101,7 +126,7 @@ def test_render_line_agents(self):
)
self.model.space.add_agents(self.line_agents)
self.assertDictEqual(
map_module.render(self.model).get("agents"),
map_module.render(self.model).get("agents")[0],
{
"type": "FeatureCollection",
"features": [
Expand Down Expand Up @@ -130,7 +155,7 @@ def test_render_line_agents(self):
)
self.model.space.add_agents(self.line_agents)
self.assertDictEqual(
map_module.render(self.model).get("agents"),
map_module.render(self.model).get("agents")[0],
{
"type": "FeatureCollection",
"features": [
Expand Down Expand Up @@ -161,7 +186,7 @@ def test_render_polygon_agents(self):
)
self.model.space.add_agents(self.polygon_agents)
self.assertDictEqual(
map_module.render(self.model).get("agents"),
map_module.render(self.model).get("agents")[0],
{
"type": "FeatureCollection",
"features": [
Expand Down Expand Up @@ -194,7 +219,7 @@ def test_render_polygon_agents(self):
)
self.model.space.add_agents(self.polygon_agents)
self.assertDictEqual(
map_module.render(self.model).get("agents"),
map_module.render(self.model).get("agents")[0],
{
"type": "FeatureCollection",
"features": [
Expand Down

0 comments on commit 7f9b8df

Please sign in to comment.