Skip to content

Comparing addon for the geoportal #767

Description

@helllth

The compare feature landed with #622, but only as a playground: CarmaMapCompare in libraries/mapping/core/src/components/compare/ and the route /compare in ng-topicmap-playground. There it compares background-layer presets, and the component owns every map, because the playground has no map of its own.

The geoportal is the opposite case: one map, its content driven by redux, carrying selection, info box, hash and the carma.mapping2D API. The playground component cannot be dropped into it.

What the geoportal needs instead is a mode over the layers already on the map: the user has several layers in the layer bar, opens a comparing control, and assigns each layer a role ("this one left, that one right"). The map is the data source of the comparison, not a panel content config. The purpose is informal, judging visible similarity between layers and getting a feel for their rendering performance.

Entry point

A row at the end of the layer bar that opens a panel, the way measurement mode works. The pattern to copy is the one introduced with #761: the addon library says when the row appears and what it contains, the app supplies the dispatches in a thin bridge hook, and the panel is contributed through ADDON_INTERACTION_COMPONENTS. No addon reaches into the geoportal store.

Modes

Kind Panels What it does
comparing The control: owns mode and role state, contributes the layer row and its panel
compareSwipe 2-4 Clip-path split, horizontal or vertical, draggable divider; four panels is the 2x2 grid
compareSpyglass 2 Clip-path circle, draggable, wheel-resizable
compareArena 2-4 Full viewports side by side, real layout rather than clipping
compareBlend 2 Two full-size maps stacked, a slider cross-fades from one to the other

Spyglass and blend have no meaning above two panels: a single slider cannot cross-fade three maps, and a lens shows one thing under it. The control disables them above two and falls back to swipe, the way the playground already handles spyglass.

How it works

useLibreLayers() already flattens the redux layers and the background into one LibreLayer[], and the app map is already driven purely by that prop. Every entry carries carmaLayerId. A panel's content is therefore that same array, filtered by role. Nothing needs to mutate redux, and the background is simply the first block of the array, which is why it can be assigned to panels like any other layer.

The app's own map stays mounted but hidden, and acts as the camera master. That is what keeps the rest of the app working untouched: the zoom buttons, the home button, the gazetteer and carma.mapping2D.flyTo all move the app map, and the app map syncs the panels. The hash keeps being written by the map that has always written it, and leaving the mode reveals a map already at the compared view. Dragging a panel moves the app map back the same way.

Panels are built on LibreMap rather than CarmaMap, since CarmaMap brings its own HashStateProvider and ControlLayout and nesting several of those inside the geoportal risks the panels fighting the app over the hash.

Camera sync is the off/jump/on dance already in CarmaMapCompare: detach every move listener, jumpTo all targets, reattach. Subscription has to happen when a map arrives rather than in a useEffect, or child-before-parent effect ordering under StrictMode leaves listeners on destroyed maps.

Open point

The addon lives in a library, useLibreLayers() is a geoportal hook, and AddonHost takes no props by design. The LibreLayer[] needs a sanctioned route into the addon, most likely a new method on the carma.mapping2D facade implemented app-side where registerMapping already happens.

Steps

  1. The stage plus compareSwipe, with roles taken implicitly from the stack (the last two layers left and right, the background in both). No layer row, no channels, no control UI. This puts two synced panels on screen and shows whether the hidden-master approach behaves.
  2. The layer row, the control panel and the two addon-state channels (compareModes, compareState). The implicit roles become what the panel starts from.
  3. compareBlend, then compareSpyglass, then compareArena.

Built against the dev-only addons route, where the highlight addon is already exercised. Nothing goes into DEFAULT_ADDONS.

Later

  • Per-panel layer order and per-panel opacity in the control table, once assigning roles is in use.
  • A per-panel timing readout: milliseconds from moveend to maplibre's idle. Note onProgressUpdate is not usable for this, it counts geojson layer loading only.
  • Feeding the hidden app map an empty layer set, if the unseen render pass turns out to cost anything.

Housekeeping from #622

libraries/mapping/core/src/hooks/useMapLibreSync.ts came along with the playground and is imported by nothing; it should not be ported and can go. CarmaMapCompare also ships with SYNC_LOG = true, so it logs on every camera event.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions