Skip to content

View Handoff (3D <-> 2D)

Use this guide when your application has both:

  • a 3D viewer (for example Potree), and
  • a MapPrism2D map view

The goal is simple: users should not lose spatial context while switching modes.


Core idea

mapprism-2d exposes two methods for synchronization boundaries:

  • setView(...) for applying external camera state to 2D
  • getView() for exporting current 2D state back to your 3D system

This lets the host app own transition timing and UX while mapprism-2d stays focused on map behavior.


This pattern syncs only when a user switches panels.
It is easier to reason about and usually enough for production UX.

3D -> 2D handoff

ts
map.setView(
  {
    center: [-79.4346, 41.1922],
    bearing: 45,
    zoom: 17,
  },
  { animate: true }
);

2D -> 3D handoff

ts
const view = map.getView();
// view.center is WGS84 [lng, lat]
// Convert and apply to your 3D camera model as needed.

Pattern B: Continuous runtime sync (optional)

Providing sync.potree.viewer has two distinct effects — understanding both prevents surprises:

  1. Init-time (always active when viewer is set): initialView is ignored. The map starts with a loading overlay and waits for the first valid Potree update event before positioning. This ensures the 2D view opens framed on the same area as the 3D scene.

  2. Runtime (only when enabled: true): A continuous bidirectional sync loop runs. Both panels track each other as the user navigates.

If you only want Potree-first initialization without live coupling, set viewer but leave enabled at its default (false).

ts
const config = {
  map2d: {
    // ...
    sync: {
      enabled: true,
      master: "auto",      // "auto" | "map2d" | "potree"
      mapPriorityMs: 1200,
      potree: {
        viewer: window.viewer,
        throttleMs: 50,
      },
    },
  },
};

When to use it

  • live split-screen workflows
  • synchronized operator and analyst views
  • interactions where both viewers remain visible/active

When to avoid it

  • simple toggle-based interfaces
  • apps where explicit transition animation is enough
  • environments where camera ownership must stay deterministic

Coordinate reminder

getView() and setView() always use WGS84 coordinates ([lng, lat]).

If your 3D system stores EPSG:3857 or engine-native coordinates, convert at the handoff boundary in your host app.


Integration checklist

  1. Attach map ready handler.
  2. If using Potree sync: pass sync.potree.viewer and omit initialView — the library derives the opening position from the first Potree camera event.
  3. Implement panel transition events in host UI.
  4. On 3D -> 2D, call setView(...).
  5. On 2D -> 3D, read getView() and update 3D camera.
  6. Add viewchange subscriptions if you maintain global app state.

Full Potree sync reference

For the complete config reference, camera math, CRS handling, loop-guard mechanics, TypeScript types, and troubleshooting see the Potree Sync Reference.