<SpatialMap> Container Shell
The <SpatialMap> component is the central container for the BloomSpark indoor mapping suite. It instantiates the underlying MapLibre GL JS map instance, registers the PMTiles protocol exactly once, manages the controlled or uncontrolled viewport camera state, and exposes context to hooks and child overlay components.
⚙️ Component Props API
typescript
import maplibregl from 'maplibre-gl';
export interface SpatialMapProps {
/** Array of building data structures serving this map view. */
buildings: Building[];
/** Controlled active building ID. Exploded floors and details are visible for this building. */
activeBuildingId?: string;
/** Callback fired when the active building switches (e.g. via zoom DynamicFocus threshold). */
onActiveBuildingChange?: (id: string) => void;
/** Uncontrolled initial viewport camera coordinates. */
initialViewState?: ViewState;
/** Controlled viewport camera coordinates. Pair with onViewStateChange. */
viewState?: ViewState;
/** Fired when coordinates or zoom values update during camera motions. */
onViewStateChange?: (vs: ViewState) => void;
/** Style mode selection. Extrudes 3D structures or locks to pitch-0 topdown plan. */
mode?: '2d' | '3d';
/** Fired when mode switches between 2D and 3D. */
onModeChange?: (m: '2d' | '3d') => void;
/** MapLibre style JSON specification or direct CDN string. Defaults to a bundled Protomaps light spec. */
mapStyle?: maplibregl.StyleSpecification | string;
/** Set to true to require a two-finger swipe to pan on mobile web views. */
cooperativeGestures?: boolean;
/** Enable or disable standard vector attribution credits overlay. Default is true. */
attributionControl?: boolean;
/** Maximum camera pitch allowed in degrees. Default is 70. */
maxPitch?: number;
/** Custom HTML className applied to the root wrapper. */
className?: string;
/** Inline React CSS variables applied to the root wrapper. */
style?: React.CSSProperties;
/** Child overlays and panels. */
children?: React.ReactNode;
}
export interface ViewState {
longitude: number;
latitude: number;
zoom: number;
pitch: number;
bearing: number;
}
export interface Building {
id: string;
name: Record<string, string>;
levels: Level[];
homography?: Homography;
}
export interface Level {
id: string;
ordinal: number;
name: Record<string, string>;
short_name: Record<string, string>;
}
export type Homography = readonly [
number, number, number,
number, number, number,
number, number, number
];🏢 Multi-Building Campus Architecture
The SDK supports single-building environments as well as massive campus footprints containing dozens of structures:
- Vector Footprints: Footprints of all loaded buildings are rendered on the basemap at low-level zoom scales.
- Active Focusing: Exploiting the
activeBuildingIdcontrols which building is selected and expanded. - Automatic Transition:
<DynamicFocus />handles automatic viewport-to-floor selection transitions when zoom ranges pass defined thresholds (zoom ≥18 locks on the building interior; zoom <16 fades back to outdoor basemaps).
🎨 MapLibre Layer Z-Ordering
Below is the authoritative layout hierarchy inside <SpatialMap> and <FloorLayers>. deck.gl layers share MapLibre's WebGL context via MapboxOverlay, preventing dual rendering delays:
| Layer Z-Index | Layer Identifier | Source Type | Description |
|---|---|---|---|
| 0 | basemap-* | PMTiles Vector | Static external Protomaps vectors |
| 100 | building-extrusion-3d | GeoJSON Footprint | 3D oblique structures |
| 200 | floorplan-image | WebGL homography raster | Scanned blueprint canvas overlays |
| 300 | floor-fill | GeoJSON Polygon | IMDF Unit fills |
| 310 | floor-outline | GeoJSON LineString | Unit boundaries |
| 320 | wall-3d | fill-extrusion | Extruded building partitions |
| 400 | door-*, opening-* | GeoJSON LineString | Exit paths / architectural openings |
| 500 | fixture-* | GeoJSON Polygon | Inside furniture / custom assets |
| 600 | amenity-icons | Symbol/Circle | Anchor point representations |
| 700 | data-layer-* | GeoJSON or deck.gl | Customer point/icon telemetry layers |
| 800 | heatmap-* | deck.gl Interleaved | Historical activity aggregation |
| 900 | wayfinding-route | GeoJSON LineString | Directed A* path paths |
| 950 | rtls-trail | deck.gl TripsLayer | Real-time moving particle vectors |
| 980 | bluedot | Symbol/Circle | Focused personal locator beacon |
| 990 | selection-highlight | GeoJSON LineString | Active click/select bounds |
🧼 Cleanup Protocols
<SpatialMap> handles system bindings transparently during unmounting:
- Removes
pmtiles://custom transport handlers usingmaplibregl.removeProtocol('pmtiles'). - Disconnects active Web Worker transport channels safely.
- Destroys local IndexedDB caching allocations for temporary viewState variables.
