Skip to content

Added inline comments and docstrings to geoplot.py for clarity (#65) #69

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 38 additions & 19 deletions geoplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
from string import Template
from agent_torch.core.helpers import get_by_path

# HTML template for the Cesium-powered visualization
# This template includes JavaScript for handling time-series data visualization using Cesium
geoplot_template = """
<!doctype html>
<html lang="en">
Expand Down Expand Up @@ -214,39 +216,51 @@


def read_var(state, var):
"""Access nested state variables using slash-separated path (e.g. 'agents/position')"""
return get_by_path(state, re.split("/", var))


class GeoPlot:
"""Cesium-based geospatial visualizer for agent simulation data.

Converts agent state trajectories into time-series GeoJSON format and generates
interactive 3D maps showing property evolution using color/size encoding.
"""
def __init__(self, config, options):
# Cesium Ion API access token
self.cesium_token = options["cesium_token"]
# Time between simulation steps (seconds)
self.step_time = options["step_time"]
# Path to agent coordinates in state (e.g. "agents/location")
self.entity_position = options["coordinates"]
# Path to visualized property in state (e.g. "agents/energy")
self.entity_property = options["feature"]
# Visualization encoding type: 'color' or 'size'
self.visualization_type = options["visualization_type"]
self.config = config
(
self.cesium_token,
self.step_time,
self.entity_position,
self.entity_property,
self.visualization_type,
) = (
options["cesium_token"],
options["step_time"],
options["coordinates"],
options["feature"],
options["visualization_type"],
)

def render(self, state_trajectory):
"""Process simulation states and generate visualization files:
- GeoJSON file with time-series agent data
- HTML file with Cesium visualization setup
"""
coords, values = [], []
name = self.config["simulation_metadata"]["name"]
geodata_path, geoplot_path = f"{name}.geojson", f"{name}.html"

# Extract coordinates and property values from each episode's final state
for i in range(0, len(state_trajectory) - 1):
final_state = state_trajectory[i][-1]
final_state = state_trajectory[i][-1] # Final state of episode

# Get agent coordinates as [[lat1, lon1], [lat2, lon2], ...]
coords = np.array(read_var(final_state, self.entity_position)).tolist()

# Extract and flatten property values for heatmap generation
values.append(
np.array(read_var(final_state, self.entity_property)).flatten().tolist()
)

# Generate simulation timestamps for Cesium timeline
start_time = pd.Timestamp.utcnow()
timestamps = [
start_time + pd.Timedelta(seconds=i * self.step_time)
Expand All @@ -256,15 +270,18 @@ def render(self, state_trajectory):
)
]

# Build GeoJSON structure for Cesium visualization
geojsons = []
for i, coord in enumerate(coords):
features = []
# Create time-encoded features for each agent's property values
for time, value_list in zip(timestamps, values):
features.append(
{
"type": "Feature",
"geometry": {
"type": "Point",
# Convert to GeoJSON [longitude, latitude] order
"coordinates": [coord[1], coord[0]],
},
"properties": {
Expand All @@ -275,19 +292,21 @@ def render(self, state_trajectory):
)
geojsons.append({"type": "FeatureCollection", "features": features})

# Write GeoJSON data file
with open(geodata_path, "w", encoding="utf-8") as f:
json.dump(geojsons, f, ensure_ascii=False, indent=2)

# Generate HTML visualization with template substitution
tmpl = Template(geoplot_template)
with open(geoplot_path, "w", encoding="utf-8") as f:
f.write(
tmpl.substitute(
{
"accessToken": self.cesium_token,
"startTime": timestamps[0].isoformat(),
"stopTime": timestamps[-1].isoformat(),
"data": json.dumps(geojsons),
"visualType": self.visualization_type,
"accessToken": self.cesium_token, # Cesium API token
"startTime": timestamps[0].isoformat(), # Simulation start
"stopTime": timestamps[-1].isoformat(), # Simulation end
"data": json.dumps(geojsons), # Embedded GeoJSON
"visualType": self.visualization_type, # Color/size encoding
}
)
)