Skip to content

Live Demo

This page explains the demo as a working integration blueprint.
If you are integrating Potree and mapprism-2d in one app, demo/index.html is the reference implementation.

Open Live Demo in New Tab


What this demo teaches

The demo shows how to run two renderers in one host layout:

  • Potree in panel-3d
  • MapPrism2D in panel-2d
  • A shared header that controls view mode and active panel
  • Runtime synchronization between 3D and 2D view state

In practical terms, it answers:

  • how to initialize both viewers from one bootstrap script
  • how to hide/show panels without breaking MapLibre sizing
  • how to keep context when users move between 3D and 2D
  • how to ship usable built-in 2D controls (layers, HUD, measurement) on day one

Runtime architecture at a glance

demo/index.html is intentionally monolithic so you can trace the full flow in one file:

  1. Load Potree and related global scripts
  2. Load MapPrism2D as an ES module
  3. Create the dual-panel DOM structure (panel-3d, panel-2d)
  4. Start Potree (window.viewer)
  5. Start MapPrism2D (window.map2d) with sync enabled against Potree
  6. Wire UI state transitions (split/alternating + panel switch)

Think of it as a host app skeleton. You can later split this into framework components, but the integration order should stay similar.


UI state model (the part most teams need)

The demo keeps UI logic simple and explicit:

  • viewMode: "alternate" or "split"
  • activePanel: "3d" or "2d" (only relevant in alternating mode)
  • PANELS metadata object: maps panel ids to labels and switch targets

Two small functions drive almost all behavior:

  • setViewMode(nextMode) applies mode classes and updates header/buttons
  • setActivePanel(next) toggles which panel is visible in alternating mode

Important integration detail: after any panel/mode change, it calls:

  • window.map2d.resize()

This is required because MapLibre must recalculate the canvas when its container visibility/size changes.


Potree bootstrap (3D side)

The demo creates Potree first and exposes it globally:

  • window.viewer = new Potree.Viewer(...)
  • sets FOV, point budget, and GUI options
  • calls viewer.fitToScreen(...) after the point cloud loads

A dataset selector at the top of the demo switches between two real-world datasets:

DatasetCRSNotes
Piney DamEPSG:3857 (Web Mercator)Default. Standard case — no srs needed.
Holtwood DamEPSG:6565 (Pennsylvania South, survey feet)Demonstrates non-EPSG:3857 support. Includes a tailrace DSM raster layer.

Switching datasets reinitializes both viewers with dataset-specific configs (including srs for Holtwood).

Why this matters: MapPrism2D sync receives the Potree viewer reference, so Potree must exist before the 2D map is initialized.


MapPrism2D bootstrap (2D side)

The demo then creates:

  • window.map2d = new MapPrism2D(document.getElementById("panel-2d"), config)

The config is close to real integration defaults:

  • initialView: center + zoom + bearing
  • basemap: "uai"
  • layerPanel: true
  • hud: true
  • tools.measurement.controls: true
  • layers: raster + PMTiles stack

This gives you a production-like starting point where users can immediately inspect layers and measurements without custom UI code.


3D <-> 2D sync wiring

The demo enables continuous sync using:

  • sync.enabled: true
  • sync.potree.viewer: window.viewer
  • sync.potree.throttleMs: 50

For the Holtwood Dam dataset, sync.potree.srs: "EPSG:6565" is also set. The library reads the proj4 definition from the loaded point cloud metadata and converts survey-foot state plane coordinates to WGS84 transparently — the map still opens on the correct location despite the scene not being in Web Mercator.

That setup keeps both views spatially coherent while users navigate either panel.
If your product does not need continuous coupling, start with transition-time handoff (getView() / setView()) and enable continuous sync later.

Working with non-EPSG:3857 datasets

The Holtwood Dam dataset in the demo is the fastest way to see srs in action:

  1. Select Holtwood Dam from the dataset dropdown.
  2. Both viewers initialize. The 2D map opens correctly over south-central Pennsylvania despite the Potree scene being in survey feet (EPSG:6565), not metres or Web Mercator.
  3. Navigate in either panel — sync stays accurate because radius/zoom conversion accounts for the survey-foot unit scale automatically.

To replicate this for your own non-EPSG:3857 dataset, add srs to your config:

ts
sync: {
  enabled: true,
  potree: {
    viewer: window.viewer,
    srs: 'EPSG:6565', // replace with your scene's EPSG code
  },
}

See the Potree Sync Reference — Non-EPSG:3857 scenes for the full projection resolution flow.


Measurement tools and debug instrumentation

The measurement UI is enabled through tools.measurement.controls: true.

The demo also adds a debug helper:

  • window.measureDebug

It wraps measurement method calls and logs drawings after each action. This is useful while integrating custom toolbars or validating keyboard finalize/cancel behavior (Enter / Escape).


How to adapt this demo into your app

Use demo/index.html as your migration checklist:

  1. Keep the same container contract (panel-3d, panel-2d) or equivalent sized regions.
  2. Preserve initialization order: Potree first, then MapPrism2D with Potree reference.
  3. Keep explicit view state (viewMode, activePanel) in your host state store.
  4. Call map2d.resize() whenever panel visibility or layout changes.
  5. Start with built-in controls; replace with custom controls only after behavior is stable.
  6. Replace demo layer URLs with your environment-specific sources.

Local run

bash
npm install
npm run dev:site

Then open /demo/index.html and keep this guide next to demo/index.html while reading the source.