Skip to content

@bloomsparkagency/wayfinding - Wayfinding Engine

The wayfinding package calculates high-performance, multi-floor routing paths through indoor buildings. It constructs an internal connectivity graph directly from IMDF properties and executes search calculations inside a background Web Worker using Comlink RPC.


🧮 Multi-Floor Graph Calculations & A*

The wayfinding engine builds a network graph from two layers of connectivity: same-floor links and vertical transitions.

1. Same-Floor Connectivity

Same-floor routing paths are calculated directly by evaluating IMDF features:

  • Units & Openings: IMDF Unit polygons connect to other units through adjacent Opening features (pedestrian openings, service doors, or ramps).
  • Avenue Nodes: Center-points of openings become graph nodes; paths are traced through unit centers to form edges.

2. Vertical Connection Inference (Stairs & Elevators)

Since standard IMDF Opening features only define same-floor links, the engine automatically infers vertical transitions:

  • Proximity Matching: The compiler scans for features with categories matching stairs, elevator, escalator, or ramp.
  • Anchor Alignment: If the IMDF Anchor display_point coordinate of a vertical transition feature on Floor A aligns horizontally within 5 meters of an equivalent vertical transition feature on Floor B (adjacent levels), a directed vertical edge is created in the graph.
  • Weight Penalty Settings:
    • Elevators: Fixed travel-time penalty of 30 seconds per floor transition.
    • Stairs: Travel-time penalty calculated as: $$\text{Time} = \frac{\text{Floor Height (meters)}}{1.2\text{ m/s}}$$
    • Escalators: Travel-time penalty of 15 seconds per floor.

3. Accessible Mode (accessible: true)

When accessibility parameters are passed, the A* engine recalculates routing options:

  • Stair & Escalator Culling: Graph edges corresponding to stairs and escalator categories are assigned an edge weight of Infinity (complete exclusion).
  • Ramps & Elevators Only: Routing paths are restricted exclusively to ramp and elevator vertical linkages.

⚙️ Routing APIs

typescript
import { IMDFArchive } from '@bloomsparkagency/imdf';

/** Compile an IMDF archive into a wayfinding graph. Cached inside the Web Worker. */
export function buildGraph(archive: IMDFArchive): WayfindingGraph;

/** Finds the shortest path between two room/feature IDs. */
export function getDirections(
  graph: WayfindingGraph,
  originUnitId: string,
  destUnitId: string,
  opts?: RouteOptions
): RouteResult;

/** Identifies paths visiting multiple stops in sequential order. */
export function getDirectionsMultiDestination(
  graph: WayfindingGraph,
  originUnitId: string,
  destUnitIds: string[],
  opts?: RouteOptions
): RouteResult;

export interface RouteOptions {
  /** If true, filters out stairs and escalators. */
  accessible?: boolean;
  
  /** Apply Chaikin smoothing algorithms to round off raw zig-zag line edges. */
  smoothing?: boolean;
  
  /** Block specific unit/opening UUIDs (e.g. temporary maintenance closures). */
  excludedConnectionIds?: string[];
  
  /** Specific areas on the map that increase A* edge costs (e.g. crowded main lobbies). */
  avoidanceZones?: { coordinates: [number, number][]; costMultiplier: number }[];
}

export interface RouteResult {
  legs: RouteLeg[];
  totalDistance: number; // in meters
  totalDuration: number; // in seconds
}

export interface RouteLeg {
  floorId: string;
  floorOrdinal: number;
  /** GeoJSON LineString coordinates for the path on this level. */
  coordinates: [number, number][];
  /** Navigational maneuvers, e.g. "Take elevator to Floor 3" */
  instruction: string;
  distance: number;
}

🔍 Search Index Compilation (MiniSearch)

To power search inputs, the wayfinding package compiles a text-search index over spatial attributes:

  1. Index Compilation: Built once inside the Web Worker after the IMDF finishes loading.
  2. Fields Scanned: unit.name, unit.alt_name, amenity.category, occupant.name, and custom keywords.
  3. No Full Rebuilds: When drawing modifications are saved in the editor, incremental events are posted:
    typescript
    worker.miniSearch.add(newFeature);
    worker.miniSearch.update(modifiedFeature);
    worker.miniSearch.remove(deletedFeatureId);
  4. Zero Main-Thread Block: The search index query is executed asynchronously in the background. Selecting a search result fires Camera.focusOn(feature) and centers the map view instantly.

Released under commercial licensing.