Skip to content

gistory

Story

Source code in maeson/gistory.py
class Story:
    def __init__(self, scenes):
        """
        A sequence of scenes forming a narrative.
        """
        self.scenes = scenes
        self.index = 0

    def _current_scene(self):
        return self.scenes[self.index]

    def _next_scene(self):
        if self.index < len(self.scenes) - 1:
            self.index += 1
        return self._current_scene()

    def _previous_scene(self):
        if self.index > 0:
            self.index -= 1
        return self._current_scene()

__init__(self, scenes) special

A sequence of scenes forming a narrative.

Source code in maeson/gistory.py
def __init__(self, scenes):
    """
    A sequence of scenes forming a narrative.
    """
    self.scenes = scenes
    self.index = 0

StoryController

Source code in maeson/gistory.py
class StoryController:
    def __init__(self, story, map_obj: Map):
        """
        Connects a Story object to a map and widget-based UI.
        """
        self.story = story
        self.map = map_obj
        self.current_layers = []

        self.next_button = widgets.Button(description="Next")
        self.back_button = widgets.Button(description="Back")
        self.next_button.on_click(self._next_scene)
        self.back_button.on_click(self._previous_scene)

        self.controls = widgets.HBox([self.back_button, self.next_button])
        self.interface = widgets.VBox([self.map, self.controls])

        self._update_scene()

    def _update_scene(self):
        scene = self.story._current_scene()
        # 1) Reset view
        self.map.center = scene.center
        self.map.zoom = scene.zoom

        # 2) Clear out any previous overlays
        self._clear_overlays()
        self.current_layers.clear()

        # 3) Re‑add each layer using your Map methods
        for ld in scene.layers:
            t = ld["type"]
            name = ld.get("name")

            try:
                if t == "geojson":
                    if "data" in ld:
                        layer = GeoJSON(data=ld["data"], name=name)
                        self.map.add_layer(layer)
                    else:
                        layer = self.map.add_geojson(ld["path"], name=name)

                elif t == "tile":
                    layer = self.map.add_tile(url=ld["url"], name=name)

                elif t == "image":
                    layer = self.map.add_image(
                        url=ld["path"],
                        bounds=tuple(tuple(c) for c in ld["bounds"]),
                        name=name,
                    )

                elif t == "video":
                    layer = self.map.add_video(
                        url=ld["path"],
                        bounds=tuple(tuple(c) for c in ld["bounds"]),
                        name=name,
                    )

                elif t == "raster":
                    layer = self.map.add_raster(
                        ld["path"], name=name, zoom_to_layer=False
                    )

                elif t == "wms":
                    layer = self.map.add_wms_layer(url=ld["url"], name=name)

                elif t == "earthengine":
                    # your Map.add_earthengine takes ee_object + vis_params
                    layer = self.map.add_earthengine(
                        ee_object=ld["ee_id"],
                        vis_params=ld.get("vis_params", {}),
                        name=name,
                    )

                else:
                    print(f"Unsupported layer type: {t}")
                    continue

                self.current_layers.append(layer)

            except Exception as e:
                print(f"❌ Failed to add {t} layer “{name}”: {e}")

        # 4) Finally, run any custom code
        if scene.custom_code.strip():
            try:
                exec(scene.custom_code, {}, {"map": self.map})
            except Exception as e:
                print(f"⚠️ Error in scene code: {e}")

    def _clear_overlays(self):
        # 1) Remove map overlays
        for lyr in list(self.map.layers)[1:]:
            self.map.remove_layer(lyr)

    def _next_scene(self, _=None):
        self.story._next_scene()
        self._update_scene()

    def _previous_scene(self, _=None):
        self.story._previous_scene()
        self._update_scene()

    def display(self):
        from IPython.display import display

        display(self.interface)

__init__(self, story, map_obj) special

Connects a Story object to a map and widget-based UI.

Source code in maeson/gistory.py
def __init__(self, story, map_obj: Map):
    """
    Connects a Story object to a map and widget-based UI.
    """
    self.story = story
    self.map = map_obj
    self.current_layers = []

    self.next_button = widgets.Button(description="Next")
    self.back_button = widgets.Button(description="Back")
    self.next_button.on_click(self._next_scene)
    self.back_button.on_click(self._previous_scene)

    self.controls = widgets.HBox([self.back_button, self.next_button])
    self.interface = widgets.VBox([self.map, self.controls])

    self._update_scene()