Smplrspace Migration Guide
Migrating from smplr.js to @diginestai/core? This guide maps each Smplrspace imperative API call to its declarative React equivalent.
Why Migrate
| Concern | Smplrspace | @diginestai/core |
|---|---|---|
| Rendering model | Imperative class-based | Declarative React components |
| SaaS dependency | app.smplrspace.com required | Fully self-hosted |
| Tile hosting | Mapbox-only (cloud) | PMTiles on R2 / S3 / self-hosted |
| IMDF export | No | Yes — Apple Indoor Maps + Microsoft Places |
| Source license | Closed source | Commercial — customer-controlled |
| React integration | Partial (wrapper required) | First-class |
| Outdoor maps | Mapbox only | MapLibre + any PMTiles source |
Loading a Space
smplr.js (before)
const space = new smplr.Space({
spaceId: 'your-space-id',
clientToken: 'pub_token',
containerId: 'map-container',
});
space.startViewer({
preview: true,
onReady: () => console.log('ready'),
onError: (error) => console.error(error),
});@diginestai/core (after)
import { SpatialProvider, SpatialMap, IMDFSource, FloorLayers } from '@diginestai/core';
import 'maplibre-gl/dist/maplibre-gl.css';
export function MyMap() {
return (
<SpatialProvider>
<SpatialMap
buildings={[
{
id: 'building-1',
name: { en: 'My Building' },
levels: [
{ id: 'level-0', ordinal: 0, name: { en: 'Ground Floor' }, short_name: { en: 'G' } },
{ id: 'level-1', ordinal: 1, name: { en: 'Floor 1' }, short_name: { en: '1' } },
],
},
]}
activeBuildingId="building-1"
initialViewState={{ longitude: -122.4, latitude: 37.8, zoom: 17, pitch: 45, bearing: 0 }}
style={{ width: '100%', height: '100vh' }}
>
<IMDFSource url="/maps/my-building.imdf.zip">
<FloorLayers />
</IMDFSource>
</SpatialMap>
</SpatialProvider>
);
}No clientToken or SaaS account required. Your IMDF archive is hosted on your own infrastructure.
Picking Mode (Click-to-Select)
smplr.js (before)
space.enablePickingMode({
onReady: () => {},
onPick: ({ levelIndex, position, spaces }) => {
console.log('picked', spaces);
},
});
// Later:
space.disablePickingMode();@diginestai/core (after)
import { useMapStore } from '@diginestai/core';
function PickingMode() {
const selection = useMapStore((s) => s.selection);
const setSelection = useMapStore((s) => s.setSelection);
// Selection is reactive — no enable/disable lifecycle
return (
<div>
{selection.featureId
? `Selected: ${selection.featureId}`
: 'Click a room to select it'}
</div>
);
}Selection state lives in the Zustand store and updates on every map click automatically. Use selectionSlice.selectedFeatureIds for multi-select.
Data Layers
Point Data Layer
smplr.js (before)
space.addPointDataLayer({
id: 'desk-availability',
data: desks.map((d) => ({
position: { levelIndex: 0, x: d.x, z: d.z },
color: d.available ? '#3aa655' : '#ff3f34',
label: d.name,
})),
onPick: (desk) => console.log(desk),
});@diginestai/core (after)
import { PointDataLayer } from '@diginestai/core';
<SpatialMap ...>
<IMDFSource url="...">
<FloorLayers />
<PointDataLayer
id="desk-availability"
data={desks.map((d) => ({
position: [d.longitude, d.latitude],
color: d.available ? '#3aa655' : '#ff3f34',
label: d.name,
metadata: d,
}))}
onPick={(info) => console.log(info.object)}
/>
</IMDFSource>
</SpatialMap>Heatmap Data Layer
smplr.js (before)
space.addHeatmapDataLayer({
id: 'occupancy-heat',
data: sensors.map((s) => ({
position: { levelIndex: 0, x: s.x, z: s.z },
value: s.count,
})),
colorScale: 'viridis',
});@diginestai/core (after)
import { HeatmapDataLayer } from '@diginestai/core';
<HeatmapDataLayer
id="occupancy-heat"
data={sensors.map((s) => ({
position: [s.longitude, s.latitude],
value: s.count,
}))}
colorScale="YlOrRd"
/>The SDK ships 22 numeric color scales matching Smplrspace's full list: YlGn, YlGnBu, GnBu, BuGn, PuBuGn, PuBu, BuPu, RdPu, PuRd, OrRd, YlOrRd, YlOrBr, Greys, Reds, Greens, Blues, Oranges, Purples, RdYlBu, RdYlGn, Spectral, RdBu.
RAG Scale Defaults
The default RAG (Red/Amber/Green) colors match Smplrspace exactly:
| State | Color |
|---|---|
red | #ff3f34 |
amber | #c77a15 |
green | #3aa655 |
nodata | #6a6c6c |
import { PointDataLayer, ragColor } from '@diginestai/core';
// ragColor returns the appropriate hex based on value thresholds
<PointDataLayer
id="rag-layer"
data={zones.map((z) => ({
position: [z.lng, z.lat],
color: ragColor(z.occupancy, { low: 0.3, high: 0.7 }),
label: z.name,
}))}
/>Wayfinding
smplr.js (before)
space.startWayfinding({
from: { levelIndex: 0, x: 10, z: 20 },
to: { levelIndex: 2, x: 50, z: 80 },
onPathFound: (path) => console.log(path),
accessible: true,
});
space.stopWayfinding();@diginestai/core (after)
import { useWayfinding } from '@diginestai/core';
import { WayfindingPath } from '@diginestai/wayfinding';
function DirectionsPanel() {
const { getDirections, clearDirections, route } = useWayfinding();
const handleNavigate = async () => {
await getDirections({
from: { featureId: 'unit-reception' },
to: { featureId: 'unit-board-room-3' },
accessible: true,
});
};
return (
<>
<button onClick={handleNavigate}>Get Directions</button>
<button onClick={clearDirections}>Clear</button>
{/* WayfindingPath renders the route as a deck.gl PathLayer */}
{route && <WayfindingPath route={route} />}
</>
);
}The wayfinding engine uses a multi-floor A* graph built from IMDF opening features (doors, escalators, elevators). Accessible routing excludes stairs automatically.
Theming
smplr.js (before)
space.setTheme({
brand: {
primary: '#1a73e8',
},
floors: {
ground: { color: '#f5f5f5' },
upper: { color: '#e8eaf6' },
},
});@diginestai/core (after)
import { SpatialProvider } from '@diginestai/core';
// Option A — via SpatialProvider prop (static)
<SpatialProvider
theme={{
primary: '#1a73e8',
floorFill: '#f5f5f5',
wallFill: '#bdbdbd',
selectedFill: '#1a73e8',
}}
>
<SpatialMap ... />
</SpatialProvider>
// Option B — dynamic update via hook
import { useTheme } from '@diginestai/core';
function ThemeSwitcher() {
const { setToken } = useTheme();
return (
<button onClick={() => setToken('primary', '#e91e63')}>
Switch to Pink
</button>
);
}All theme values are CSS custom properties under --bs-*. Changes apply instantly without a page reload.
Floor Switching
smplr.js (before)
space.showUpToLevel(levelIndex);
space.goToFloor(levelIndex);@diginestai/core (after)
import { FloorSwitcher } from '@diginestai/core';
import { useFloorStore } from '@diginestai/core';
// Drop-in UI control
<FloorSwitcher position="top-right" />
// Programmatic control
function MyControl() {
const setLevel = useFloorStore((s) => s.setCurrentLevelOrdinal);
return <button onClick={() => setLevel(2)}>Go to Floor 2</button>;
}Keyboard shortcuts are built-in: ↑/↓ to step floors, 0–9 to jump, E to toggle exploded 3D view.
API Surface Quick Reference
| smplr.js API | @diginestai/core equivalent |
|---|---|
new smplr.Space({ spaceId, clientToken, containerId }) | <SpatialProvider> + <SpatialMap buildings={[...]} /> |
.startViewer({ onReady, onError }) | Mount component — React lifecycle handles readiness |
.enablePickingMode({ onPick }) | useMapStore((s) => s.selection) — reactive, always enabled |
.disablePickingMode() | N/A — set interactive={false} on <SpatialMap> to disable |
.addPointDataLayer({ id, data }) | <PointDataLayer id="..." data={[...]} /> |
.addIconDataLayer({ id, data }) | <IconDataLayer id="..." data={[...]} /> |
.addPolylineDataLayer({ id, data }) | <PolylineDataLayer id="..." data={[...]} /> |
.addPolygonDataLayer({ id, data }) | <PolygonDataLayer id="..." data={[...]} /> |
.addFurnitureDataLayer({ id, data }) | <FurnitureDataLayer id="..." data={[...]} /> |
.addHeatmapDataLayer({ id, data }) | <HeatmapDataLayer id="..." data={[...]} /> |
.removeDataLayer(id) | Unmount the layer component (React handles cleanup) |
.startWayfinding({ from, to, accessible }) | useWayfinding().getDirections({ from, to, accessible }) |
.stopWayfinding() | useWayfinding().clearDirections() |
.setTheme({ brand }) | <SpatialProvider theme={...}> or useTheme().setToken(key, value) |
.showUpToLevel(n) | useFloorStore((s) => s.setCurrentLevelOrdinal)(n) |
.goToFloor(n) | Same as above — camera follows automatically |
Next Steps
- Quick Start Tutorial — build a full indoor map in 10 minutes
- Data Layers & Scales — full data layer API reference
- Wayfinding Engine — A* graph configuration and accessible routing
- Custom Theming System — CSS token reference
