Skip to content

@bloomsparkagency/editor - Spatial Drawing Editor ​

The editor package is a fully featured, in-browser GIS editor that allows facilities managers and developers to draw, modify, copy, and georeference indoor assets directly. It extends Terra Draw and implements a highly memory-efficient, transactional undo/redo system.


πŸ–ŒοΈ Custom Drawing Modes ​

<SpatialEditor /> wraps the Terra Draw MapLibre adapter and extends it with three custom architectural drawing modes alongside standard shapes:

typescript
export type EditorMode =
  | 'select'     // Drag, rotate, and scale features. Draggable handles enabled.
  | 'polygon'    // Draw rooms/zones. Enforces ValidateNotSelfIntersecting.
  | 'rectangle'  // Draw rectangular units.
  | 'circle'     // Draw circular zones.
  | 'freehand'   // Draw arbitrary paths.
  | 'linestring' // Draw path vectors.
  | 'point'      // Drop point markers.
  | 'wall'       // Custom LineString mode: auto-generates Opening lines at intersections.
  | 'door'       // Custom Point mode: snaps to the nearest wall LineString.
  | 'fixture'    // Custom Polygon mode: draws and auto-categorizes furniture.
  | 'image-snap' // Georeferencing control points selection tool.

πŸ—ΊοΈ WebGL Homography Warping & Georeferencing ​

Photographed or scanned floor plan blueprints frequently suffer from perspective distortions. Simple 2D affine scaling (6 Degrees of Freedom) is insufficient. BloomSpark uses a full 3D Homography Matrix (8 effective Degrees of Freedom):

1. Control-Points Alignment Wizard ​

The user clicks $\ge 4$ control points on the raw image (pixel $u,v$ coordinates) and maps them to the corresponding $\ge 4$ points on the georeferenced MapLibre map (latitude/longitude coordinates).

2. DLT Least-Squares Solver ​

The editor passes these control point pairs to a Direct Linear Transformation (DLT) solver running inside a Web Worker. It solves for a $3 \times 3$ Homography matrix $H$:

$$H = \begin{bmatrix} h_0 & h_1 & h_2 \ h_3 & h_4 & h_5 \ h_6 & h_7 & 1 \end{bmatrix}$$

3. High-Performance WebGL Custom Shader ​

To avoid massive CPU-bound image recalculations during map pans or zooms:

  • The raw blueprint is loaded as a WebGL texture.
  • The editor injects a MapLibre CustomLayerInterface that binds the WebGL context.
  • WebGL Fragment Shader: Takes the inverse homography matrix $H^{-1}$ and transforms map-space coordinates ($x,y$) directly to texture UV coordinates ($u,v$) in real time.
  • Z-Fighting Mitigation: floorplan.elevationInCm introduces a customizable z-axis height offset (default 2 cm) to prevent z-fighting overlays between building floors and basemaps in 3D views. floorplan.alpha controls opacity (0–1).

πŸ“„ Lazy-Loaded PDF Rasterization ​

Facilities blueprints frequently arrive as multi-megabyte vector PDFs.

To prevent this from bloating your application's initial bundle, pdfjs-dist is lazy-loaded dynamically via an async import ONLY when the first PDF file is dropped onto the canvas:

typescript
// Lazy-loads ~1.5 MB gzipped dependency at runtime only when needed
const { getDocument } = await import('pdfjs-dist');

PDFs are parsed and rasterized locally in the browser at high-fidelity 150 DPI for coordinate snapping.


πŸ”„ Transactional Patch Undo/Redo (zundo) ​

Standard undo/redo stacks store full state snapshots, consuming hundreds of megabytes of memory on large floor plans. BloomSpark uses patch-based transaction histories:

  • Diff-Patching: Using Zustand middleware powered by zundo, the editor captures only JSON Patch arrays (forward and backward changes) using immer.
  • Sub-Megabyte Footprint: A massive copy operation across multiple floors containing 200+ rooms takes up less than 1 MB of transaction history (vs. 50 MB for raw snapshots).
  • Transaction Cap: Default queue size is 50 steps.
  • Group Collapsing: Multi-select operations or bulk actions are automatically grouped and collapsed into a single undo transaction step.

⌨️ Custom Keyboard Shortcuts ​

ShortcutMode/ActionDescription
VSelect ModeToggle selection handles, drag, and scale shapes.
RRectangle ModeDraw rectangular shapes.
PPolygon ModeDraw custom multi-point room shapes.
WWall ModeDraw partition lines with opening snap hooks.
DDoor ModeSnaps doors to the nearest wall.
FFixture ModeDraws furniture polygon boundaries.
IImage-Snap ModeDrop georeferencing wizard points.
Delete / BackspaceDelete SelectionRemoves selected features.
Cmd/Ctrl + CCopy AssetsCopy selected shapes to clipboard.
Cmd/Ctrl + VPaste AssetsPaste clipboard shapes at mouse pointer.
Cmd/Ctrl + Shift + CCopy Across FloorsOpens the cross-floor translation modal.
Cmd/Ctrl + ZUndoStep backward in the patch stack.
Cmd/Ctrl + Shift + ZRedoStep forward in the patch stack.
EscExit ModeClear current drawing shape and exit to Select mode.
↑ / ↓Floor SwitchMove active floor rendering up or down.
EExplode PivotExplode floors out in 3D view.

πŸ“‹ Cross-Floor & Cross-Building Copy-Paste ​

typescript
interface CopyOptions {
  /** carry level details. geometry-only copies coordinates; geometry+metadata includes attributes. */
  carry: 'geometry' | 'geometry+metadata' | 'geometry+metadata+sensorBindings';
  /** conflict resolutions */
  conflict: 'merge' | 'replace' | 'skip' | 'offset';
  /** offset distance applied when duplicating to avoid exact geometry stacking overlaps. Default is 0.5m. */
  offsetMeters?: number;
}
  • Cross-Building Re-Projection: When copying features between different buildings, the editor takes coordinates from Building A, converts them to local meters, translates them using Building B's homography matrix, and projects them accurately to the new WGS-84 coordinates.
  • Visual Pasting Diff: Pasted features render with a dashed blue outline (#2393d4) at 60% opacity until the user confirms the action, letting them adjust coordinates visually before committing the transaction.

Released under commercial licensing.