Map: add side-by-side swipe comparison rendering driven through the plugin event bus
Motivation
Missions often need to visually compare two map states — two different layers, or the same layers at two different points in time — by dragging a divider across the map and revealing one state on each side. Today the map can only toggle whole layers on or off; there is no way to show different content on the left and right of a draggable divider.
There is also no clean way for a tool to ask the map to do this. The plugin architecture requires tools to stay independent and talk to the rest of the system only through the event bus — they must not reach into rendering internals. So this comparison/swipe behavior has to exist as a core map capability that a tool can drive entirely through events. This issue adds that primitive. A separate Comparison tool will consume it.
How it should work
- A consumer (a tool/plugin) can turn on a comparison swipe mode on the map: a draggable vertical divider appears over the map.
- The consumer specifies what renders on each side — a set of layers for the left, and a set for the right — and, for the date use case, an optional date pinned to each side.
- Dragging the divider reveals more or less of each side. Left of the divider shows the left configuration; right of it shows the right configuration.
- It stays consistent with the timeline: when the active date changes, each side updates its time-enabled layers according to the date assigned to that side (or the global date when a side isn't pinned to a specific date).
- The consumer can update the sides live (swap layers, change a side's date) and turn the mode off, returning the map to a normal single view.
- Everything above is reachable through the plugin event bus — a tool can drive it through events and requests alone, without touching map rendering directly. The capability emits events when the divider moves and when the mode toggles, so tools can react.
Done when
Out of scope
- The Comparison tool UI/panel itself — that's a separate issue; this is only the map-side capability it consumes.
- 3D / globe comparison — see the open question; can be a follow-up.
- On-demand / floating panels.
Open questions
- 2D only, or also 3D? Recommend delivering the 2D map capability first and treating 3D/globe swipe as a follow-up, since the clipping approach differs substantially between the two engines.
Draft implementation plan — written as of abef1d7 on 2026-06-15. Rough guide; re-verify against latest code.
Current behavior
- Layer visibility is all-or-nothing per layer; there is no per-side or clipped rendering anywhere in the codebase (no side-by-side / swipe / clip mechanism exists).
- The plugin event bus (
on / off / emit / request / provide, plus a forPlugin namespacing wrapper) is implemented and is the intended integration surface. Core modules own the map:, layers:, time: namespaces; plugins use plugin:<id>:*.
- Time-enabled layers update on date change driven by the timeline;
time:change-style events flow over the bus. The time machinery assumes a single globally-active date.
Where the change lands & rough plan
- Add a swipe/clip rendering mechanism to the 2D map engine: clip the relevant layer/tile panes to the left or right of a draggable divider (a leaflet "side-by-side" style pane clip is the conventional approach).
- Model two sides, each a set of layer ids plus an optional pinned date. When the active date changes, resolve each side's time-enabled layers against that side's date.
- Expose a small set of
provide/request handlers and events on the event bus under the core map: namespace — enable/disable comparison, set a side's config, get/observe the divider position — so tools drive it without direct coupling. Emit events on divider move and on mode change.
⚠️ Gotcha: rendering two date-versions of the same time-enabled layer at once (the compare-dates case) likely means instantiating a second set of layer renderers for the right side. The existing time machinery assumes one globally-active date, so resolving "what does this side show at its date" without disturbing global timeline state is the hard part — budget for it.
References
Understood to be snapshot-accurate only:
src/essence/mmgisAPI/mmgisAPI.js — event bus and the forPlugin namespacing wrapper.
src/essence/Basics/Map_/Map_.js — 2D/3D rendering engine.
src/essence/Basics/TimeControl_/ — timeline and time-change events.
docs/adr/20260209-plugin-communication-model.md — the plugin communication model (event bus, namespaces, why tools stay decoupled).
- Prototype (visual reference for the divider/swipe behavior): https://visualization-tool-prototype.netlify.app/
Map: add side-by-side swipe comparison rendering driven through the plugin event bus
Motivation
Missions often need to visually compare two map states — two different layers, or the same layers at two different points in time — by dragging a divider across the map and revealing one state on each side. Today the map can only toggle whole layers on or off; there is no way to show different content on the left and right of a draggable divider.
There is also no clean way for a tool to ask the map to do this. The plugin architecture requires tools to stay independent and talk to the rest of the system only through the event bus — they must not reach into rendering internals. So this comparison/swipe behavior has to exist as a core map capability that a tool can drive entirely through events. This issue adds that primitive. A separate Comparison tool will consume it.
How it should work
Done when
Out of scope
Open questions
Draft implementation plan — written as of abef1d7 on 2026-06-15. Rough guide; re-verify against latest code.
Current behavior
on/off/emit/request/provide, plus aforPluginnamespacing wrapper) is implemented and is the intended integration surface. Core modules own themap:,layers:,time:namespaces; plugins useplugin:<id>:*.time:change-style events flow over the bus. The time machinery assumes a single globally-active date.Where the change lands & rough plan
provide/requesthandlers and events on the event bus under the coremap:namespace — enable/disable comparison, set a side's config, get/observe the divider position — so tools drive it without direct coupling. Emit events on divider move and on mode change.References
Understood to be snapshot-accurate only:
src/essence/mmgisAPI/mmgisAPI.js— event bus and theforPluginnamespacing wrapper.src/essence/Basics/Map_/Map_.js— 2D/3D rendering engine.src/essence/Basics/TimeControl_/— timeline and time-change events.docs/adr/20260209-plugin-communication-model.md— the plugin communication model (event bus, namespaces, why tools stay decoupled).