diff --git a/geoplot.py b/geoplot.py
index 6f264d1..a049a74 100644
--- a/geoplot.py
+++ b/geoplot.py
@@ -31,194 +31,228 @@
```
"""
-import re
-import json
+# Standard library imports for regex parsing and JSON serialization
+import re # regular expressions for splitting variable paths
+import json # encode Python objects as JSON
-import pandas as pd
-import numpy as np
+# Third-party libraries for data manipulation
+import pandas as pd # timestamp generation and time series handling
+import numpy as np # numeric arrays and conversions
-from string import Template
+# Utilities for templating and nested state access
+from string import Template # substitute placeholders in HTML template
+# access nested state via a path
from agent_torch.core.helpers import get_by_path
+# HTML template for Cesium-based visualization with placeholders:
+# $accessToken, $data, $startTime, $stopTime, $visualType
geoplot_template = """
-
-
-
- Cesium Time-Series Heatmap Visualization
-
-
-
-
-
-
-
-
+
+
+
+ Cesium Time-Series Heatmap Visualization
+
+
+
+
+
+
+
+
"""
def read_var(state, var):
+ """
+ Retrieve a nested variable from a state dict given a slash-separated path.
+ """
return get_by_path(state, re.split("/", var))
class GeoPlot:
+ """Convert simulation state trajectories into Cesium visualizations."""
+
def __init__(self, config, options):
+ # Store simulation config and visualization settings
self.config = config
(
self.cesium_token,
@@ -227,27 +261,37 @@ def __init__(self, config, options):
self.entity_property,
self.visualization_type,
) = (
- options["cesium_token"],
- options["step_time"],
- options["coordinates"],
- options["feature"],
- options["visualization_type"],
+ options["cesium_token"], # Ion access token
+ options["step_time"], # seconds between steps
+ options["coordinates"], # path to position in state dict
+ options["feature"], # path to property values in state dict
+ options["visualization_type"], # 'color' or 'size'
)
def render(self, state_trajectory):
+ """Generate GeoJSON and HTML files from a list of state dicts."""
coords, values = [], []
- name = self.config["simulation_metadata"]["name"]
+ name = self.config["simulation_metadata"]["name"] # base filename
geodata_path, geoplot_path = f"{name}.geojson", f"{name}.html"
+ # Extract coordinates and feature values at each step
for i in range(0, len(state_trajectory) - 1):
final_state = state_trajectory[i][-1]
- coords = np.array(read_var(final_state, self.entity_position)).tolist()
+ # Read the nested coordinate list and flatten to Python list
+ coords = np.array(
+ read_var(
+ final_state,
+ self.entity_position)).tolist()
+ # Read feature array, flatten, and store
values.append(
- np.array(read_var(final_state, self.entity_property)).flatten().tolist()
+ np.array(read_var(final_state, self.entity_property))
+ .flatten()
+ .tolist()
)
- start_time = pd.Timestamp.utcnow()
+ # Generate timestamps for each step in the simulation
+ start_time = pd.Timestamp.utcnow() # use UTC now as timeline start
timestamps = [
start_time + pd.Timedelta(seconds=i * self.step_time)
for i in range(
@@ -256,15 +300,18 @@ def render(self, state_trajectory):
)
]
- geojsons = []
+ geojsons = [] # will hold FeatureCollections for each entity
+ # Build GeoJSON features per coordinate index
for i, coord in enumerate(coords):
features = []
for time, value_list in zip(timestamps, values):
+ # Create a GeoJSON Feature with geometry and time/value props
features.append(
{
"type": "Feature",
"geometry": {
"type": "Point",
+ # Cesium expects [lon, lat]
"coordinates": [coord[1], coord[0]],
},
"properties": {
@@ -273,11 +320,15 @@ def render(self, state_trajectory):
},
}
)
- geojsons.append({"type": "FeatureCollection", "features": features})
+ # Wrap features into a FeatureCollection
+ geojsons.append(
+ {"type": "FeatureCollection", "features": features})
+ # Write GeoJSON data to file for later loading in HTML
with open(geodata_path, "w", encoding="utf-8") as f:
json.dump(geojsons, f, ensure_ascii=False, indent=2)
+ # Fill HTML template placeholders and write out the final viewer page
tmpl = Template(geoplot_template)
with open(geoplot_path, "w", encoding="utf-8") as f:
f.write(