Skip to content
Open
Show file tree
Hide file tree
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
12 changes: 11 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
VENV := /tmp/ts-build-test
DIST := dist

.PHONY: help build test test-package clean lint publish-test publish
.PHONY: help build test test-package build-docs clean lint publish-test publish

help: ## Show this help
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | \
Expand Down Expand Up @@ -45,6 +45,16 @@ test-package: build ## Build, install in clean venv, run smoke test
@echo ""
@echo "Package is ready. Run 'make publish-test' for TestPyPI or 'make publish' for PyPI."

build-docs: ## Regenerate all docs/*.html from build scripts
python docs/build_desert_farm.py
python docs/build_desert_farm_summary.py
python docs/build_explorer.py
@echo ""
@echo "Regenerated:"
@echo " docs/desert_farm_stommel.html"
@echo " docs/desert_farm_summary.html"
@echo " docs/explorer.html"

publish-test: build ## Upload to TestPyPI
pip install twine
twine check $(DIST)/*
Expand Down
74 changes: 34 additions & 40 deletions docs/build_explorer.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,14 @@
"""

import pandas as pd
import numpy as np
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, CustomJS, Select, TextInput, Button, Div, Span, Label, HoverTool
from bokeh.layouts import column, row
from bokeh.resources import CDN
from bokeh.embed import components

from timeSpace.constants import TIME_MARKERS, SPACE_MARKERS
from timeSpace.calculations import create_ellipse_data, classify_process_geometry
from timeSpace.etl import process_magnitude_column
from timeSpace.etl import transform_process_response_sheet, POSSIBLE_COL_LIST

# ── Configuration ──────────────────────────────────────────────────
# Match main figure axis ranges (from plotting.py's create_space_time_figure)
Expand Down Expand Up @@ -56,37 +54,33 @@


def load_reference_objects(csv_path):
"""Read reference objects CSV and generate render coordinates.

Classifies each object's geometry (ellipse/vline/hline/point) and only
generates ellipse polygon data for true ellipses. Degenerate axes are
flagged so the rendering layer can use lines or point markers.

Applies astropy units (seconds, m³) to match the package's ETL pipeline,
then calls create_ellipse_data from timeSpace.calculations.
"""Read reference objects CSV and run the package ETL pipeline.

Pre-ETL adapter: the reference-objects CSV uses a different schema
from a Google Form response sheet, so we adapt before delegating to
transform_process_response_sheet:
- Rename Name → FullName so create_name's ShortName fallback inside
the ETL doesn't overwrite the descriptive name we want for hover
tooltips and labels.
- Map Category → Color (uppercase to match POSSIBLE_COL_LIST).
- Set ShortName = FullName since reference objects don't have
separate short forms; create_name needs ShortName to exist.

transform_process_response_sheet handles unit conversion, geometry
classification, ellipse polygon generation, label_x/label_y, and
filters out rows where Time_min > Time_max or Space_min > Space_max.
"""
df = pd.read_csv(csv_path)

# Apply units — same function as etl.py pipeline
for col in ["Time_min", "Time_max", "Space_min", "Space_max"]:
df[col] = df.apply(process_magnitude_column, column=col, axis=1)

# Classify geometry before generating coords
df["geometry"] = df.apply(classify_process_geometry, axis=1)

# Only generate ellipse data for actual ellipses
ellipse_mask = df["geometry"] == "ellipse"
df.loc[ellipse_mask, ["x_coords", "y_coords"]] = df.loc[
ellipse_mask, ["Time_min", "Time_max", "Space_min", "Space_max"]
].apply(create_ellipse_data, axis=1, result_type="expand", n_points=EXPLORER_N_POINTS, space_on_x=False)

df["color"] = df.Category.map(CATEGORY_COLORS)

# Center position for labels (geometric mean in log space)
df["label_x"] = np.sqrt(df.Time_min.apply(lambda q: q.value) * df.Time_max.apply(lambda q: q.value))
df["label_y"] = np.sqrt(df.Space_min.apply(lambda q: q.value) * df.Space_max.apply(lambda q: q.value))

return df
df = df.rename(columns={"Name": "FullName"})
df["Color"] = df.Category.map(CATEGORY_COLORS)
df["ShortName"] = df.FullName

return transform_process_response_sheet(
df,
possible_col_list=POSSIBLE_COL_LIST + ["FullName", "Category"],
space_on_x=False,
n_points=EXPLORER_N_POINTS,
)


# ── Figure construction ───────────────────────────────────────────
Expand Down Expand Up @@ -289,10 +283,10 @@ def _patch_coords(row):
data=dict(
xs=[_patch_coords(row)[0] for _, row in df.iterrows()],
ys=[_patch_coords(row)[1] for _, row in df.iterrows()],
color=df.color.tolist(),
color=df.Color.tolist(),
alpha=[0.0] * len(df), # start hidden
line_alpha=[0.0] * len(df),
name=df.Name.tolist(),
name=df.FullName.tolist(),
category=df.Category.tolist(),
time_min=[row.Time_min.value for _, row in df.iterrows()],
time_max=[row.Time_max.value for _, row in df.iterrows()],
Expand Down Expand Up @@ -326,7 +320,7 @@ def _line_coords(row):
data=dict(
xs=[_line_coords(row)[0] for _, row in df.iterrows()],
ys=[_line_coords(row)[1] for _, row in df.iterrows()],
color=df.color.tolist(),
color=df.Color.tolist(),
alpha=[0.0] * len(df),
)
)
Expand All @@ -345,7 +339,7 @@ def _line_coords(row):
data=dict(
x=[row.Time_min.value if row.geometry == "point" else float("nan") for _, row in df.iterrows()],
y=[row.Space_min.value if row.geometry == "point" else float("nan") for _, row in df.iterrows()],
color=df.color.tolist(),
color=df.Color.tolist(),
alpha=[0.0] * len(df),
)
)
Expand Down Expand Up @@ -385,9 +379,9 @@ def _line_coords(row):
data=dict(
x=df.label_x.tolist(),
y=df.label_y.tolist(),
text=df.Name.tolist(),
text=df.FullName.tolist(),
alpha=[0.0] * len(df),
color=df.color.tolist(),
color=df.Color.tolist(),
)
)

Expand Down Expand Up @@ -480,7 +474,7 @@ def _line_coords(row):
categories = ["— Select category —"] + sorted(CATEGORY_COLORS.keys())
cat_select = Select(title="Filter by category:", value=categories[0], options=categories, width=220)

objects = ["— Select object —"] + sorted(df.Name.tolist())
objects = ["— Select object —"] + sorted(df.FullName.tolist())
obj_select = Select(title="Or pick an object:", value=objects[0], options=objects, width=280)

# Custom input fields
Expand Down Expand Up @@ -740,6 +734,6 @@ def _line_coords(row):
if __name__ == "__main__":
import sys

csv_path = sys.argv[1] if len(sys.argv) > 1 else "data/local_data/time_space_reference_objects.csv"
csv_path = sys.argv[1] if len(sys.argv) > 1 else "data/datasets/time_space_reference_objects.csv"
output_path = sys.argv[2] if len(sys.argv) > 2 else "docs/explorer.html"
build_explorer(csv_path, output_path)
6 changes: 3 additions & 3 deletions docs/desert_farm_stommel.html

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions docs/desert_farm_summary.html

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions docs/explorer.html

Large diffs are not rendered by default.

Loading